Skip to content

feat(query-orchestrator): add trace_id SQL comment to data source queries - #11376

Open
igorlukanin wants to merge 8 commits into
masterfrom
igor/cub-3093-query-tracing-sql-comment
Open

feat(query-orchestrator): add trace_id SQL comment to data source queries#11376
igorlukanin wants to merge 8 commits into
masterfrom
igor/cub-3093-query-tracing-sql-comment

Conversation

@igorlukanin

Copy link
Copy Markdown
Member

Summary

  • Adds an opt-in trace_id SQL comment to the queries Cube sends to a data source, so a warehouse's query history can be joined back to Cube's own query history. Off by default, enabled with CUBEJS_SQL_INCLUDE_TRACE_ID=true. Works for every driver, since it operates on the SQL text rather than on a driver-specific query id.
  • The comment is appended, not prepended: Snowflake strips leading comments before recording the SQL text in QUERY_HISTORY, so a leading comment is silently lost on exactly the engine this is most useful for. Verified live against Snowflake — the same query prepended loses the comment and appended retains it. No supported engine requires a leading comment, so appending is both simpler and more correct than per-dialect placement.
  • Tagging happens at execution time, after the cache key is computed. The SQL text is the cache key, so tagging any earlier would give every request a unique key and defeat both result caching and in-flight query coalescing.
  • Only the query the user actually asked for is tagged (primaryQuery). Refresh keys, pre-aggregation helper queries and background renewals travel the same path but answer to no API request, so they stay untagged.
  • The request id can be client-supplied (the REST API reads x-request-id/traceparent verbatim), so it is sanitized with a character allowlist. Both comment delimiters are stripped: engines disagree on whether block comments nest, so a stray closing delimiter would leak live SQL on some and a stray opening one would swallow the statement on others.
  • The emitted trace_id deliberately drops the -span-N suffix so it matches the trace_id column of the Query History export, and the span is emitted as a separate field to tell apart the several data source queries one request fans out into.

Test plan

  • Unit tests for comment construction, sanitization and injection, including a hostile request id that attempts to escape the comment (17 tests)
  • Unit tests for the orchestrator wiring: flag off/on, cache-key stability, tuple-form queries, and that refresh-key/background-renewal queries stay untagged (10 tests)
  • Unit test for the new environment variable
  • Full pre-existing cubejs-query-orchestrator unit suite green (78 tests), covering the queued and streaming paths
  • Verified live end-to-end on Snowflake (QUERY_HISTORY), Postgres (server log) and DuckDB (driver boundary): the main query is tagged, streaming queries are tagged with the correct SQL text, refresh-key queries are untagged, and results are unchanged
  • CI must pass

@igorlukanin
igorlukanin requested review from a team and keydunov as code owners July 27, 2026 13:53
@github-actions github-actions Bot added javascript Pull requests that update Javascript code data source driver labels Jul 27, 2026
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 1m 48s —— View job


Review complete — 0 blockers, 3 low-severity notes
  • Read changed files
  • Analyze code quality, security, tests
  • Post review

Overall

Clean, well-scoped design. The three subtle points — comment must come after the SQL for Snowflake, tagging must happen after cache key derivation, and comment delimiters must be sanitized because request ids are client-controllable — are all correctly handled and explained in comments and tests. Coverage is thorough (streaming path, tuple form, hostile input, cache-key stability, refresh keys stay untagged). Nothing blocking.

Low-severity notes

1. Dead code in sanitizeTraceId.replace(/^\++/, '') is a no-op

packages/cubejs-base-driver/src/trace-comment.ts:30

The allowlist regex /[^A-Za-z0-9._:-]/g on the previous line already strips + (it's not in the allowed set). By the time .replace(/^\++/, '') runs, no + can remain. The +MAPJOIN(a) → MAPJOINa test passes for the same reason. Either drop the second replace or, if you want to explicitly preserve + for some engines and only strip it in leading position, add + to the allowlist first. As written it's a comment-only guarantee.

2. splits the cache key between two requests test doesn't cover what its name implies

packages/cubejs-query-orchestrator/test/unit/QueryTraceComment.test.ts:182-189

QueryCache.queryCacheKey only reads query, values, preAggregations, and invalidate — never requestId. So this test would pass identically without any of the trace-comment changes; it doesn't demonstrate that tagging is deferred past key computation. The load-bearing property (that the actual SQL sent to the driver is tagged but the cache lookup key isn't) is really covered by the first test in that block plus the "is appended when enabled" test. Consider either deleting this test or replacing it with one that verifies the same request id produces different cache lookups for different queries, or that a cache hit occurs across two requests with different ids under the flag.

3. Doc: "span identifies the attempt within that request" is misleading

docs-mintlify/admin/connect-to-data/query-tracing.mdx:32

The code comment (splitRequestId) says the span exists to tell apart the several data source queries one request fans out into — not retries/attempts. Suggest: "span distinguishes the individual data source queries when one request fans out into several." Same wording nit applies to the Snowflake page addition if it uses "attempt."

Nits (optional)

  • MAX_TRACE_ID_LENGTH = 128 is a magic constant that isn't referenced in the docs; if someone with a 200-char traceparent wonders why their tag looks chopped, a one-line mention in query-tracing.mdx would help.
  • The scheduler- prefix check in traceQuery is a defensive backstop given renewCycle already guards background renewals — worth a one-word comment tying the two together, since a future refactor could plausibly delete either thinking the other is sufficient.
    · Branch

@mintlify

mintlify Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
cubed3 🟢 Ready View Preview Jul 27, 2026, 1:54 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

Comment thread docs-mintlify/admin/connect-to-data/query-tracing.mdx Outdated
Comment thread docs-mintlify/admin/connect-to-data/query-tracing.mdx Outdated
Comment thread docs-mintlify/admin/connect-to-data/query-tracing.mdx Outdated
Comment thread docs-mintlify/admin/connect-to-data/query-tracing.mdx Outdated
Comment thread packages/cubejs-base-driver/src/trace-comment.ts Outdated
Comment thread packages/cubejs-base-driver/src/trace-comment.ts Outdated
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 1m 9s —— View job


Re-review after 81926a1 — 0 blockers, 1 low-severity note
  • Diff prior head → HEAD
  • Verify prior review points addressed
  • Skim new logic and tests for regressions

Verdict

The follow-up commit cleanly addresses all three prior notes:

  1. Dead .replace(/^\++/, '') removed+ is now excluded from the allowlist itself, with a comment noting the Hive/Spark reason. packages/cubejs-base-driver/src/trace-comment.ts:18-21
  2. Cache-key test rewritten to actually demonstrate the deferral — it now runs an end-to-end fetch and asserts both that the driver received the tagged SQL and that queryCacheKey for the same request produces no trace_id. packages/cubejs-query-orchestrator/test/unit/QueryTraceComment.test.ts:181-206
  3. span removed from the payload entirely (per Igor's inline reviews) — comment format is now just /* trace_id: … */; docs, tests, and toTraceId all updated consistently. packages/cubejs-base-driver/src/trace-comment.ts:42-55

Bonus cleanups that were on the "nits" list also landed: the 128-char truncation is now called out in query-tracing.mdx, and the renewCycle / scheduler- guards have a one-line comment tying them together (QueryCache.ts:432-436).

Low-severity note

Truncation runs before -span-N stripping, so a >128-char request id can be cut mid-suffix

packages/cubejs-base-driver/src/trace-comment.ts:30-49

sanitizeTraceId slices to 128 chars first; toTraceId then searches for -span- in the truncated string. If the raw request id is longer than 128 chars and the boundary falls inside the suffix (e.g. …-spa gets kept, n-1 gets cut), the emitted trace_id still contains the partial marker and no longer matches the Query History export — the exact scenario the split was added to prevent. Cube-generated ids are well under this, so this only bites client-supplied x-request-id/traceparent values at pathological lengths. Either strip the span before truncating, or size the limit against the emitted trace id, not the raw input.

Nits (optional, non-blocking)

  • The removed cross-link between query-tracing.mdx and the Snowflake query-tag page is one-directional now — the Snowflake page still points here, but a reader landing on query-tracing no longer discovers that connection-level tags exist. Fine given Igor's explicit call, just flagging.

Nothing else stands out. Ready to ship pending CI.
· Branch ·

@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 53s —— View job


Re-review after efe2c4e — 0 blockers, 0 outstanding notes

Prior truncation/span note addressed cleanly
  • Diff 81926a1..HEAD
  • Verify prior note is resolved
  • Skim for regressions in the reordering

The fix reorders sanitize → strip-span → cap, exactly matching the prior note. sanitizeTraceId no longer caps (it's only used from buildTraceComment, so no external caller is affected); the 128-char slice now applies to the emitted trace_id after -span-N has been stripped, so a raw id that ends in …-span-1 at position >128 can no longer leave a partial -spa marker in the output. The new caps after stripping the span, never mid-suffix test in packages/cubejs-base-driver/test/unit/trace-comment.test.ts:76-81 locks the ordering in with a targeted case, and the previous caps the length test was correctly moved from sanitizeTraceId to buildTraceComment where the cap now lives.

Nothing else to flag. Ready to ship pending CI.

· [Branch](https://github.com/cube-js/cube/tree/igor/cub-3093-query-tracing-sql-comment)

@igorlukanin
igorlukanin requested a review from paveltiunov July 27, 2026 14:12
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.59459% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.49%. Comparing base (cad6ce8) to head (2957448).
⚠️ Report is 7 commits behind head on master.

Files with missing lines Patch % Lines
...-query-orchestrator/src/orchestrator/QueryCache.ts 84.61% 2 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (cad6ce8) and HEAD (2957448). Click for more details.

HEAD has 1 upload less than BASE
Flag BASE (cad6ce8) HEAD (2957448)
cubesql 1 0
Additional details and impacted files
@@             Coverage Diff             @@
##           master   #11376       +/-   ##
===========================================
- Coverage   83.92%   59.49%   -24.44%     
===========================================
  Files         257      224       -33     
  Lines       80755    17922    -62833     
  Branches        0     3643     +3643     
===========================================
- Hits        67777    10663    -57114     
+ Misses      12978     6738     -6240     
- Partials        0      521      +521     
Flag Coverage Δ
cube-backend 59.49% <94.59%> (?)
cubesql ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 45s —— View job


Re-review after e967fa8 — 0 blockers, 0 outstanding notes

Tuple-form coverage added; nothing else changed
  • Diff efe2c4e..HEAD
  • Verify the new test matches the code path
  • Skim for regressions

The only change since the previous review is the addition of a tuple-form queries describe block in packages/cubejs-query-orchestrator/test/unit/QueryTraceComment.test.ts:160-179. It exercises traceQuery directly with query: [SQL, params], matching the Array.isArray(req.query) branch at packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts:443-446:

  • Flag on → SQL gets the comment appended, params passed through untouched (toEqual).
  • Flag off → the exact same reference comes back (toBe), so no allocation on the hot path.

The comment above the block correctly notes that queryWithRetryAndRelease types query as any, so while today's internal callers unwrap the tuple upstream, the branch is a public surface worth locking down. No regressions in surrounding tests. Ready to ship pending CI.

· [Branch](https://github.com/cube-js/cube/tree/igor/cub-3093-query-tracing-sql-comment)

@igorlukanin
igorlukanin requested review from paveltiunov and removed request for paveltiunov July 27, 2026 17:38
@igorlukanin
igorlukanin force-pushed the igor/cub-3093-query-tracing-sql-comment branch from e967fa8 to d74b9bc Compare July 27, 2026 22:41
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 2m 45s —— View job


Re-review — 0 blockers, 1 medium, 1 low (both new, both about which queries get tagged)

Details
  • Diff against origin/master, identify what changed since the last review
  • Trace every path that reaches a data source, checking where primaryQuery is set
  • Review sanitization, cache-key deferral, tests, docs
  • Check own stale threads (none unresolved), post review

What changed

No code changes since e967fa8 — this synchronize is a rebase onto current master. The previously reviewed content (span removal, cap-after-strip ordering, tuple-form test, rewritten cache-key test) is intact. So this pass went deeper on the one thing the earlier reviews took at face value: the claim that exactly the user's query gets tagged. Two spots don't hold.

Medium — cache=stale-while-revalidate requests are never tagged

packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts:346-358

cachedQueryResult has three exits for the user's own query, and only two of them set primaryQuery:

path sets primaryQuery
persistent / no refresh keys → queryWithRetryAndRelease (lines 266, 281)
must-revalidate and default !backgroundRenewrenewQuery (line 884)
background fetch → cacheQueryResult (line 346)

The third branch is taken whenever queryCacheOptions.backgroundRenew is on or the request carries cacheMode: 'stale-while-revalidate' — and that is client-settable via the REST cache= param (gateway.ts:337). For those requests traceQuery bails on !req.primaryQuery, the SQL reaches the warehouse untagged, and the join the feature exists for silently produces nothing. The tests only cover the default path, so nothing catches it. One-line fix plus a test; if the omission is deliberate, it needs a comment and a line in query-tracing.mdx, because "silently off for a whole cacheMode" is surprising for an opt-in feature. Inline comment posted.

Low — the lambda source-table download is tagged too

QueryCache.ts:884 hardcodes primaryQuery: true inside the public renewQuery, and PreAggregationPartitionRangeLoader.downloadLambdaTable calls it (PreAggregationPartitionRangeLoader.ts:389) with the user's requestId and no renewCycle. So one pre-aggregation helper query does get tagged, contradicting "only the query the user actually asked for". Harmless in itself — it is a data source query issued for that request, and csvQuery tags it consistently — but the invariant as written is wrong, so either thread primaryQuery through or relax the wording. Inline comment posted.

What holds up well

  • Sanitization. Allowlist rather than blocklist, both delimiters stripped, + excluded with the Hive/Spark hint reason spelled out. The hostile-request-id test asserts delimiter counts, not just absence of the payload, which is the right assertion.
  • Cache-key deferral. traceQuery is called at the client.query / client.stream / downloadQueryResults boundary, strictly after QueryCache.queryCacheKey; the queue dedup key is likewise derived upstream. The rewritten test now actually demonstrates this end-to-end.
  • Append, not prepend, with the trailing-semicolon handling, and the Snowflake reason recorded in the source rather than only in the PR description.
  • Cap after span-strip ordering is locked in by a targeted test.
  • Streaming path has its own coverage via the createQueue traceFn, with the default (req) => req.query keeping other createQueue callers untouched.
  • renewCycle and the scheduler- prefix as deliberately redundant guards, with the comment explaining why both stay.

Both findings are about coverage of the feature, not about correctness of what it emits — nothing here can corrupt SQL or break caching.

· [Branch](https://github.com/cube-js/cube/tree/igor/cub-3093-query-tracing-sql-comment)

Comment thread packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts
Comment thread packages/cubejs-query-orchestrator/src/orchestrator/QueryCache.ts
@claude

claude Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 2m 35s —— View job


Re-review after f700157 — 0 blockers, 1 low (docs), 1 nit

Both prior findings fixed correctly; the background-fetch fix is precisely scoped and can't mis-tag detached refreshes.

  • Diff latest head against prior review point
  • Verify the primaryQuery background-fetch fix and its test
  • Check doc wording change for the lambda/multi-query claim
  • Check own stale threads (all already resolved), post review

Prior findings

Medium — background fetch untagged: fixed. primaryQuery: true added at QueryCache.ts:358. I traced whether this could over-tag, since cacheQueryResult has three detached fetchNew() call sites that inherit primaryQuery:

detached path in cacheQueryResult reachable from the background-fetch branch?
isSameRequest && !renewCycle continue-wait refresh (line 1085) yes, but only for the same request id — correct to tag
renewalKey && isExpired → "Renewing existing key" (line 1096) no — that branch passes no renewalKey
waitForRenewreturn fetchNew() n/a, request waits on it

So no background refresh belonging to a stale request id can pick up a tag through the new option. The fix is exactly as narrow as it needs to be. The test's setup (cacheKeyQueries: { renewalThreshold, queries: [] } + cacheMode: 'stale-while-revalidate') does reach the background-fetch branch, so it genuinely fails without the change.

Low — lambda source-table read contradicts the "only the user's query" claim: fixed by correcting the claim. query-tracing.mdx:71-78 and the traceQuery comment now both say queries issued to serve a request, name the lambda read as the concrete second case, and tell readers to match on trace_id rather than expect one row. That's the better half to change — the lambda read really is issued for that request.

Low — docs omit pre-aggregations from "no tagged query to find"

docs-mintlify/admin/connect-to-data/query-tracing.mdx:80

A request served from a pre-aggregation goes through getExternalQueue, whose handler calls client.query(q.query, …) with no traceQuery (QueryCache.ts:642). Correct behaviour — Cube Store isn't the data source — but it's the case a production deployment hits most often, and the troubleshooting sentence lists only cache hits and coalescing. Inline suggestion posted.

Nit

The new is appended on the background fetch path test asserts only that the tagged SQL is present (toContain). The renew cycle startRenewCycle kicks off concurrently on that same branch, so an added expect(...).not.toContain on an untagged copy of that SQL would also lock in that the renew-cycle query stays untagged — the second half of what the original note asked for. Non-blocking; the existing is absent for background refreshes test covers the renew path separately.

Verified unchanged and still sound

  • Sanitization: allowlist, both delimiters stripped, + excluded with the Hive/Spark reason recorded.
  • Cap-after-span-strip ordering in buildTraceComment (trace-comment.ts:47-49), locked in by a targeted test.
  • Cache-key deferral: traceQuery only runs at the client.query / client.stream / downloadQueryResults boundary, strictly after QueryCache.queryCacheKey and after the queue dedup key is derived.
  • Append-not-prepend with trailing-semicolon handling; renewCycle and the scheduler- prefix as deliberately redundant guards.

Not verified: I couldn't execute the test suites in this environment (sandbox denied jest), so the assessment above is from reading the code paths, not from a green run. CI covers it.

· [Branch](https://github.com/cube-js/cube/tree/igor/cub-3093-query-tracing-sql-comment)

Comment thread docs-mintlify/admin/connect-to-data/query-tracing.mdx Outdated
@igorlukanin
igorlukanin force-pushed the igor/cub-3093-query-tracing-sql-comment branch from f700157 to 12d2a0a Compare July 29, 2026 19:15
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 2m 10s —— View job


Re-review after 12d2a0a — 0 blockers, 1 low (new)

Docs fix is correct and complete. One new low finding from a fresh pass on the -span- stripping: it duplicates extractRequestUUID, the one function that must not drift.

  • Diff against prior review point
  • Verify the pre-aggregation docs note is accurate against the code
  • Fresh pass over the trace-comment module and every driver-boundary call in QueryCache
  • Check own stale threads (all resolved), post review

Prior finding — fixed

Low — docs omitted pre-aggregations from "no tagged query to find": fixed. query-tracing.mdx:80-83 now reads "one served from the cache or from a pre-aggregation never reaches your data source". Matches the code: a pre-aggregation-served request goes through getExternalQueue, whose handler calls client.query(q.query, …) with no traceQuery (QueryCache.ts:642) — untagged by design, since Cube Store isn't the data source.

That was the only change in this commit. Everything else is unchanged from 189df30.

Low — toTraceId duplicates extractRequestUUID

packages/cubejs-base-driver/src/trace-comment.ts:40-44

Byte-for-byte the same lastIndexOf('-span-') + substring as packages/cubejs-query-orchestrator/src/orchestrator/utils.ts:38-41, which is what QueryCache.extractRequestUUID exposes and what the Query History export's trace_id comes from. Those two are exactly the pair that must never diverge — the join is worthless if they do — and neither file references the other. cubejs-query-orchestrator already depends on base-driver, so the direction to deduplicate in is available: export toTraceId here, re-export it as extractRequestUUID there. Inline comment posted.

(The loose lastIndexOf rather than an anchored -span-\d+$ is right, incidentally — subscription-server.ts:163 builds -span-${uuidv4()}, so the suffix isn't always numeric.)

Re-verified this pass, still sound

  • Comment placement under driver-side SQL wrapping. Some drivers wrap the query text (CREATE TABLE … AS (query), subquery pagination). A trailing block comment survives all of those — …/* trace_id: x */) LIMIT 1 is valid everywhere. A trailing -- line comment would not have been; the block form is load-bearing, not incidental.
  • Cache-key deferral. traceQuery is only invoked inside executeFn / the stream traceFn / csvQuery, all strictly after QueryCache.queryCacheKey and after the queue dedup key is derived. Confirmed by walking every call site, not just the tested ones.
  • primaryQuery / renewCycle are pre-existing fields on CacheQueryResultOptions (lines 44-45) already threaded through the logging calls — the PR reuses the established concept rather than inventing a parallel one, and loadRefreshKey sets neither, so refresh keys stay untagged for free.
  • Sanitization: allowlist not blocklist, both delimiters stripped, + excluded with the Hive/Spark hint reason recorded; hostile-input test asserts delimiter counts.
  • Cap-after-span-strip ordering (trace-comment.ts:47-49), locked in by a targeted test.
  • Append-not-prepend with trailing-semicolon handling; renewCycle and the scheduler- prefix as deliberately redundant guards, each with its stated reason.

Not verified: the test suites weren't executed in this environment, so the above is from reading code paths. CI covers it.

· Branch ·

Comment thread packages/cubejs-base-driver/src/trace-comment.ts Outdated
@claude

claude Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 1m 36s —— View job


Re-review after fb69fd7 — 0 blockers, 0 outstanding notes, 1 nit

The deduplication is done in the right direction and no caller changes behaviour. Pure refactor; nothing else moved.

  • Diff prior review point → HEAD
  • Verify the toTraceId / extractRequestUUID deduplication
  • Check callers, export surface and dependency direction
  • Check own stale threads (all resolved), post review

Prior finding — fixed

Low — toTraceId duplicated extractRequestUUID: fixed, and in the direction suggested. toTraceId is now exported from packages/cubejs-base-driver/src/trace-comment.ts:45, and packages/cubejs-query-orchestrator/src/orchestrator/utils.ts:42 is a one-line re-export under the old name. There is now exactly one lastIndexOf('-span-') in the repo, and both doc comments name the other side, so the invariant is discoverable from either file.

I checked the mechanics rather than taking the re-export at face value:

check result
Dependency direction @cubejs-backend/base-driver is already a runtime dependencies entry of cubejs-query-orchestrator (package.json:32), and base-driver does not depend back — no cycle. utils.ts previously imported only types from it, so this adds a real runtime edge, but a legal one.
Export reachability base-driver/src/index.ts:4 already has export * from './trace-comment', so toTraceId is reachable from the package root.
Name collisions in the widened surface trace-comment's four exports don't collide with ./utils (cancelCombinator, TableName, SaveCancelFn) or the other barrel members.
Callers The four extractRequestUUID call sites — QueryCache.ts:410, QueryCache.ts:1078 (same-request continue-wait match) and QueryQueue.ts:522,527 (cancel-by-request matching) — all import the aliased name and see byte-identical logic. The public static QueryCache.extractRequestUUID keeps its signature.

Nit

toTraceId is now a shared contract but has no test that exercises it under its own name — the strip is only asserted indirectly, through buildTraceComment (trace-comment.test.ts:63-79). That indirect coverage is real and includes the load-bearing cap-after-strip case, so this is genuinely a nit; a two-line direct test would just make the invariant fail loudly at the right level if someone edits it. The non-numeric suffix case (subscription-server.ts:163 builds -span-${uuidv4()}) is the one worth pinning, since it's what forces lastIndexOf over an anchored -span-\d+$.

Verified unchanged and still sound

  • Sanitization: allowlist not blocklist, both comment delimiters stripped, + excluded with the Hive/Spark hint reason recorded; the hostile-input test asserts delimiter counts rather than mere absence of the payload.
  • Cap after span-strip ordering (trace-comment.ts:49-51), locked in by a targeted test.
  • Cache-key deferral: traceQuery only runs at the client.query / client.stream / downloadQueryResults boundary, strictly after QueryCache.queryCacheKey and after the queue dedup key is derived.
  • Append-not-prepend with trailing-semicolon handling; the block-comment form survives driver-side SQL wrapping (CREATE TABLE … AS (query), subquery pagination) where a trailing -- would not.
  • Tagged-query coverage: primaryQuery: true on the background-fetch branch, renewCycle and the scheduler- prefix as deliberately redundant guards, and docs that correctly list cache hits, pre-aggregations and coalescing as the cases with no tagged query to find.

Not verified: dependencies aren't installed in this environment, so I couldn't typecheck or run the suites — the export/collision checks above are from reading the barrels and package.json, not from a green build. CI covers it.

· [Branch](https://github.com/cube-js/cube/tree/igor/cub-3093-query-tracing-sql-comment)

@igorlukanin

Copy link
Copy Markdown
Member Author

Fair on the nit: added a direct toTraceId block in 2957448, pinning the non-numeric span suffix specifically.

@claude

claude Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Claude finished @igorlukanin's task in 1m 11s —— View job


Re-review after 2957448 — 0 blockers, 0 outstanding notes

The prior nit is closed. Tests-only commit; the three cases chosen are exactly the ones that pin the contract, including the non-numeric suffix that forces lastIndexOf.

  • Diff prior review point → HEAD
  • Verify the new direct toTraceId tests against the implementation
  • Confirm the re-export is behaviour-preserving for existing callers
  • Check own stale threads (all resolved), post review

Prior nit — closed

toTraceId had no test under its own name: fixed. packages/cubejs-base-driver/test/unit/trace-comment.test.ts:46-67 adds a dedicated describe, with a header comment naming the orchestrator side so a reader who edits either one is pointed at the other. Traced each case against trace-comment.ts:45-49:

case input expected holds
numeric suffix …d479-span-1 …d479
uuid suffix conn1-msg2-span-f47ac10b-… conn1-msg2 ✅ — lastIndexOf finds the single -span-; the uuid's own hyphens don't match the marker
no suffix scheduler-abc unchanged lastIndexOf-1
empty '' ''

The uuid case is the one worth having: it's the case an anchored -span-\d+$ would silently fail, and its inline comment cites subscription-server.ts:163 as the producer, so the "why not a regex" question is answered in place rather than in review history.

I also checked the file is actually collected — jest.base-ts.config.js matches <rootDir>/test/**/*.test.ts and the package's unit script points at test/unit, so it runs.

Re-verified the re-export is behaviour-preserving

Diffed the pre-fb69fd7 extractRequestUUID against today's toTraceId: identical signature ((requestId: string): string), identical lastIndexOf('-span-') + substring, only the ternary polarity flipped. No caller can observe a difference. The four call sites (QueryCache.ts:410, QueryCache.ts:1078, QueryQueue.ts:522,527) are unchanged, and utils.ts:42 keeps the old name.

Verified unchanged and still sound

  • Sanitization: allowlist not blocklist, both comment delimiters stripped, + excluded with the Hive/Spark hint reason recorded; the hostile-input test asserts delimiter counts, not just absence of the payload.
  • Cap after span-strip ordering (trace-comment.ts:49-51), locked in by the targeted caps after stripping the span, never mid-suffix case.
  • Cache-key deferral: traceQuery only runs at the client.query / client.stream / downloadQueryResults boundary, strictly after QueryCache.queryCacheKey and after the queue dedup key is derived.
  • Append-not-prepend with trailing-semicolon handling; the block-comment form survives driver-side SQL wrapping (CREATE TABLE … AS (query), subquery pagination) where a trailing -- would not.
  • Tagged-query coverage: primaryQuery: true on the background-fetch branch, renewCycle and the scheduler- prefix as deliberately redundant guards, and docs that list cache hits, pre-aggregations and coalescing as the cases with no tagged query to find.

Not verified: the sandbox denied jest, so the assertions above are traced by hand against the implementation rather than from a green run. CI covers it.

Ready to ship pending CI.

· [Branch](https://github.com/cube-js/cube/tree/igor/cub-3093-query-tracing-sql-comment)

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

Labels

data source driver javascript Pull requests that update Javascript code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant