feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(server-utils): Emit low cardinality redis span names - #23741

Merged
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names
Sep 2, 2026
Merged

feat(server-utils): Emit low cardinality redis span names#23741
Lms24 merged 8 commits into
developfrom
lms/feat-server-utils-low-card-redis-span-names

Conversation

@Lms24

@Lms24Lms24 commented Aug 28, 2026

Copy link
Copy Markdown
Member

With span streaming enabled, redis db.query spans are named {db.operation.name} {server.address}:{server.port}. Redis has no SQL statement to summarize and no collection to pair the command with, so the connection is the only low-cardinality target left. The command and its arguments stay on db.query.text. db.namespace is not used: for redis it is the numeric database index.

staticstreamed
ioredisset test-key [1 other arguments]set localhost:6379
node-redisSET test-key [1 other arguments]SET localhost:6379
native DCredis-SETSET localhost:6379
unix socketSET test-key [1 other arguments]redis
FCALLunchangedfcall my_func
batchMULTI / PIPELINEunchanged
cacheunchangedunchanged

Changes:

  • FCALL/FCALL_RO are named after the redis function
  • New db.stored_procedure.name attribute holds that function name
  • Skipped when the library redacted the function name
  • Batch spans now also set db.operation.name
  • node-redis clients always report server.address and server.port
  • Cache span names untouched, already made low cardinality in feat(core): Emit low-cardinality cache.* span names #23830
  • Streamed tests added to four redis suites
  • MIGRATION.md entry added

node-redis v4 writes its localhost:6379 socket defaults back into client.options, v5 does not. The same client therefore reported a connection on v4 and none on v5. Both now resolve it the way node-redis >= 5.12 publishes it on its diagnostics channel, in both trace lifecycles.

Refs #23523

@github-actions

github-actionsBot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

⚠️Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

PathSize% ChangeChange
@sentry/browser28.57 kB--
@sentry/browser - with treeshaking flags26.94 kB--
@sentry/browser - with treeshaking flags tracing without tracing26.83 kB--
@sentry/browser (incl. Tracing)48.87 kB--
@sentry/browser (incl. Tracing + Span Streaming)48.88 kB--
@sentry/browser (incl. Tracing, Profiling)51.8 kB--
@sentry/browser (incl. Tracing, Replay)88.36 kB--
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags77.76 kB--
@sentry/browser (incl. Tracing, Replay with Canvas)93.05 kB--
@sentry/browser (incl. Tracing, Replay, Feedback)106.02 kB--
@sentry/browser (incl. Feedback)46.06 kB--
@sentry/browser (incl. sendFeedback)33.64 kB--
@sentry/browser (incl. FeedbackAsync)38.74 kB--
@sentry/browser (incl. Metrics)29.52 kB--
@sentry/browser (incl. Logs)29.81 kB--
@sentry/browser (incl. Metrics & Logs)30.45 kB--
@sentry/react30.32 kB--
@sentry/react (incl. Tracing)51.08 kB--
@sentry/vue35.74 kB--
@sentry/vue (incl. Tracing)51.14 kB--
@sentry/svelte28.6 kB--
CDN Bundle30.36 kB--
CDN Bundle (incl. Tracing)49.52 kB--
CDN Bundle (incl. Logs, Metrics)32.59 kB--
CDN Bundle (incl. Tracing, Logs, Metrics)51.43 kB--
CDN Bundle (incl. Replay, Logs, Metrics)73.19 kB--
CDN Bundle (incl. Tracing, Replay)87 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics)88.88 kB--
CDN Bundle (incl. Tracing, Replay, Feedback)92.93 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics)94.83 kB--
CDN Bundle - uncompressed89.97 kB--
CDN Bundle (incl. Tracing) - uncompressed147.57 kB--
CDN Bundle (incl. Logs, Metrics) - uncompressed96.27 kB--
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed153.26 kB--
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed225.43 kB--
CDN Bundle (incl. Tracing, Replay) - uncompressed267.07 kB--
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed272.74 kB--
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed280.77 kB--
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed286.43 kB--
@sentry/nextjs (client)53.7 kB--
@sentry/sveltekit (client)49.3 kB--
@sentry/core/server40.74 kB--
@sentry/core/browser13.42 kB--
@sentry/node124.48 kB+0.23%+279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection)81.51 kB--
@sentry/node - without tracing88.3 kB+0.03%+22 B 🔺
@sentry/node - without channel injection103.77 kB+0.26%+265 B 🔺
@sentry/aws-serverless96.66 kB+0.03%+24 B 🔺
@sentry/cloudflare (withSentry) - minified200.81 kB--
@sentry/cloudflare (withSentry)499.76 kB--

View base workflow run

@Lms24Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span namesfeat(server-utils): Emit low cardinality redis span namesAug 31, 2026
@Lms24
Lms24 marked this pull request as ready for review August 31, 2026 16:05
@Lms24
Lms24 requested review from a team as code ownersAugust 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a teamAugust 31, 2026 16:05
@Lms24Lms24 self-assigned this Aug 31, 2026
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5CompareAugust 31, 2026 16:37

@chargomechargome left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Generally LGTM! Just a follow up q. Also likely needs a migration entry.

]);

expect(childSpans(container)).toEqual([
span('redis', redisSpanOp, {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: Just wondering if this span name makes sense compared to redis-4 (where it is SET localhost:6383). Could we somehow apply the same defaults here with localhost and port here?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

good catch, thanks! Updated to also include the host/port

@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faadCompareSeptember 2, 2026 11:42
Lms24and others added 8 commits September 2, 2026 15:03
Redis reports no SQL statement, so there is no query summary to name its spans
after. With span streaming they use the next conventions template that can be
filled instead: the command paired with `{server.address}:{server.port}`, since
redis has no collection or namespace to pair with. It falls back to
`{db.system.name}` when the client was configured without a host.
This keeps the serialized command, which carries the key and its arguments, out
of the span name. It stays on `db.query.text`.
`traceLifecycle: 'static'` keeps the existing names.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pairing the command with `{server.address}:{server.port}` put a host and port in
every redis span name, which says nothing about what ran. Redis has nothing low
cardinality to pair the operation with, so the name is now the bare command,
matching the span name OTel prescribes for redis. The key and its arguments stay
on `db.query.text`.
`db.namespace` is deliberately left out of the name: for redis it is the numeric
database index, which OTel excludes from span names for that reason.
`FCALL`/`FCALL_RO` are the exception, since they name a redis function — the one
redis construct the conventions model as a stored procedure. Those spans report
`db.stored_procedure.name` and pair it with the operation, unless the publishing
library redacted the function name.
Batch spans now report the `MULTI`/`PIPELINE` operation they were already named
after, so their name follows from their attributes too.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Naming streamed redis spans after the bare command dropped the only target the
conventions can fill for redis, leaving `SET` to say nothing about where the
command went. Pair the operation with `{server.address}:{server.port}` again,
falling back to `{db.system.name}` when the client was configured without a host.
The native diagnostics_channel subscriber gets the same name, built from the
`serverAddress`/`serverPort` its payload already carries.
`FCALL`/`FCALL_RO` keep naming the redis function they call: the conventions rank
`db.stored_procedure.name` ahead of the connection.
Refs #23523
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… suites
The streamed expectations these suites gained were written before #23830
landed, so they still expected cache spans to be named after the cache key
and carried no `cache.operation` attribute. Name them after the cache
operation the hook now uses, and fold the repeated peer/operation attributes
into a `cacheSpan` helper so the two cannot drift apart again.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
node-redis v4 writes its `localhost:6379` socket defaults back into
`client.options`, v5 does not. Reading the options as-is therefore reported
`server.address`/`server.port` for a v4 client and neither for the
identically configured v5 one — so the same client got `SET localhost:6383`
on v4 and the bare `redis` fallback on v5 once span names became low
cardinality.
Resolve the connection the way node-redis >= 5.12 reports it on its own
diagnostics channel instead: a TCP client falls back to `localhost:6379`,
and a unix socket reports its path as the address and no port. Batch spans
go through the same helper rather than repeating the option lookup.
The redis-cache suite picks up the low-cardinality cache names from #23830
in the same pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Add the `db.query` (redis, ioredis) row to the span name table and the
prose covering why the connection replaces the serialized command, the
new `db.stored_procedure.name` attribute on `FCALL`, and the node-redis
connection defaults that now apply in both trace lifecycles.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both comments misled a review bot. The connect span shares its trace with
the command spans and lands in the same container, so say what `unordered`
is actually guarding against, and spell out that the conventions have no
address-only template, which is why a unix socket keeps the `redis`
fallback even though its address is known.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#23844 ported this app to span streaming while this branch was open, so its
ioredis assertions still expected the serialized command as the span name.
The route builds its client without a host or port, so the name reports
ioredis' own `localhost:6379` defaults, which the expectations now pin as
`server.address`/`server.port` too. The duplicate-span check keys off
`db.query.text`, which still tells the two commands apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114CompareSeptember 2, 2026 13:08

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

...(options?.socket?.host != null ? { [SERVER_ADDRESS]: options.socket.host } : {}),
...(options?.socket?.port != null ? { [SERVER_PORT]: options.socket.port } : {}),
[SERVER_ADDRESS]: host,
...(port != null ? { [SERVER_PORT]: port } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

URL clients get default Redis target

Medium Severity

nodeRedisAttributes now treats a missing socket.host/socket.path as localhost:6379 and never reads options.url. A node-redis client configured only with a URL therefore reports the wrong server.address/server.port and, when streaming, a wrong span name such as SET localhost:6379.

Fix in CursorFix in Web

Reviewed by Cursor Bugbot for commit 6a59114. Configure here.

@Lms24
Lms24 merged commit 2c64580 into developSep 2, 2026
216 of 217 checks passed
@Lms24
Lms24 deleted the lms/feat-server-utils-low-card-redis-span-names branch September 2, 2026 14:05
s1gr1d added a commit that referenced this pull request Sep 2, 2026
Composes the start-time cache classification with develop's low-cardinality
redis db span names (#23741): at each span start site the cache name/op wins
for cache-prefixed keys, the streamed db name applies otherwise.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Lms24 added a commit that referenced this pull request Sep 2, 2026
Looks like we ran into some timing issues with merging PRs. #23741
changed redis span names to be low cardinality when span streaming is
enabled, and some e2e tests were converted to span streaming at the same
time. However, the e2e tests didn't take these changes into account.
Partially my bad because I wasn't aware that we test redis spans in so
many e2e tests. Good to have them though!
Partially ... merge queues?
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@Lms24@chargome