ptrdom opened a new pull request, #668:
URL: https://github.com/apache/pekko-projection/pull/668

   ### Motivation
   Akka Projection 1.5.0 (Apache 2.0 since its 2026-10-25 change date) added H2 
support to the R2DBC projections:
   
   - akka/akka-projection#914 H2 support in R2DBC projections (H2 dialect, 
`H2OffsetStoreDao`, test config)
   - akka/akka-projection#938 Default H2 schema through config instead of 
copy-paste
   
   apache/pekko-persistence-r2dbc#517 added the `h2` dialect to the persistence 
plugin, so the projections can follow.
   
   **Draft until pekko-persistence-r2dbc is released.** This builds against 
`2.0.0-M1+113-ac55a4d8-SNAPSHOT`, which includes #517. The snapshot 
repository's `maven-metadata.xml` still reports an older snapshot (`+101`) as 
`latest`, so the version is pinned rather than resolved as the latest snapshot. 
Once the release is out, only `PekkoPersistenceR2DBCDependency.currentVersion` 
needs to change.
   
   ### Modification
   - **Dialect:** `R2dbcOffsetStore.fromConfig` creates an `H2R2dbcOffsetStore` 
for `Dialect.H2`. It is based on upstream's `H2OffsetStoreDao`:
     - The offset and management upserts use `MERGE INTO ... 
KEY(projection_name, projection_key)` instead of `ON CONFLICT`.
     - `timestamp_consumed` uses `CURRENT_TIMESTAMP` instead of 
`transaction_timestamp()`, which H2 doesn't have.
     - The timestamp offset inserts (single, multi-row batch and `add()` batch) 
and the `= ANY (?)` delete run unchanged on H2.
   - **Table creation:** with H2, the projection creates the offset store 
tables of its own settings before the first connection is handed out 
(`H2OffsetStoreTables`). This uses `CREATE SCHEMA/TABLE IF NOT EXISTS` with the 
schema and table names from `offset-store`. The `projection_offset_store` table 
is skipped when `offset-table = ""`. Nothing is created when the connection is 
configured with an H2 `url`. A failed creation is retried on the next 
connection.
   - **Why auto-creation instead of #938's config include:**
     - Upstream creates the tables through the connection-level 
`additional-init` (#938 ships `h2-default-projection-schema.conf` to include 
there). pekko-persistence-r2dbc#517 deliberately didn't port that. Its H2 
connection has no `INIT`: each plugin creates its own tables on first use, and 
`additional-init` moved to `journal/snapshot/state.h2.additional-init`. Copying 
#938 would put the projection on a different model from the plugins it runs 
alongside.
     - The only place left for a fixed schema string would be one of the 
plugins' `h2.additional-init`. That runs only when that plugin starts. A 
projection over durable state alone, or one that starts before the journal 
plugin is used, would find no tables.
     - A fixed schema string hardcodes the default table names and schema. It 
would break `offset-store.schema`, custom table names, `offset-table = ""`, a 
projection-specific `use-connection-factory` and runtime projection configs. 
Deriving the DDL from `R2dbcProjectionSettings` handles all of these, the same 
way #517 handles `state.custom-table` and runtime plugin configs.
     - It also needs no user setup. With the `h2` dialect the projection works 
with no schema script and no config include, which is the point of an 
in-process database for tests and small applications.
     - The table-creating connection factory follows 
`H2Connections.creatingTables` from #517. It is copied rather than called 
because that helper is `private[r2dbc]` in `org.apache.pekko.persistence.r2dbc`.
   - **Executor:** projections now get their connection factory and 
`R2dbcExecutor` from 
`R2dbcProjectionImpl.connectionFactory`/`createR2dbcExecutor`, so the table 
creation and the dispatcher apply to every projection type. With H2, database 
calls run on the connection factory's `h2.use-dispatcher` (default 
`pekko.actor.default-blocking-io-dispatcher`) because the H2 driver blocks, the 
same as the persistence plugins. Within a projection, the offset store and the 
handler now share one executor instead of creating two over the same pool.
   - **`close-calls-exceeding`:** the r2dbc snapshot deprecates the 
`R2dbcExecutor` constructor without `closeCallsExceeding`, and the build treats 
warnings as errors. The projection's executor now reads `close-calls-exceeding` 
from its connection factory config, as the r2dbc plugins do. The default (20 s) 
is what the deprecated constructor hardcoded, so nothing changes unless a user 
configured a different value. The same applies to the test executors in 
`r2dbc-int-test` and `grpc-int-test`.
   - **Dependencies:** `io.r2dbc:r2dbc-h2:1.1.0.RELEASE` (Apache 2.0) and 
`com.h2database:h2:2.5.252` (EPL 1.0 or MPL 2.0, ASF Category B; the version 
pekko-persistence-r2dbc uses). Both are `test` only in `r2dbc-int-test`. The 
`r2dbc` module doesn't reference H2 classes, so users add the driver as 
described in the pekko-persistence-r2dbc docs.
   - **CI:** a new "Build and Test Integration for R2DBC with H2" job (Scala 
2.13 and Scala 3 next), with no database container or schema script.
   - **Docs:** an H2 section in `r2dbc.md` covering automatic table creation, 
the `url` and `offset-table = ""` cases, and the dispatcher. `reference.conf` 
now lists `h2` as a dialect.
   - **Tests:**
     - `TestConfig` gets an `h2` case.
     - `TestDbLifecycle` uses the projection's own connection factory and 
executor, so H2 creates the offset tables before the cleanup deletes. A 
persistence table the H2 plugins haven't created yet counts as empty.
     - `RuntimePluginConfigSpec` uses one in-memory database per plugin 
(`h2.database`), matching the separate databases of the other dialects.
     - `R2dbcProjectionSpec`'s test repository gets an H2 `MERGE` and keeps its 
table unquoted for H2.
   
   ### Result
   R2DBC projections can store offsets in an in-memory or file-based H2 
database, with the tables created automatically.
   
   ### Tests
   - `sbt -Dpekko.persistence.r2dbc.dialect=h2 
-Dpekko.projection.r2dbc.dialect=h2 r2dbc-int-test/test` (Windows 11, JDK 25): 
124 succeeded, 0 failed, 0 pending, 12 suites.
   - `sbt r2dbc-int-test/test` against PostgreSQL in docker: 124 succeeded, 0 
failed. This includes the new spec, which configures H2 itself and so runs in 
every dialect's job.
   - New `H2OffsetStoreTablesSpec`: dialect selection, tables created for a 
custom schema and table names, `MERGE` updates of offsets and management state, 
no offset table for `offset-table = ""`, no tables with an H2 `url`.
     - Without the table-creating connection factory, 3 of its 5 tests fail 
with table not found.
     - With the base `ON CONFLICT` offset store for H2, 4 of 5 fail with a 
syntax error.
   - `++3.3.8! r2dbc/compile r2dbc-int-test/Test/compile`: pass. 
`grpc-int-test/Test/compile`: pass on 2.13. On Scala 3 it fails in 
`ReplicationJavaDSLIntegrationSpec`, which this PR doesn't touch; CI only 
builds `grpc-int-test` with 2.13.
   - `sbt "checkCodeStyle; mimaReportBinaryIssues"` (as in CI), 
`+headerCheckAll`, `docs/paradox`: pass.
     - `sbt +mimaReportBinaryIssues` fails on the Scala 3 leg resolving 
`pekko-projection-slick_3:1.0.0`, which was never published. This is unrelated 
to this change.
   - scalafmt (native, diff against `main` plus the new files): formatted. `git 
diff --check`: clean.
   - MySQL and Yugabyte not run locally; relying on CI.
   
   ### Notes
   - Copied code:
     - `H2R2dbcOffsetStore` (the `MERGE` statements) is from akka-projection 
1.5.0 `H2OffsetStoreDao` and keeps its Lightbend copyright line.
     - The DDL in `H2OffsetStoreTables` is from #938's 
`h2-default-projection-schema.conf`. It matches `create_tables_postgres.sql` in 
this repo.
     - The table-creating connection factory in the same file is from 
pekko-persistence-r2dbc `H2Connections` (#517) and keeps that file's Lightbend 
copyright line.
     - `sbt headerCreate` added the ASF headers.
   - After the pekko-persistence-r2dbc release: bump `currentVersion` to the 
release, re-run CI, mark ready, and change the reference to `Fixes #662`.
   
   ### References
   Refs #662, akka/akka-projection#914, akka/akka-projection#938, 
apache/pekko-persistence-r2dbc#517
   


-- 
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