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

@Lms24 Lms24 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.

static streamed
ioredis set test-key [1 other arguments] set localhost:6379
node-redis SET test-key [1 other arguments] SET localhost:6379
native DC redis-SET SET localhost:6379
unix socket SET test-key [1 other arguments] redis
FCALL unchanged fcall my_func
batch MULTI / PIPELINE unchanged
cache unchanged unchanged

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-actions Bot 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.

Path Size % Change Change
@sentry/browser 28.57 kB - -
@sentry/browser - with treeshaking flags 26.94 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 26.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 flags 77.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/react 30.32 kB - -
@sentry/react (incl. Tracing) 51.08 kB - -
@sentry/vue 35.74 kB - -
@sentry/vue (incl. Tracing) 51.14 kB - -
@sentry/svelte 28.6 kB - -
CDN Bundle 30.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 - uncompressed 89.97 kB - -
CDN Bundle (incl. Tracing) - uncompressed 147.57 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 96.27 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 153.26 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 225.43 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 267.07 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 272.74 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 280.77 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 286.43 kB - -
@sentry/nextjs (client) 53.7 kB - -
@sentry/sveltekit (client) 49.3 kB - -
@sentry/core/server 40.74 kB - -
@sentry/core/browser 13.42 kB - -
@sentry/node 124.48 kB +0.23% +279 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.51 kB - -
@sentry/node - without tracing 88.3 kB +0.03% +22 B 🔺
@sentry/node - without channel injection 103.77 kB +0.26% +265 B 🔺
@sentry/aws-serverless 96.66 kB +0.03% +24 B 🔺
@sentry/cloudflare (withSentry) - minified 200.81 kB - -
@sentry/cloudflare (withSentry) 499.76 kB - -

View base workflow run

@Lms24 Lms24 changed the title feat(server-utils)!: Emit low cardinality redis span names feat(server-utils): Emit low cardinality redis span names Aug 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 owners August 31, 2026 16:05
@Lms24
Lms24 requested review from isaacs, mydea and s1gr1d and removed request for a team August 31, 2026 16:05
@Lms24 Lms24 self-assigned this Aug 31, 2026
Comment thread packages/server-utils/src/integrations/redis/redis-span-name.ts
@Lms24
Lms24 force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 70ad779 to 87fa5b5 Compare August 31, 2026 16:37

@chargome chargome 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
Member Author

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
Lms24 force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 87fa5b5 to 882faad Compare September 2, 2026 11:42
Comment thread packages/server-utils/src/integrations/redis/redis-span-name.ts
Lms24 and 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
Lms24 force-pushed the lms/feat-server-utils-low-card-redis-span-names branch from 3692135 to 6a59114 Compare September 2, 2026 13:08

@cursor cursor Bot 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 Cursor Fix in Web

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

@Lms24
Lms24 merged commit 2c64580 into develop Sep 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 free to 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