-
Notifications
You must be signed in to change notification settings - Fork 3.6k
feat: Utility for passing debug correlation id to query implementations #32851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+140
−0
Merged
Changes from 2 commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
48 changes: 48 additions & 0 deletions
48
akka-persistence-query/src/main/scala/akka/persistence/query/QueryCorrelationId.scala
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /* | ||
| * Copyright (C) 2009-2025 Lightbend Inc. <https://www.lightbend.com> | ||
| */ | ||
|
|
||
| package akka.persistence.query | ||
|
|
||
| /** | ||
| * (Optional) mechanism for query implementations to pick up a correlation id from the caller, to use in logging and | ||
| * error messages. Used by akka-projections to make correlating projection logs with debug and trace logging from the | ||
| * underlying akka persistence query implementations possible. | ||
| */ | ||
| object QueryCorrelationId { | ||
|
|
||
| private val threadLocal = new ThreadLocal[String] | ||
|
|
||
| /** | ||
| * Expected to be used "around" calls to plugin query method, will clear the correlation id from thread local | ||
| * to make sure there is no leak between logic executed on shared threads. | ||
| */ | ||
| def withCorrelationId[T](correlationId: String)(block: () => T): T = { | ||
| threadLocal.set(correlationId) | ||
| try { | ||
| block() | ||
| } finally { | ||
| threadLocal.remove() | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Expected to be used "around" calls to plugin query method to pass along a prevously extracted optional correlation id, | ||
| * will clear the correlation id from thread local to make sure there is no leak between logic executed on shared threads. | ||
| */ | ||
| def withCorrelationId[T](correlationId: Option[String])(block: () => T): T = { | ||
| correlationId match { | ||
| case None => block() | ||
| case Some(actualId) => withCorrelationId(actualId)(block) | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @return Expected to be called directly after receiving a query call, before starting any asynchronous tasks, | ||
| * returns and clears out the correlation id to make sure there is no leak between tasks. Further passing | ||
|
||
| * around of the uuid inside the query plugin implementation is up to the implementer. | ||
| */ | ||
| def get(): Option[String] = | ||
| Option(threadLocal.get) | ||
|
|
||
| } | ||
66 changes: 66 additions & 0 deletions
66
akka-persistence-query/src/test/scala/akka/persistence/query/QueryCorrelationIdSpec.scala
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /* | ||
| * Copyright (C) 2009-2025 Lightbend Inc. <https://www.lightbend.com> | ||
| */ | ||
|
|
||
| package akka.persistence.query | ||
|
|
||
| import akka.testkit.TestException | ||
| import org.scalatest.matchers.should.Matchers | ||
| import org.scalatest.wordspec.AnyWordSpecLike | ||
|
|
||
| import java.util.UUID | ||
|
|
||
| class QueryCorrelationIdSpec extends AnyWordSpecLike with Matchers { | ||
|
|
||
| def pretendQueryMethod(): Option[String] = | ||
| QueryCorrelationId.get() | ||
|
|
||
| "The query correlation id utility" should { | ||
|
|
||
| "pass and clear correlation id" in { | ||
| val uuid = UUID.randomUUID().toString | ||
| val observed = | ||
| QueryCorrelationId.withCorrelationId(uuid) { () => | ||
| pretendQueryMethod() | ||
| } | ||
| observed shouldEqual Some(uuid) | ||
|
|
||
| // cleared after returning | ||
| QueryCorrelationId.get() shouldBe None | ||
| } | ||
|
|
||
| "pass along and clear correlation id if present" in { | ||
| val uuid = UUID.randomUUID().toString | ||
| val observed = | ||
| QueryCorrelationId.withCorrelationId(Some(uuid)) { () => | ||
| pretendQueryMethod() | ||
| } | ||
| observed shouldEqual Some(uuid) | ||
|
|
||
| // cleared after returning | ||
| QueryCorrelationId.get() shouldBe None | ||
| } | ||
|
|
||
| "just invoke the block if correlation id not present" in { | ||
| val observed = | ||
| QueryCorrelationId.withCorrelationId(None) { () => | ||
| pretendQueryMethod() | ||
| } | ||
| observed shouldEqual None | ||
| } | ||
|
|
||
| "clear correlation id when call fails" in { | ||
| val uuid = UUID.randomUUID().toString | ||
| intercept[TestException] { | ||
| QueryCorrelationId.withCorrelationId(uuid) { () => | ||
| throw TestException("boom") | ||
| } | ||
| } | ||
|
|
||
| // cleared after throwing | ||
| QueryCorrelationId.get() shouldBe None | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } |
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'll probably only use this ourselves, with Scala, but shall we think about if it works for Java if we are making it public?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. We could mark it as internal, but it will need to be binary compatible either way so I thought it can just as well be public API
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added Java API