refactor: cache protobuf parser method lookup Motivation: Address PR review feedback that repeatedly calling MethodHandles.publicLookup() has unnecessary overhead while keeping the MethodHandle-based parser resolution behavior. Modification: Store a shared public Lookup for Java protobuf parser discovery and cache parser MethodHandles with a ConcurrentHashMap putIfAbsent pattern to avoid non-atomic TrieMap getOrElseUpdate lookup races. Result: ProtoAnySerialization reuses the public lookup and aligns parser handle caching with the existing ConsumerFilterStore MethodHandle cache style. Tests: - scalafmt --mode diff-ref=origin/main - scalafmt --list --mode diff-ref=origin/main - sbt "grpc-test / Test / testOnly org.apache.pekko.projection.grpc.internal.ProtoAnySerializationSpec" - sbt "jdbc / Test / testOnly org.apache.pekko.projection.jdbc.JdbcProjectionTest org.apache.pekko.projection.jdbc.JdbcOffsetStoreSpec" - sbt "jdbc / Test / testOnly org.apache.pekko.projection.jdbc.H2JdbcOffsetStoreSpec" - sbt "jdbc-int-test / Test / testOnly org.apache.pekko.projection.jdbc.JdbcProjectionSpec" - sbt "examples / Test / compile" - git diff --check - qodercli stdout review: No must-fix findings (/tmp/project-qoder-review-2.log) References: Refs apache/pekko-projection#561
diff --git a/grpc/src/main/scala/org/apache/pekko/projection/grpc/internal/ProtoAnySerialization.scala b/grpc/src/main/scala/org/apache/pekko/projection/grpc/internal/ProtoAnySerialization.scala index 39078e6..09a8afa 100644 --- a/grpc/src/main/scala/org/apache/pekko/projection/grpc/internal/ProtoAnySerialization.scala +++ b/grpc/src/main/scala/org/apache/pekko/projection/grpc/internal/ProtoAnySerialization.scala
@@ -14,6 +14,7 @@ package org.apache.pekko.projection.grpc.internal import java.lang.invoke.{ MethodHandle, MethodHandles, MethodType } +import java.util.concurrent.ConcurrentHashMap import scala.collection.concurrent.TrieMap import scala.collection.immutable @@ -45,8 +46,9 @@ final val PekkoSerializationTypeUrlPrefix = "ser.pekko.io/" final val PekkoTypeUrlManifestSeparator = ':' private final val ProtoAnyTypeUrl = GoogleTypeUrlPrefix + "google.protobuf.Any" + private val publicLookup = MethodHandles.publicLookup() private val parserMethodType = MethodType.methodType(classOf[Parser[?]]) - private val parserHandles = TrieMap.empty[Class[?], MethodHandle] + private val parserHandles = new ConcurrentHashMap[Class[?], MethodHandle] private val log = LoggerFactory.getLogger(classOf[ProtoAnySerialization]) @@ -246,8 +248,15 @@ } } - private def parserHandle(clazz: Class[?]): MethodHandle = - parserHandles.getOrElseUpdate(clazz, MethodHandles.publicLookup().findStatic(clazz, "parser", parserMethodType)) + private def parserHandle(clazz: Class[?]): MethodHandle = { + val cached = parserHandles.get(clazz) + if (cached ne null) cached + else { + val handle = publicLookup.findStatic(clazz, "parser", parserMethodType) + val existing = parserHandles.putIfAbsent(clazz, handle) + if (existing eq null) handle else existing + } + } private def hasExtension[ContainerT <: com.google.protobuf.GeneratedMessage.ExtendableMessage[ContainerT], T]( msg: com.google.protobuf.GeneratedMessage.ExtendableMessageOrBuilder[ContainerT],