ptrdom commented on code in PR #664:
URL: https://github.com/apache/pekko-projection/pull/664#discussion_r4377550266


##########
docs/src/main/paradox/r2dbc.md:
##########
@@ -14,6 +14,10 @@ The target database operations can be run in the same 
transaction as the storage
 that @ref:[exactly-once](#exactly-once) processing semantics is supported. It 
also offers
 @ref:[at-least-once](#at-least-once) semantics.
 
+An `R2dbcSession` can also be used outside of a projection handler, for 
example to query the projected data.
+`R2dbcSession.withSession` runs the given function with a new transaction that 
is committed when the returned
+@scala[`Future`]@java[`CompletionStage`] completes successfully, and rolled 
back if it fails.

Review Comment:
   The rest of this page uses `@@snip` with Scala and Java tabs. Consider 
adding short snippets for both, paired with the Java usage suggested on the 
javadsl file, plus `@apidoc[R2dbcSession$]`. Its own subsection may read better 
than the intro paragraph.



##########
r2dbc/src/main/scala/org/apache/pekko/projection/r2dbc/scaladsl/R2dbcSession.scala:
##########
@@ -19,10 +19,55 @@ import scala.concurrent.Future
 
 import org.apache.pekko
 import pekko.actor.typed.ActorSystem
+import pekko.annotation.InternalApi
+import pekko.persistence.r2dbc.ConnectionFactoryProvider
 import pekko.persistence.r2dbc.internal.R2dbcExecutor
+import pekko.projection.r2dbc.R2dbcProjectionSettings
 import io.r2dbc.spi.Connection
 import io.r2dbc.spi.Row
 import io.r2dbc.spi.Statement
+import org.slf4j.LoggerFactory
+
+object R2dbcSession {
+  private val log = LoggerFactory.getLogger(classOf[R2dbcSession])
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * The connection factory is the one configured by 
`pekko.projection.r2dbc.use-connection-factory`.
+   *
+   * @since 2.0.0
+   */
+  def withSession[A](system: ActorSystem[?])(fun: R2dbcSession => Future[A]): 
Future[A] =
+    withSession(system, connectionFactoryConfigPath(system))(fun)
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * @param connectionFactoryConfigPath config path of the connection factory 
to use, for example
+   *   `pekko.persistence.r2dbc.connection-factory`
+   * @since 2.0.0
+   */
+  def withSession[A](system: ActorSystem[?], connectionFactoryConfigPath: 
String)(
+      fun: R2dbcSession => Future[A]): Future[A] = {
+    val connectionFactory = 
ConnectionFactoryProvider(system).connectionFactoryFor(connectionFactoryConfigPath)
+    val r2dbcExecutor =
+      new R2dbcExecutor(connectionFactory, log, 
R2dbcProjectionSettings(system).logDbCallsExceeding)(

Review Comment:
   `connectionFactoryFor(path)` and `R2dbcProjectionSettings(system)` run 
before any `Future` exists. A typo in the config path throws `ConfigException` 
straight out of `withSession`, so a Java caller chaining `.exceptionally(...)` 
never sees it. Consider wrapping it in `try { ... } catch { case NonFatal(e) => 
Future.failed(e) }`. Upstream does the same thing, so this is optional.



##########
r2dbc/src/main/scala/org/apache/pekko/projection/r2dbc/scaladsl/R2dbcSession.scala:
##########
@@ -19,10 +19,55 @@ import scala.concurrent.Future
 
 import org.apache.pekko
 import pekko.actor.typed.ActorSystem
+import pekko.annotation.InternalApi
+import pekko.persistence.r2dbc.ConnectionFactoryProvider
 import pekko.persistence.r2dbc.internal.R2dbcExecutor
+import pekko.projection.r2dbc.R2dbcProjectionSettings
 import io.r2dbc.spi.Connection
 import io.r2dbc.spi.Row
 import io.r2dbc.spi.Statement
+import org.slf4j.LoggerFactory
+
+object R2dbcSession {
+  private val log = LoggerFactory.getLogger(classOf[R2dbcSession])
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * The connection factory is the one configured by 
`pekko.projection.r2dbc.use-connection-factory`.
+   *
+   * @since 2.0.0
+   */
+  def withSession[A](system: ActorSystem[?])(fun: R2dbcSession => Future[A]): 
Future[A] =
+    withSession(system, connectionFactoryConfigPath(system))(fun)
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * @param connectionFactoryConfigPath config path of the connection factory 
to use, for example
+   *   `pekko.persistence.r2dbc.connection-factory`
+   * @since 2.0.0
+   */
+  def withSession[A](system: ActorSystem[?], connectionFactoryConfigPath: 
String)(
+      fun: R2dbcSession => Future[A]): Future[A] = {
+    val connectionFactory = 
ConnectionFactoryProvider(system).connectionFactoryFor(connectionFactoryConfigPath)
+    val r2dbcExecutor =
+      new R2dbcExecutor(connectionFactory, log, 
R2dbcProjectionSettings(system).logDbCallsExceeding)(
+        system.executionContext,
+        system)
+    r2dbcExecutor.withConnection("R2dbcSession") { connection =>
+      fun(new R2dbcSession(connection)(system.executionContext, system))
+    }
+  }
+
+  /**
+   * INTERNAL API
+   */
+  @InternalApi private[pekko] def connectionFactoryConfigPath(system: 
ActorSystem[?]): String =
+    
system.settings.config.getConfig(R2dbcProjectionSettings.DefaultConfigPath).getString("use-connection-factory")

Review Comment:
   Nit: `R2dbcProjectionSettings(system)` re-parses the whole 
`pekko.projection.r2dbc` block on every call, and this helper then reads 
`use-connection-factory` a second time, but the settings object already has it 
as `useConnectionFactory`. You could use that and drop the helper. It's 
`@InternalApi`, so removing it is binary-safe.



##########
r2dbc/src/main/scala/org/apache/pekko/projection/r2dbc/javadsl/R2dbcSession.scala:
##########
@@ -25,10 +26,45 @@ import org.apache.pekko
 import pekko.actor.typed.ActorSystem
 import pekko.annotation.ApiMayChange
 import pekko.persistence.r2dbc.internal.R2dbcExecutor
+import pekko.projection.r2dbc.scaladsl
 import io.r2dbc.spi.Connection
 import io.r2dbc.spi.Row
 import io.r2dbc.spi.Statement
 
+object R2dbcSession {
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * The connection factory is the one configured by 
`pekko.projection.r2dbc.use-connection-factory`.
+   *
+   * @since 2.0.0
+   */
+  def withSession[A](system: ActorSystem[?], fun: JFunction[R2dbcSession, 
CompletionStage[A]]): CompletionStage[A] =
+    withSession(system, 
scaladsl.R2dbcSession.connectionFactoryConfigPath(system), fun)
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * @param connectionFactoryConfigPath config path of the connection factory 
to use, for example
+   *   `pekko.persistence.r2dbc.connection-factory`
+   * @since 2.0.0
+   */
+  def withSession[A](
+      system: ActorSystem[?],
+      connectionFactoryConfigPath: String,
+      fun: JFunction[R2dbcSession, CompletionStage[A]]): CompletionStage[A] =
+    scaladsl.R2dbcSession
+      .withSession(system, connectionFactoryConfigPath) { scaladslSession =>
+        val javadslSession = new 
R2dbcSession(scaladslSession.connection)(system.executionContext, system)
+        fun(javadslSession).asScala
+      }
+      .asJava
+
+}
+
 @ApiMayChange

Review Comment:
   Nit: the javadsl class keeps `@ApiMayChange`, but the new companion object 
and the scaladsl side don't have it. Upstream dropped the annotation in #1017. 
Either drop it here too, which needs maintainers to agree to stabilise the 
javadsl, or annotate the new `object R2dbcSession` as well so both sides 
promise the same stability.



##########
r2dbc/src/main/scala/org/apache/pekko/projection/r2dbc/javadsl/R2dbcSession.scala:
##########
@@ -25,10 +26,45 @@ import org.apache.pekko
 import pekko.actor.typed.ActorSystem
 import pekko.annotation.ApiMayChange
 import pekko.persistence.r2dbc.internal.R2dbcExecutor
+import pekko.projection.r2dbc.scaladsl
 import io.r2dbc.spi.Connection
 import io.r2dbc.spi.Row
 import io.r2dbc.spi.Statement
 
+object R2dbcSession {
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * The connection factory is the one configured by 
`pekko.projection.r2dbc.use-connection-factory`.
+   *
+   * @since 2.0.0
+   */
+  def withSession[A](system: ActorSystem[?], fun: JFunction[R2dbcSession, 
CompletionStage[A]]): CompletionStage[A] =
+    withSession(system, 
scaladsl.R2dbcSession.connectionFactoryConfigPath(system), fun)
+
+  /**
+   * Runs the passed function using a R2dbcSession with a new transaction. The 
connection is closed and the
+   * transaction is committed at the end or rolled back in case of failures.
+   *
+   * @param connectionFactoryConfigPath config path of the connection factory 
to use, for example
+   *   `pekko.persistence.r2dbc.connection-factory`
+   * @since 2.0.0
+   */
+  def withSession[A](
+      system: ActorSystem[?],
+      connectionFactoryConfigPath: String,
+      fun: JFunction[R2dbcSession, CompletionStage[A]]): CompletionStage[A] =
+    scaladsl.R2dbcSession
+      .withSession(system, connectionFactoryConfigPath) { scaladslSession =>
+        val javadslSession = new 
R2dbcSession(scaladslSession.connection)(system.executionContext, system)
+        fun(javadslSession).asScala
+      }
+      .asJava

Review Comment:
   The javadsl tests are in Scala, so they call `withSession` through the Scala 
object. They never exercise the static forwarders that Java users rely on, or 
how Java infers lambda types against the overloads. If a later change 
suppresses the forwarders, every test still passes, but 
`R2dbcSession.withSession(system, session -> ...)` stops compiling for Java 
users.
   
   Could you add a small Java usage? For example, a snippet in 
`R2dbcProjectionDocExample.java` or a Java compile test along the lines of 
grpc's `ConsumerCompileTest`. That also keeps the Scala and Java DSLs in step, 
as AGENTS.md asks.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to