Report configured Redis targets for Rediscala by trask · Pull Request #20075 · open-telemetry/opentelemetry-java-instrumentation · GitHub
Skip to content

Report configured Redis targets for Rediscala - #20075

Merged
trask merged 11 commits into
mainfrom
trask-redis-rediscala-targets
Sep 21, 2026
Merged

trask merged 11 commits into
mainfrom
trask-redis-rediscala-targets

Conversation

@trask

@trask trask commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Reports configured Redis deployments in stable server.address and server.port for Rediscala direct clients, pools, replicated clients, clusters, Sentinel deployments, and transactions. These attributes describe the configured deployment instead of the network peer selected for a request. Legacy database semantic convention output does not change.

This is the Rediscala slice of #20010 and follows #19899.

Stacked on #20070, which introduces the shared RedisServerTarget.ofEndpointAndUnorderedEndpoints helper this PR reuses for master-and-replicas targets.

@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Cluster requests do not match the current request matcher, leaving cluster commands uninstrumented and the configured-target path unreachable.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Updates Rediscala instrumentation to report configured Redis targets through stable server attributes while preserving legacy semantic-convention behavior.

Changes:

  • Adds target extraction for clients, pools, replicas, Sentinel, clusters, and transactions.
  • Propagates configured targets through request and transaction instrumentation.
  • Adds integration and unit-test coverage.
File summaries
File Reviewed changes
settings.gradle.kts Registers unit tests.
instrumentation/rediscala-1.8/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaConfiguredTargetTest.scala Tests configured-target behavior.
instrumentation/rediscala-1.8/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaClientTest.scala Updates stable target assertions.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/TransactionInstrumentation.java Applies targets to transaction spans.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/TransactionBuilderInstrumentation.java Captures transaction client state.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/ServerEndpoint.java Resolves operation endpoints.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RequestInstrumentation.java Adds request target extraction; the cluster matcher path remains unreachable.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaTransactionState.java Stores transaction client state.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaSingletons.java Adds virtual-field state.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaServerTargets.java Extracts and refreshes configured targets.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaRequest.java Carries configured targets.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaMutablePoolInstrumentation.java Tracks mutable-pool changes.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaInstrumentationModule.java Registers pool instrumentation.
instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaAttributesGetter.java Emits stable server attributes.
instrumentation/rediscala-1.8/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/ServerEndpointTest.java Tests endpoint resolution.
instrumentation/rediscala-1.8/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaServerTargetsTest.java Tests target extraction and refresh.
instrumentation/rediscala-1.8/javaagent-unit-tests/src/test/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaAttributesGetterTest.java Tests stable and legacy attributes.
instrumentation/rediscala-1.8/javaagent-unit-tests/build.gradle.kts Configures unit-test dependencies and execution.
Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved critical and moderate findings affect configured-target resolution and master/slave client coverage.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RequestInstrumentation.java:55

  • RedisClientMasterSlaves is not covered by this matcher in Rediscala 1.8: it implements RedisCommands with Transactions and defines send directly, rather than extending ActorRequest, Request, BufferedRequest, RedisCluster, or RoundRobinPoolRequest. Its commands therefore only reach the delegated master/pool instrumentation, so stable spans report the selected child target instead of the configured master-plus-replicas target (and the new master/slaves target path is never used). Add dedicated coverage for this wrapper while preventing its delegated send from creating a second span, or propagate the wrapper target into the delegated request.
            "redis.RedisCluster",
            "redis.RoundRobinPoolRequest"));

instrumentation/rediscala-1.8/javaagent/src/test/scala/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaConfiguredTargetTest.scala:133

  • [Testing] This is the only integration test for the new RedisCluster target path, but it is skipped unless testLatestDeps() is enabled. The module's normal test dependency is com.github.etaty:rediscala_2.11:1.8.0, which also provides RedisCluster, so the baseline supported artifact never exercises CLUSTER_REDIS_SERVERS or its matcher. Run this test with the regular dependency as well (or add an equivalent baseline test) to catch version-specific target extraction regressions.
  @Test def testClusterCommandUsesConfiguredTarget(): Unit = {
    assumeTrue(emitStableDatabaseSemconv())
    assumeTrue(testLatestDeps())
  • Files reviewed: 18/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Cache immutable configured targets per client to address the identified command-path performance issue.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Two moderate unresolved issues affect target invalidation concurrency and Sentinel blocking-client database namespace reporting.

Review details

Suppressed comments (2)

instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RediscalaServerTargets.java:343

  • [Concurrency] markUnavailable() writes target = null without coordinating with the map lock used by refresh(). A successful mutation's exit advice can be delayed until after a concurrent failed mutation marks the pool unavailable, then publish a snapshot and make stable spans report a target that was intentionally invalidated. Serialize invalidation with refresh or add a generation check so an in-flight refresh cannot republish after a failed update.
  public static void markMutablePoolUnavailable(Object pool) {
    MutablePoolState state = mutablePoolState(pool);
    if (state != null) {
      state.markUnavailable();
    }

instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/ServerEndpoint.java:47

  • [General] Stable spans from SentinelMonitoredRedisBlockingClient lose the configured database namespace here. RequestInstrumentation calls this overload for stable semconv, but this branch only recognizes SentinelMonitoredRedisClient; the blocking client is a sibling wrapper with its own redisClient, so endpoint stays null and RediscalaAttributesGetter.getDbNamespace() cannot report its configured db even though RediscalaServerTargets already handles the blocking sentinel target. Resolve the blocking wrapper's redisClient here as well (without changing the legacy path).
    if (client instanceof SentinelMonitoredRedisClient) {
      RedisClientActorLike redisClient = ((SentinelMonitoredRedisClient) client).redisClient();
      return redisClient != null ? create(redisClient) : null;
  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@trask trask changed the title Capture configured Rediscala Redis targets Report configured Redis targets for Rediscala Sep 10, 2026
@trask
trask requested a balanced review from Copilot September 10, 2026 08:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Two unresolved moderate findings affect replicated-client instrumentation and Sentinel blocking-client database attributes.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RequestInstrumentation.java:55

  • [Bug] RedisClientMasterSlaves in Rediscala 1.8 does not implement Request, ActorRequest, or RoundRobinPoolRequest; it only mixes in RedisCommands/Transactions and defines send itself. Therefore its direct commands never reach SendAdvice, so the new replicated-client target extraction is unreachable for ordinary reads and writes. Add this concrete type to the matcher.
            "redis.RedisCluster",
            "redis.RoundRobinPoolRequest"));
  • Files reviewed: 18/18 changed files
  • Comments generated: 1
  • Review effort level: Lite

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No unresolved review issues were identified.

Review details
  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@trask
trask changed the base branch from main to trask-redis-redisson-targets September 10, 2026 17:20
@trask
trask added this pull request to stack #20098 September 10, 2026 20:10
@trask
trask marked this pull request as ready for review September 13, 2026 21:30
@trask
trask requested a review from a team as a code owner September 13, 2026 21:30
if (MUTABLE_POOL_CLASS != null && MUTABLE_POOL_CLASS.isInstance(client)) {
return ofMutablePool(client);
}
if (client instanceof RedisClientActorLike) {

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.

RedisClient lands on this branch rather than the memoized Request branch below it, which I think is deliberate and correct since reconnect() mutates host/port in place, so a cached target would go stale. But it means every command in stable mode recomputes the target through ofHostAndPort → endpoint() (StringBuilder) → Endpoint.parse() → DbServerTarget.builder().build().

I ran some benchmarks and the latency was fine but it introduced additional allocations of 320 B/op of garbage per command. For a high throughput pipeline doing 100k ops/s that's ~32 MB/s of allocation that didn't exist before

RedisClientActorLike.reconnect(String, int) is a concrete public method, so a VirtualField<RedisClientActorLike, RedisServerTarget> cleared by a small advice on reconnect would get RedisClient onto the cached path and remove essentially all of it. testReconnectRefreshesServerTarget / testTransactionRefreshesServerTarget already cover the invalidation.

This comment was marked as low quality.

@trask trask Sep 15, 2026 •

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.

oh, good catch, thanks! 0484acf

Comment on lines +262 to +267
endpoints.add(null);
continue;
}
RedisServer redisServer = (RedisServer) server;
endpoints.add(RedisServerTarget.endpoint(redisServer.host(), redisServer.port()));
}

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.

we could reuse endpoint(Object) at line 349 which already does this instanceof RedisServer → RedisServerTarget.endpoint(host, port), returning null for anything else, which is the same fail-closed sentinel this loop is building.

The loop at lines 190-196 does the same conversion (it fails fast rather than appending null, but the two lines that build the endpoint are identical).

This comment was marked as low quality.

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.

@jaydeluca jaydeluca Sep 14, 2026 •

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.

This is generic over <T> but has exactly one call site, and it makes RediscalaSingletons call back into RediscalaServerTargets, which already statically imports REQUEST_TARGET from here. Moving it into RediscalaServerTargets as a non-generic Request overload would drop both the cycle and the unused generality.

This comment was marked as low quality.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cluster commands remain outside the request matcher, so their configured-target spans are not produced.

Review details

Suppressed comments (1)

instrumentation/rediscala-1.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/rediscala/v1_8/RequestInstrumentation.java:87

  • [General] The current head no longer contains the cluster instrumentation added in be221ed: typeMatcher() still excludes redis.RedisCluster, and end() has no way to obtain a cluster's RedisClientPoolLike.executionContext. Since RedisCluster declares its own send, cluster commands never reach this target extraction and produce no spans. Restore the cluster matcher and cached reflective execution-context handling from that fix.
          endpoint = ServerEndpoint.create(action, cmd.isMasterOnly());
          serverTarget = RediscalaServerTargets.get(action);
  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cross-version reflection, asynchronous client behavior, and mutable topology tracking warrant final human review.

Review details
  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Cross-version Scala reflection, asynchronous clients, and mutable topology tracking warrant final human validation.

Review details
  • Files reviewed: 19/19 changed files
  • Comments generated: 0 new
  • Review effort level: Balanced

Base automatically changed from trask-redis-redisson-targets to main September 18, 2026 22:41
trask and others added 11 commits September 18, 2026 15:41
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot comment:

This new cluster branch is unreachable for cluster commands: `RequestInstrumentation.typeMatcher()` still matches only `ActorRequest`, `Request`, `BufferedRequest`, and `RoundRobinPoolRequest`, but Rediscala 1.8's `RedisCluster` extends `RedisClientPoolLike` directly and declares its public `send` method. Consequently cluster commands produce no spans and never reach this configured-target extraction; include `redis.RedisClientPoolLike` (or `redis.RedisCluster`) in that matcher.

Analysis: `RedisCluster` declares the one-argument `send` method but did not match `RequestInstrumentation`, so its commands produced no spans. The matcher now includes only `RedisCluster` to avoid adding a second span around existing pool commands. Cluster futures use the pool execution context, which is resolved reflectively because its JVM return type differs across supported Rediscala versions.

Upsides: Cluster commands now produce spans with configured server targets. The focused matcher preserves existing pool span behavior, and reflective lookup keeps muzzle compatibility across Scala and Rediscala versions.

Downsides: Cluster completion adds one cached reflective method invocation per command.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review finding:

`endpoint(Object)` is declared `public static`, but the only caller is `MutablePoolState.snapshot`, a private method of a nested class in this same file. No advice class, no other instrumentation class, and no test refers to it. The repository's Java style rules require the principle of least access: "Use the most restrictive modifier that still works." Make the method `private static`.

Review finding:

`MutablePoolState.markUnavailable()` and `MutablePoolState.refresh(HashMap)` are declared `public`, but every caller is either an enclosing `RediscalaServerTargets` method in this same file or `RediscalaServerTargetsTest`, which sits in the same package. The two sibling members of this class, `fromMap` and `target()`, are already package-private and carry `// visible for testing` comments, so the public modifier on these two methods is inconsistent with the class's own design and with the repository's least-access style rule. Make both methods package-private.

Analysis: `endpoint` has a single caller, `MutablePoolState.snapshot`, in the same top-level class, so `private static` is the most restrictive modifier that still compiles. `MutablePoolState` itself is only referenced by its enclosing class and by `RediscalaServerTargetsTest`, which declares the same package, so the class and its two mutating methods drop to package-private alongside the `fromMap` and `target()` members that were already package-private. A `VirtualField` value type does not need to be public: `ElasticsearchTransportServerTargets` uses a `private static class UpdateState` as its value type, so the runtime accepts a non-public one.

Upsides: The class now applies one consistent visibility rule instead of two. Nothing outside this package can call into instrumentation-internal state, and a future reader can find every caller of these members without searching the whole repository.

Downsides: No material downside identified. The unit test lives in a different Gradle module but the same package, so package-private access still works, which the module's test run confirms.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot comment:

[General] Stable requests from `SentinelMonitoredRedisBlockingClient` are handled by `RequestInstrumentation`, and `RediscalaServerTargets` already recognizes that client, but this endpoint overload does not. Because the blocking client carries its configured database on its underlying `redisClient`, `ServerEndpoint` stays null and stable `db.namespace` is omitted for every blocking Sentinel command (notably non-default databases). Add a branch that unwraps the blocking client's `redisClient` just like the regular Sentinel branch.

Analysis: `RequestInstrumentation` passes blocking Sentinel requests to `ServerEndpoint.create(Object, boolean)`, but the blocking client does not implement `RedisClientActorLike`. The new branch unwraps its `redisClient()` before endpoint extraction and does not depend on the master-only flag.

Upsides: Stable spans for blocking Sentinel commands now include the configured server endpoint and database namespace. The test covers a non-master command with a non-default database.

Downsides: Stable request handling performs one extra type check for clients that did not match an earlier branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Review finding:

This new `else if (action instanceof RedisClientPoolLike && POOL_EXECUTION_CONTEXT != null)` branch can never run, because the earlier `action instanceof Request` branch always claims the same receivers. Every rediscala class that extends `redis.RedisClientPoolLike` also implements `redis.RedisCommands`, and `redis.RedisCommands` extends `redis.commands.Keys`, which extends `redis.Request`. I checked this with javap over the whole supported range: `redis.RedisClientPool` in 1.5.0, 1.6.0, 1.7.0, 1.8.0, 1.9.0, 1.13.0 and 1.17.0; `redis.RedisClientMutablePool` in 1.6.0 onward; and `redis.RedisCluster` in 1.8.0 onward. Each one also exposes a working `executionContext()` returning `scala.concurrent.ExecutionContext`, so the existing `((Request) action).executionContext()` call already resolves correctly, including in 1.8.0 where `RedisClientPoolLike.executionContext()` itself returns `akka.dispatch.MessageDispatcher`. Reordering the branch would not help either, since the `Request` branch wins whatever the order. The remaining shapes `typeMatcher()` admits are covered too: an `ActorRequest` receiver is taken by the first branch, a `BufferedRequest` receiver never reaches `end()` because `start()` returns null for it, a `redis.RedisCluster` receiver implies `redis.Request`, and a `RoundRobinPoolRequest` receiver that somehow is not a `Request` is already served by the later `RoundRobinPoolRequest` branch with a direct call, which this reflective branch would only shadow. For completeness, the `BufferedRequest` branch in the same chain is also unreachable, but it predates this pull request and establishes no convention for adding new dead branches. Delete the branch together with the `POOL_EXECUTION_CONTEXT` field and the `logger` field it needs, and drop the imports that become unused: the static imports of `RediscalaServerTargets.findMethod` and `java.util.logging.Level.FINE`, plus `java.lang.reflect.Method`, `java.util.logging.Logger` and `redis.RedisClientPoolLike`. Keep the new `ctx == null` guard, which is a real fix.

Review finding:

Adding `"redis.RedisCluster"` to `hasSuperType(namedOneOf(...))` matches nothing that the list did not already match. `redis.RedisCluster` implements `redis.RedisCommands`, which extends `redis.commands.Keys`, which extends `redis.Request`, and `"redis.Request"` is already an entry in the same list. I confirmed the hierarchy with javap on 1.8.0, 1.9.0, 1.13.0 and 1.17.0, and `redis.RedisCluster` does not exist at all in 1.5.0, so there is no supported version where the extra entry changes which types are instrumented. The entry is also the only concrete class in a list that otherwise names traits, which makes the matcher read as though cluster clients need a special case when they do not. Remove the line.

Analysis: Both findings share one root cause. The pull request treated rediscala pool and cluster clients as a shape the existing instrumentation did not already cover, but they were covered all along, because `redis.RedisCommands` reaches `redis.Request` through `redis.commands.Keys`. That single fact makes the added type matcher entry match no new type and makes the added reflective execution context branch unreachable, since the `Request` branch takes every receiver that could have reached it. The reflection also solved a problem that does not arise: `RedisClientPoolLike.executionContext()` does return a different type across versions, but the call site goes through `Request`, whose signature is stable. So the fix removes the matcher entry, the dead branch, the `POOL_EXECUTION_CONTEXT` handle and the `logger` it used to report a failed lookup, and the five imports that then have no use. The `ctx == null` guard that the same pull request added stays, because a null execution context is reachable and skipping the callback for it is correct.

Upsides: The advice class no longer performs a reflective method lookup at class initialization, nor an `instanceof` and a null check per request, for a branch that never executes. The type matcher lists only traits again, so it no longer suggests that cluster clients need a special case. Five imports, two fields and a logger disappear, which leaves less code to keep working across the seven rediscala versions this instrumentation supports.

Downsides: If a future rediscala release introduced a `RedisClientPoolLike` implementation that is not a `redis.Request`, its execution context would no longer be found. That receiver would also fail the type matcher, so it would not be instrumented at all, and the branch would not have helped it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Master-and-replicas targets now go through RedisServerTarget.ofEndpointAndUnorderedEndpoints, the helper PR #20070 introduced for Redisson, instead of hand-rolling the master-first sort. Behavior is unchanged: the master stays first, replicas are sorted, and an unsupported replica still fails closed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Review finding:

`typeMatcher()` in this new instrumentation uses only `named("redis.RedisClientActorLike")`, but the class also adds `classLoaderOptimization()` with the same type name. The repository's TypeInstrumentation guidance says name-only matchers are already fast and that `classLoaderOptimization()` should be added for expensive hierarchy or annotation matchers. Remove this redundant override and its `hasClassesNamed` static import.

Analysis: The exact-name matcher already rejects every unrelated type without inspecting bytecode. The extra class-loader probe repeats the same name check at an earlier stage and does not narrow the match.

Upsides: Removes redundant matcher work and follows the repository's TypeInstrumentation guidance.

Downsides: No material downside identified.

Review finding:

`typeMatcher()` in this new instrumentation uses only `named("redis.RedisClientMutablePool")`, but the class also adds `classLoaderOptimization()` with the same type name. The repository's TypeInstrumentation guidance says name-only matchers are already fast and that `classLoaderOptimization()` should be added for expensive hierarchy or annotation matchers. Remove this redundant override and its `hasClassesNamed` static import.

Analysis: The exact-name matcher already rejects every unrelated type without inspecting bytecode. The extra class-loader probe repeats the same name check at an earlier stage and does not narrow the match.

Upsides: Removes redundant matcher work and follows the repository's TypeInstrumentation guidance.

Downsides: No material downside identified.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 73cbb4d5-6c82-4793-8bbf-4771aa8ceb10
Review finding:

`RediscalaServerTargets.get(Object)` is declared `public`, but its only production callers are `AdviceScope.start` helpers in `RequestInstrumentation` and `TransactionInstrumentation`, which are in the same package. The annotated advice methods call those `start` helpers rather than calling `get` directly, and the repository visibility guidance says only members directly referenced by advice need public access; transitive helpers can stay package-private. Remove the `public` modifier.

Analysis: Both callers compile in the same package. The method is not part of an inlined advice body, so package access preserves advice dispatch while avoiding an unnecessary public helper API.

Upsides: Applies least access and keeps the helper surface limited to its package.

Downsides: No material downside identified.

Review finding:

The new `ServerEndpoint.create(Object)` and `create(Object, boolean)` overloads are declared `public`, but every caller is in this package. The annotated advice directly calls only the pre-existing `create(RedisClientActorLike)` overload; the new overloads are reached from `AdviceScope.start` helpers, so the repository's direct-advice visibility exception does not require them to be public. Remove `public` from both new overloads.

Analysis: The two Object overloads are package-internal dispatch helpers. The directly advice-referenced RedisClientActorLike overload remains public, so narrowing these overloads does not change advice linkage.

Upsides: Applies least access without changing endpoint selection behavior.

Downsides: No material downside identified.

Review finding:

`RediscalaTransactionState.getClient()` and `getEndpoint()` are declared `public`, but their only caller is `TransactionInstrumentation.AdviceScope.start` in the same package. The state class and constructor need public access because the annotated transaction-builder advice directly creates the value, but no advice method directly calls either getter. Make both getters package-private under the repository's least-access rule.

Analysis: TransactionInstrumentation reads both values from a same-package helper. The class and constructor stay public for direct advice construction, while the getters need no cross-package access.

Upsides: Keeps only the advice-linked state construction API public.

Downsides: No material downside identified.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 73cbb4d5-6c82-4793-8bbf-4771aa8ceb10
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Cross-version Scala compatibility, reflection, asynchronous completion, and mutable topology tracking warrant final human review.

Review effort: Balanced
Findings: None

@trask
trask merged commit 9a24858 into main Sep 21, 2026
103 checks passed
@trask
trask deleted the trask-redis-rediscala-targets branch September 21, 2026 02:42
laurit pushed a commit to laurit/opentelemetry-java-instrumentation that referenced this pull request Sep 21, 2026
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 73cbb4d5-6c82-4793-8bbf-4771aa8ceb10
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.

3 participants