Skip to content

r2dbc: support MariaDB - #671

Draft
ptrdom wants to merge 4 commits into
apache:mainfrom
ptrdom:r2dbc-mariadb-support
Draft

ptrdom wants to merge 4 commits into
apache:mainfrom
ptrdom:r2dbc-mariadb-support

Conversation

@ptrdom

@ptrdom ptrdom commented Oct 31, 2026

Copy link
Copy Markdown
Member

Motivation

apache/pekko-persistence-r2dbc#534 added the mariadb dialect to the persistence plugin. R2DBC projections can't use it yet:

  • R2dbcOffsetStore.fromConfig has no offset store for Dialect.MariaDB.
  • The MySQL offset store upserts with the VALUES (...) AS excluded row alias, which MariaDB doesn't support ([1064] ... near 'AS excluded ON DUPLICATE KEY UPDATE ...').

Draft until pekko-persistence-r2dbc is released. This builds against 2.0.0-M1+120-5a212bab-SNAPSHOT, which includes apache/pekko-persistence-r2dbc#534 (and the r2dbc-mariadb 1.3.2 bump in apache/pekko-persistence-r2dbc#538). It is stacked on #668, which needs the same snapshot handling (the close-calls-exceeding executor change) and adds Dialect.H2 to the same matches. Until #668 is merged, the diff here includes its commits; the MariaDB change is the last commit.

Modification

  • Dialect: R2dbcOffsetStore.fromConfig creates a MariaDBR2dbcOffsetStore for Dialect.MariaDB. It extends MySQLR2dbcOffsetStore and only overrides the offset and management upserts. These use ON DUPLICATE KEY UPDATE col = VALUES(col), the same approach as MariaDBSnapshotDao in core: support MariaDB pekko-persistence-r2dbc#534. Everything else is the MySQL store:
    • the NOW(6) consumed timestamp,
    • binding each batch row before add(),
    • the CONCAT(...) NOT IN (...) delete.
  • Timestamps: the MariaDB driver doesn't support java.time.Instant. Nothing in the projection handles that: ConnectionFactoryProvider in the plugin wraps the MariaDB connection pool so that Instant is bound and read as UTC LocalDateTime, with a UTC session. The projection gets its connections from that provider, so the offset store and the statements of an R2dbcSession are converted the same way.
  • Schema: MariaDB uses the MySQL DDL (create_tables_mysql.sql), like the persistence plugin.
  • Dependencies: org.mariadb:r2dbc-mariadb:1.3.2 (Apache 2.0), the version pekko-persistence-r2dbc is built against, test only in r2dbc-int-test. The r2dbc module doesn't reference driver classes.
  • CI: a new "Build and Test Integration for R2DBC with MariaDB" job (Scala 2.13 and Scala 3 next). It uses docker-files/docker-compose-mariadb.yml (MariaDB 12.3.3, same as the plugin), with the server in America/New_York so that a non-UTC session would show up as wrong timestamps.
  • Docs: a MariaDB entry in the schema list and a MariaDB section in r2dbc.md that links to the plugin's MariaDB docs. reference.conf lists mariadb as a dialect.
  • Tests:
    • TestConfig gets a mariadb case.
    • R2dbcProjectionSpec's test repository gets a VALUES() upsert for MariaDB.
    • R2dbcTimestampOffsetProjectionSpec's spy offset store gets a MariaDB case.
    • New MariaDBR2dbcOffsetStoreSpec.

Result

R2DBC projections can store offsets in MariaDB 10.11 or later with the mariadb dialect.

Tests

  • New MariaDBR2dbcOffsetStoreSpec (MariaDB 12.3.3 in America/New_York, Windows 11, JDK 25): 3 passed.
    • Dialect selection and the upsert SQL. This test makes no database calls, so it runs in every dialect's job.
    • Offset and management state updates.
    • Timestamp offsets at two instants whose UTC local times are ambiguous (2026-11-01T01:30Z) or don't exist (2027-03-14T02:30Z) in the server zone. The test checks the stored instant with UNIX_TIMESTAMP, which doesn't depend on the session time zone, and then reads the offset back.
      • With a New York session, MariaDB stores 2026-11-01 01:30 as 06:30Z but still reads it back as 01:30, so only UNIX_TIMESTAMP catches it. It rejects 2027-03-14 02:30 (ERROR 1292). Both checked with the mariadb CLI against the test image.
      • Not run through the plugin with a non-UTC session: the plugin rejects a non-UTC timezone option, and sessionVariables=time_zone=SYSTEM in the URL left the session in UTC.
    • The other two tests are canceled with other dialects.
    • With the MySQL upserts for MariaDB, 2 of 3 fail: the SQL check, and the update test with R2dbcBadGrammarException ... near 'AS excluded ...'.
  • sbt r2dbc-int-test/test against PostgreSQL: 137 succeeded, 0 failed, 2 canceled.
  • sbt -Dpekko.persistence.r2dbc.dialect=h2 -Dpekko.projection.r2dbc.dialect=h2 r2dbc-int-test/test: 137 succeeded, 0 failed, 2 canceled.
  • sbt -Dpekko.persistence.r2dbc.dialect=mariadb -Dpekko.projection.r2dbc.dialect=mariadb r2dbc-int-test/test (MariaDB 12.3.3), 3 runs: 137 to 138 of 139 succeeded.
  • sbt -Dpekko.persistence.r2dbc.dialect=mysql -Dpekko.projection.r2dbc.dialect=mysql r2dbc-int-test/test (MySQL 9.5.0): 136 succeeded, 1 failed (EventSourcedPubSubSpec, see above), 2 canceled.
  • ++3.3.8! and ++3.9.0! r2dbc/compile r2dbc-int-test/Test/compile: pass.
  • sbt "checkCodeStyle; mimaReportBinaryIssues", headerCheckAll, Test/headerCheckAll, docs/paradox: pass. sbt headerCreateAll added the ASF headers.
  • scalafmt (native, diff against r2dbc: support H2 database (port akka-projection #914, #938) #668's branch plus the new files): formatted. git diff --check: clean.
  • Yugabyte not run locally; relying on CI.

Notes

References

Refs apache/pekko-persistence-r2dbc#534, Refs #668

Motivation:
Akka Projection 1.5.0 added H2 support to the R2DBC projections
(akka/akka-projection#914, akka/akka-projection#938), and
apache/pekko-persistence-r2dbc#517 added the h2 dialect to the
persistence plugin.

Modification:
Add H2R2dbcOffsetStore with MERGE upserts and CURRENT_TIMESTAMP. With
the h2 dialect the projection creates the offset store tables of its own
settings before the first connection, like the persistence plugins,
instead of upstream's connection-level additional-init schema. Database
calls run on the connection factory's h2.use-dispatcher. The executor
reads close-calls-exceeding from the connection factory config, since
the snapshot deprecates the constructor without it. Build against the
pekko-persistence-r2dbc snapshot that includes
apache/pekko-persistence-r2dbc#517, add test-only H2 dependencies, an H2
CI job and docs.

Result:
R2DBC projections can store offsets in an H2 database, with the tables
created automatically.

Tests:
- r2dbc-int-test/test with h2 dialect: 124 succeeded, 0 failed
- r2dbc-int-test/test with PostgreSQL: 124 succeeded, 0 failed
- checkCodeStyle; mimaReportBinaryIssues, +headerCheckAll, docs/paradox: pass

References:
Refs apache#662
Motivation:
main gained R2dbcSession.withSession (apache#664), which creates an
R2dbcExecutor with the constructor that the pekko-persistence-r2dbc
snapshot deprecates, so the build fails with warnings as errors. The
spy offset store from apache#620 has no H2 case.

Modification:
R2dbcSession.withSession creates its executor with
R2dbcProjectionImpl.createR2dbcExecutor, which takes the logger to
keep its own logger name. That also runs H2 sessions on h2.use-dispatcher
and reads close-calls-exceeding. Add the H2 case to the spy offset
store in R2dbcTimestampOffsetProjectionSpec.

Result:
The branch compiles and passes with the changes from main.

Tests:
- r2dbc-int-test/test with h2 dialect: 136 succeeded, 0 failed
- r2dbc-int-test/test with PostgreSQL: 136 succeeded, 0 failed
- Test/compile, checkCodeStyle; mimaReportBinaryIssues, +headerCheckAll,
  docs/paradox, ++3.3.8 r2dbc-int-test/Test/compile: pass

References:
Refs apache#662
Motivation:
apache/pekko-persistence-r2dbc#534 added the mariadb dialect to the
persistence plugin. R2DBC projections fail on it: the dialect has no
offset store, and MariaDB doesn't support the `AS excluded` row alias
that the MySQL offset store upserts with.

Modification:
Add MariaDBR2dbcOffsetStore, the MySQL offset store with VALUES(col)
upserts, and select it for the mariadb dialect. Instants need no
handling in the projection, as the connection factory of the plugin
converts them for the MariaDB driver. Build against the
pekko-persistence-r2dbc snapshot that includes
apache/pekko-persistence-r2dbc#534, add the MariaDB driver as a test
dependency, a MariaDB CI job and docs.

Result:
R2DBC projections can store offsets in MariaDB with the mariadb dialect.

Tests:
- MariaDBR2dbcOffsetStoreSpec on MariaDB 12.3.3 (America/New_York): 3 passed; 2 of 3 fail with the MySQL upserts
- r2dbc-int-test/test with PostgreSQL and H2: 137 passed, 2 canceled (MariaDB only)
- r2dbc-int-test/test with MariaDB and MySQL: event sourced specs fail locally under load on Windows, also on MySQL and the H2 branch
- checkCodeStyle, mimaReportBinaryIssues, headerCheckAll, docs/paradox, Scala 3.3.8 and 3.9.0 compile: pass

References:
Refs apache/pekko-persistence-r2dbc#534
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.

1 participant