Skip to content

feat(ibis): add header to disable the fallback and refine fallback log#1174

Merged
douenergy merged 7 commits intoCanner:mainfrom
goldmedal:fix/legacy-mysql-fallback
May 2, 2025
Merged

feat(ibis): add header to disable the fallback and refine fallback log#1174
douenergy merged 7 commits intoCanner:mainfrom
goldmedal:fix/legacy-mysql-fallback

Conversation

@goldmedal
Copy link
Copy Markdown
Contributor

@goldmedal goldmedal commented Apr 29, 2025

Description

The v2 Wren core doesn't support some data sources, such as legacy MySQL (before 8). We don't need to try to fallback on this case. If we try to fallback, the error message thrown by the v2 Wren core is confusing.

New Header

x-wren-fallback_disable: true

Other Changes

  • Refine the migration message.
    • Now, the migration message is only printed when the v3 query fails and the v2 query succeeds.
  • Change some ordering of the arguments.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added support for a request header that allows users to disable automatic fallback to a previous connector version in API endpoints.
    • Enhanced tracing and logging for fallback scenarios, improving transparency and traceability.
  • Bug Fixes

    • Improved filtering of request headers to ensure only relevant headers are included in API processing.
  • Tests

    • Added tests to verify correct behavior when fallback is explicitly disabled via the new request header.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 29, 2025

Walkthrough

This update introduces a controlled fallback mechanism between v3 and v2 connector endpoints in the Ibis server. A new header, X_WREN_FALLBACK_DISABLE, allows clients to disable automatic fallback to v2 endpoints when v3 encounters exceptions. The fallback context is now propagated using OpenTelemetry headers, and detailed logging is added for fallback events. Endpoint signatures are updated to explicitly manage fallback behavior, including an optional is_fallback flag in v2 endpoints and explicit headers dependency injection in v3 endpoints. Comprehensive tests are added to verify that disabling fallback via the header results in expected error responses.

Changes

File(s) Change Summary
ibis-server/app/dependencies.py Added constant X_WREN_FALLBACK_DISABLE. Modified get_wren_headers to filter headers using the new _filter_headers function, which allows only specific tracing and custom headers.
ibis-server/app/routers/v2/connector.py Updated endpoint signatures to remove the headers parameter and add an optional is_fallback boolean. Added logic to call get_fallback_message when is_fallback is true for logging fallback events.
ibis-server/app/routers/v3/connector.py Standardized fallback control for all endpoints. Added logic to check for the X_WREN_FALLBACK_DISABLE header, conditionally disable fallback, and propagate tracing context using append_fallback_context. Endpoint signatures now explicitly require headers as a dependency. Logging updated for fallback events.
ibis-server/app/util.py Added append_fallback_context for OpenTelemetry propagation, get_fallback_message for logging, and the MIGRATION_MESSAGE constant. Added safe_strtobool utility function.
ibis-server/tests/routers/v3/connector/postgres/test_fallback_v2.py Extended tests to include cases with X_WREN_FALLBACK_DISABLE header set to "true", asserting that fallback is correctly disabled and 422 errors are returned.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant V3_Endpoint
    participant Util
    participant V2_Endpoint

    Client->>V3_Endpoint: Send request (with/without X_WREN_FALLBACK_DISABLE)
    V3_Endpoint->>V3_Endpoint: Process request
    alt Exception occurs
        V3_Endpoint->>V3_Endpoint: Check X_WREN_FALLBACK_DISABLE header
        alt Fallback disabled
            V3_Endpoint-->>Client: Raise original exception (error response)
        else Fallback allowed
            V3_Endpoint->>Util: append_fallback_context(headers, span)
            V3_Endpoint->>V2_Endpoint: Call v2 endpoint with headers, is_fallback=True
            V2_Endpoint->>Util: get_fallback_message(...)
            V2_Endpoint-->>V3_Endpoint: Return fallback result
            V3_Endpoint-->>Client: Return fallback result
        end
    else No exception
        V3_Endpoint-->>Client: Return result
    end
Loading

Possibly related PRs

Poem

In the world of headers, a new rule appears,
"X-WREN-FALLBACK_DISABLE" now steers.
If v3 should stumble, v2 may arise—
Unless you say "no" with a header so wise!
Tracing and logging now travel along,
With rabbits and code, we all hop along.
🐇✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51ce2c0 and 707fea1.

📒 Files selected for processing (1)
  • ibis-server/app/util.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ibis-server/app/util.py
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: ci
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added ibis python Pull requests that update Python code labels Apr 29, 2025
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
ibis-server/app/routers/v3/connector.py (1)

321-326: Same boolean-parsing issue repeated — refactor to a helper.

See earlier note on lines 137-143; this copy-paste carries the same exception-clobbering risk.

🧹 Nitpick comments (3)
ibis-server/app/dependencies.py (1)

26-35: Consider simplifying the header filtering logic.

The current implementation has multiple if-elif branches, which could be simplified for better readability and maintainability.

-def _filter_headers(header_string: str) -> bool:
-    if header_string.startswith("x-wren-"):
-        return True
-    elif header_string == "traceparent":
-        return True
-    elif header_string == "tracestate":
-        return True
-    elif header_string == "sentry-trace":
-        return True
-    return False
+def _filter_headers(header_string: str) -> bool:
+    allowed_exact_headers = {"traceparent", "tracestate", "sentry-trace"}
+    return header_string.startswith("x-wren-") or header_string in allowed_exact_headers
ibis-server/app/routers/v3/connector.py (2)

145-146: Minor logging nit — newline isn’t needed with Loguru’s lazy formatting.

logger.warning("… {}\n", str(e)) adds an extra blank line to every warning.
Drop \n unless you really want the gap:

-logger.warning("Failed to execute v3 query, try to fallback to v2: {}\n", str(e))
+logger.warning("Failed to execute v3 query, try to fallback to v2: {}", str(e))

163-170: Opportunity: centralise OpenTelemetry context injection.

append_fallback_context(headers, span) is repeatedly called right before delegating to v2.
Consider moving the call into the helper that builds the v2 request (or into append_fallback_context itself) so each handler doesn’t need to remember this boilerplate.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47ca29e and 7a44fc0.

📒 Files selected for processing (5)
  • ibis-server/app/dependencies.py (1 hunks)
  • ibis-server/app/routers/v2/connector.py (9 hunks)
  • ibis-server/app/routers/v3/connector.py (7 hunks)
  • ibis-server/app/util.py (2 hunks)
  • ibis-server/tests/routers/v3/connector/postgres/test_fallback_v2.py (11 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
ibis-server/tests/routers/v3/connector/postgres/test_fallback_v2.py (5)
ibis-server/tests/conftest.py (1)
  • client (18-23)
ibis-server/tests/routers/v3/connector/postgres/test_query.py (1)
  • manifest_str (96-97)
ibis-server/tests/routers/v3/connector/postgres/test_model_substitute.py (1)
  • manifest_str (46-47)
ibis-server/tests/routers/v2/connector/test_postgres.py (1)
  • manifest_str (124-125)
ibis-server/tests/routers/v3/connector/postgres/conftest.py (1)
  • connection_url (48-50)
ibis-server/app/dependencies.py (2)
ibis-server/app/model/data_source.py (2)
  • DataSource (42-67)
  • get_dto_type (63-67)
ibis-server/app/model/__init__.py (1)
  • QueryDTO (17-20)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: ci
🔇 Additional comments (16)
ibis-server/app/dependencies.py (2)

7-7: Good addition of the fallback disable header constant.

This header constant provides a clean way to control the fallback behavior and follows good naming conventions for HTTP headers.


15-23: Good refactoring to filter header propagation.

The implementation correctly filters headers to only pass those that are relevant for tracing and Wren-specific operations, which is more secure and reduces noise in the header propagation.

ibis-server/tests/routers/v3/connector/postgres/test_fallback_v2.py (3)

6-6: Good import of the fallback disable header constant.

Using the constant directly from the dependencies module ensures consistency across the codebase.


48-59: Well-implemented test for disabling fallback.

The test correctly verifies that setting the X_WREN_FALLBACK_DISABLE header to "true" prevents fallback to v2 and returns a 422 error.


96-108: Thorough test coverage for the fallback disable feature.

The tests comprehensively cover all endpoint scenarios where fallback behavior might occur, ensuring that the fallback disable header works consistently across different operations.

Also applies to: 137-149, 162-174, 209-221, 252-264, 278-290, 304-315, 328-339, 352-363

ibis-server/app/util.py (4)

10-20: Good addition of OpenTelemetry imports.

The imports correctly include all necessary components for trace context propagation, which is essential for maintaining tracing across fallback scenarios.


26-27: Clear and informative migration message.

The message provides helpful context for users when fallback occurs, guiding them on how to assist with the migration process.


117-128: Well-implemented fallback context propagation.

The function correctly handles trace context propagation for fallback scenarios, converting spans to non-recording spans and injecting both baggage and trace context into headers.


136-146: Good implementation of fallback message logging.

The function formats the message clearly, properly serializes relevant data, and logs at an appropriate level. The replacement of newlines in SQL ensures readable log formatting.

ibis-server/app/routers/v2/connector.py (7)

25-25: Good update to imports.

The import statement properly includes the new utility functions needed for fallback handling.


46-47: Proper update to function parameters.

The function signature now correctly accepts the filtered headers and an optional fallback flag, allowing for controlled fallback behavior.

Also applies to: 62-62


152-156: Good implementation of fallback message logging.

The code correctly logs fallback events when the is_fallback flag is set, providing valuable information for debugging and monitoring.


166-172: Consistent implementation of fallback handling.

The validate endpoint follows the same pattern as the query endpoint, ensuring consistent behavior and logging across different operations.

Also applies to: 186-191


245-249: Good implementation of fallback handling in dry_plan.

The function correctly logs fallback events while maintaining the original functionality.

Also applies to: 257-260


269-274: Consistent implementation in dry_plan_for_data_source.

The endpoint follows the same pattern as other endpoints, ensuring consistent logging and behavior.

Also applies to: 284-288


301-302: Complete coverage of all endpoints.

The model_substitute endpoint includes the same fallback handling logic, ensuring that all endpoints in the v2 connector router support the new fallback mechanism.

Also applies to: 317-321

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
ibis-server/app/dependencies.py (1)

26-37: Consider optimizing the header filtering logic

The current implementation uses multiple if-elif statements which is perfectly functional but could be made more concise.

-def _filter_headers(header_string: str) -> bool:
-    if header_string.startswith("x-wren-"):
-        return True
-    elif header_string.startswith("x-user-"):
-        return True
-    elif header_string == "traceparent":
-        return True
-    elif header_string == "tracestate":
-        return True
-    elif header_string == "sentry-trace":
-        return True
-    return False
+def _filter_headers(header_string: str) -> bool:
+    return (
+        header_string.startswith("x-wren-") or
+        header_string.startswith("x-user-") or
+        header_string in {"traceparent", "tracestate", "sentry-trace"}
+    )
ibis-server/app/util.py (2)

26-27: Fix grammar in migration message

There's a minor grammatical error in the migration message.

-MIGRATION_MESSAGE = "Wren engine is migrating to Rust version now. \
-    Wren AI team are appreciate if you can provide the error messages and related logs for us."
+MIGRATION_MESSAGE = "Wren engine is migrating to Rust version now. \
+    Wren AI team would appreciate if you can provide the error messages and related logs for us."

148-149: Consider making safe_strtobool more explicit with negative cases

The implementation handles positive cases well but is implicit about negative cases.

def safe_strtobool(val: str) -> bool:
-    return val.lower() in {"1", "true", "yes", "y"}
+    val = val.lower() if val else ""
+    return val in {"1", "true", "yes", "y"}

This makes the handling of None values explicit and still maintains the same behavior.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7a44fc0 and 51ce2c0.

📒 Files selected for processing (3)
  • ibis-server/app/dependencies.py (1 hunks)
  • ibis-server/app/routers/v3/connector.py (6 hunks)
  • ibis-server/app/util.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • ibis-server/app/routers/v3/connector.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
ibis-server/app/dependencies.py (2)
ibis-server/app/model/data_source.py (2)
  • DataSource (42-67)
  • get_dto_type (63-67)
ibis-server/app/model/__init__.py (1)
  • QueryDTO (17-20)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: ci
🔇 Additional comments (5)
ibis-server/app/dependencies.py (2)

7-7: LGTM: Added header constant to control fallback behavior

The new header constant provides a clean way to allow clients to disable the v2 fallback mechanism.


15-24: LGTM: Header filtering implementation

The implementation correctly filters headers based on prefix and specific tracing headers, allowing only necessary headers to be propagated during fallback operations.

ibis-server/app/util.py (3)

10-18: LGTM: Added necessary OpenTelemetry imports

The new imports are focused on the specific OpenTelemetry components needed for context propagation, which is good practice.


117-128: LGTM: Well-implemented OpenTelemetry context propagation

The append_fallback_context function correctly handles trace context propagation:

  1. Handles the case when headers is None
  2. Creates a non-recording span to avoid duplicate spans
  3. Properly injects both W3C baggage and trace context into headers

This follows OpenTelemetry best practices for context propagation across service boundaries.


136-146: LGTM: Improved fallback message logging

The implementation provides clear, structured logging for fallback events with relevant context (datasource, model hash, SQL). The SQL formatting (replacing newlines with spaces) improves log readability.

@goldmedal goldmedal requested a review from douenergy May 2, 2025 08:31
Copy link
Copy Markdown
Contributor

@douenergy douenergy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @goldmedal, just a small nit. The rest looks good.

sql = sql.replace("\n", " ")

message = orjson.dumps(
{"datasource": datasource, "mdl_hash": mdl_hash, "sql": sql}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we encode the mdl in base64, so maybe we should just name it mdl or mdl_base64.

@douenergy douenergy merged commit 2380ae8 into Canner:main May 2, 2025
5 checks passed
@douenergy
Copy link
Copy Markdown
Contributor

Thanks @goldmedal

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ibis python Pull requests that update Python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants