Repository navigation
Fix NPE and lost trace context of reactive Lettuce commands when tracing is enabled - #841
Merged
wu-sheng merged 3 commits intoOct 31, 2026
Merged
Conversation
…ing is enabled Put the snapshot on the command that carries the span instead of the TracedCommand wrapper, and skip the command interceptors for a command without a span.
carteruu
force-pushed
the
fix-lettuce-decorated-command-npe
branch
from
October 30, 2026 15:52
2b2c90d to
84cb7cd
Compare
ubuntu-latest now resolves to ubuntu-26.04, which ships Docker 29 with the containerd image store. The pinned infra-e2e action (8c21e43) disables the containerd snapshotter and restarts dockerd before running the case, so the agent image built and loaded by `make build docker.java8` (skywalking/skywalking-java:latest-java8) is no longer visible. Compose then tries to pull it from Docker Hub and both E2E cases fail with "pull access denied". Upstream infra-e2e removed that step in apache/skywalking-infra-e2e#146 and runs compose through testcontainers-go. Pin the current main (56ac8befeb5f910f2dc9339e65d07cd18953f90e), whose CI passes on ubuntu-26.04. The new action installs Go 1.26 itself. Drop the workflow's own actions/setup-go@v2 (Go 1.24) step: it exports GOROOT, which makes the action's Go 1.26 toolchain compile against the Go 1.24 standard library ("compile: version go1.24.13 does not match go tool version go1.26.10").
The scenario consumer has no committed offset and starts from the latest offset, while healthCheck reported success as soon as Tomcat started. When the health check hit right after startup, the entry request sent "helloWorld" before the consumer got its partition, so the consumer skipped it, the request waited forever on the latch and its segment was never reported (SegmentNotFoundException for GET:/case/spring-kafka-case). On ubuntu-26.04 runners spring-kafka 2.7.14 lost this race on every attempt. Report healthy only after partitions are assigned, the same way kafka-scenario does.
wu-sheng
approved these changes
Oct 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix the
NullPointerExceptionand the lost trace context of reactive Lettuce commands when Lettuce tracing is enabled (apache/skywalking#14178)When Lettuce tracing is enabled, which Spring Boot does by default when there is an
ObservationRegistrybean, Lettuce wraps each reactive command in aTracedCommand.RedisSubscriptionConstructorInterceptorstores that wrapper, andRedisSubscriptionSubscribeMethodInterceptorputs the snapshot on it, butRedisChannelWriterInterceptorunwrapsDecoratedCommandand creates the span on the innermost command. So the snapshot and the span end up on different objects:RedisCommandEnhanceInfowithout a span. When it completes or is cancelled, the command interceptors dereference the null span, which is the NPE from the issue.RedisReactive/localandContextManager.continuedare skipped, and the exit span is not linked to the trace that issued the command.A second case doesn't need tracing. A reactive command cancelled after subscribing but before any demand has a snapshot and no span, and
RedisPublisher.State#cancelcallscommand.cancel()directly.This PR fixes both:
RedisSubscriptionConstructorInterceptorreusesgetSpanCarrierCommand, so the snapshot goes on the command that carries the span. Commands without a decorator are unchanged.RedisCommandEnhanceInfo#getSpan(EnhancedInstance)returns null when a command has no span, and the complete, completeExceptionally and cancel interceptors skip such a command.RedisReactiveCommandSpanTestuses the realTracedCommand. Before this changesnapshotOfDecoratedCommandIsContinuedOnSpanCarrierCommandfails andcommandCancelledBeforeWriteHasNoSpanToFinishfails with the NPE.commandWithoutDecoratorStillContinuesSnapshotpasses before and after, and guards the existing behavior.I also ran the agent against real Lettuce on JDK 17, with 3
SETcommands plus 1 cancelled before demand, against a minimal fake Redis server:SET[Lettuce/SET], no reference[Lettuce/SET, RedisReactive/local], with reference[Lettuce/SET, RedisReactive/local], with reference[Lettuce/SET, RedisReactive/local], with reference./mvnw verifypasses forlettuce-commonand both Lettuce plugins. I did not run thelettuce-*plugin test scenarios locally, and they don't enable Lettuce tracing.CHANGESlog.