Skip to content

Add an Oracle Database 23ai store#102

Merged
jcputney merged 22 commits into
mainfrom
oracle-store
Jun 25, 2026
Merged

Add an Oracle Database 23ai store#102
jcputney merged 22 commits into
mainfrom
oracle-store

Conversation

@jcputney

@jcputney jcputney commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

What this adds

An Oracle Database 23ai store (ratchet-store-oracle), built against the same store TCK as the MySQL, PostgreSQL, and MongoDB stores, plus the migrator, matrix, and docs work to go with it.

  • Store. UUIDs as RAW(16), JSON columns as CLOB with JSON_VALUE virtual columns, enum-like columns as VARCHAR2 + CHECK, flags as the native 23ai BOOLEAN. The claim path is two-phase (an unlocked candidate select, then FOR UPDATE SKIP LOCKED over those ids) because Oracle rejects FETCH FIRST combined with FOR UPDATE SKIP LOCKED.
  • Schema migrator. Oracle joins the auto-migrate whitelist. Concurrent migrators serialize through an EXCLUSIVE lock on a dedicated ratchet_schema_lock table held on a second connection — Oracle has no grant-free session lock, and its DDL auto-commits, so a lock on the migration connection would not survive the first CREATE TABLE. That design was peer-reviewed before implementation. It needs a pool that can hand out two connections.
  • Driver. ojdbc11 stays test-scoped. Its license is Oracle's Free Use Terms and Conditions, not OSI-approved, so Ratchet never bundles it and deployers supply their own. The parent POM flags this.
  • Docs. An Oracle deployment page, an entry in the database-setup guide and the migrator dialect table, and a sidebar link.

What's verified

  • mvn verify -pl stores/ratchet-store-oracle -am is green on a real gvenzl/oracle-free:slim-faststart (Oracle 23ai) container: 380 store + schema-conformance tests, the restored OracleSchemaMigratorIT (3/3, including the parallel-migrator convergence test that exercises the lock), and OracleExplainPlanCaptureIT (the captured plan rides INDEX RANGE SCAN | IDX_CLAIM_EXECUTABLE, no full scan).
  • MySQL, PostgreSQL, and MongoDB store modules still pass with the shared store-core and TCK changes (Clob-aware RowValues, the cron NULL→"" coercion, and the two DialectTypeMapper hooks all keep their existing behaviour for those stores).

Server matrix

Both cells run the full integration suite against Oracle and pass, so the matrix is wired into CI: one Hibernate cell (wildfly-managed) and one EclipseLink cell (openliberty-managed), both 204/204.

Getting there took five fixes, all of which surfaced only once the store ran inside a managed server under concurrent load:

  • CLOB reads. Several Oracle native-query mappers (workflow condition expression, batch progress hook, signal payload and rejection reason, the hot-row last_error comparison) read CLOB columns with a String cast or toString(). Under a pooled connection the driver hands those back as a java.sql.Clob locator instead of a String, so the cast threw or toString() leaked an oracle.sql.CLOB@... handle into JSON-B. They now go through the Clob-aware RowValues.stringOrNull.
  • Isolation self-check. The Oracle store had inherited MySQL's @@SESSION.transaction_isolation probe, which Oracle rejects with ORA-00936. The failure marked the startup transaction rollback-only and sank the @Recurring master insert. Oracle has no privilege-free way to read the session isolation level and no REPEATABLE READ level to guard against, so the check now skips on Oracle.
  • Resource permits. The permit acquire counted live permits in a separate statement after locking the limit row. Under EclipseLink that read can still see the pre-lock snapshot, so concurrent acquirers over-admitted past the limit. The capacity check now rides inside a guarded INSERT ... SELECT, the same shape the PostgreSQL store already uses.
  • Test cleanup. The JPA cleanup truncated tables between tests while the poller was live; on Oracle that fails on foreign-key-referenced tables (ORA-02266) and invalidates in-flight queries (ORA-08103). Oracle now clears with DELETE, like PostgreSQL.
  • Test data binding. The data manipulator backdated rows with native UPDATEs that bound a bare UUID; Oracle's RAW(16) job_id needs the 16 bytes, which Hibernate coerced for free but EclipseLink did not.

Follow-ups

  • The remaining server×Oracle combinations (wildfly-ee11-managed, payara-managed, glassfish-managed) are wired but not yet exercised in CI.

https://claude.ai/code/session_01Q3y1VcGjFU9Q6ceSoKUsfX

RowValues reads a Clob column by pulling its substring instead of assuming a String, so a store can return a java.sql.Clob without breaking hydration. AbstractJobRowMapper coerces a null cron expression back to the empty-string sentinel the engine expects, since Oracle collapses '' to NULL and cannot keep that column NOT NULL. Both keep their existing behaviour for MySQL and PostgreSQL.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Qodana for JVM

1 new problem were found

Inspection name Severity Problems
Busy wait 🔶 Warning 1

☁️ View the detailed Qodana report

Contact Qodana team

Contact us at qodana-support@jetbrains.com

jcputney added 13 commits June 23, 2026 23:37
DialectTypeMapper gains two hooks: one to fold identifiers to the case the catalog stores, one to supply the delete-rule lookup query. The conformance contract routes table names and the foreign-key check through them. Oracle has no information_schema and upper-cases unquoted identifiers; the defaults still match MySQL and PostgreSQL.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Adds Oracle to the migrator whitelist and dialect map, a MERGE-based version-ledger upsert, and a CREATE TABLE IF NOT EXISTS ledger. Oracle has no grant-free session lock and its DDL auto-commits, so concurrent migrators serialise through an EXCLUSIVE lock on a dedicated ratchet_schema_lock table held on a second connection, which means the pool needs room for two. The checksum and version-gap guards are unchanged.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
A full Oracle 23ai store built against the same store TCK as the others. UUIDs are RAW(16), JSON columns are CLOB with JSON_VALUE virtual columns, enum-like columns are VARCHAR2 with CHECK constraints, and flags use the native BOOLEAN. The claim path is two-phase because Oracle rejects FETCH FIRST combined with FOR UPDATE SKIP LOCKED. The JDBC driver stays test-scoped: its license is not OSI-approved, so deployers supply their own ojdbc at runtime.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Adds an oracle profile to the testsuite and coverage builds, the Oracle container and datasource plumbing for the managed servers, and the WildFly driver module (which needs java.security.jgss because ojdbc reaches for GSS classes). Forces inline LOB prefetch on the server JVMs so CLOB reads through a pooled connection return content rather than a stale locator.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Adds an Oracle deployment page, lists Oracle in the database-setup guide and the migrator dialect table, and adds it to the sidebar next to the other SQL stores. Notes the LOB prefetch setting that keeps CLOB reads reliable through a connection pool.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Several native-query mappers in the Oracle store pulled CLOB-backed
columns off the result row with a String cast or toString(). That holds
only when the driver inlines the LOB through prefetch and returns a
String. Under the pooled, concurrent poller load of the server matrix the
driver hands back a java.sql.Clob locator instead, so the cast throws and
toString() yields "oracle.sql.CLOB@...", which then reaches JSON-B as a
value starting with 'o' and fails to parse.

Route the workflow condition expression, the batch progress hook, the
signal payload and rejection reason, and the hot-row last_error
comparison through RowValues.stringOrNull. It reads the locator's content
when the driver returns one and passes a plain String through untouched
otherwise, so MySQL and PostgreSQL, which already return these columns as
String, are unaffected.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The Oracle store inherited MySQL's isolation self-check verbatim,
including the @@SESSION.transaction_isolation query. Oracle rejects that
syntax with ORA-00936, and the failure marks the surrounding JTA
transaction rollback-only. When the first store write after startup
shares that transaction, as the @Recurring master INSERT does, it then
fails with STATUS_MARKED_ROLLBACK and the recurring job never registers.

Oracle has no privilege-free way to read the session isolation level
outside a transaction, and it offers no REPEATABLE READ level to guard
against in the first place. Pass no probe query so the shared check
degrades to a safe skip. The datasource and hibernate.connection.isolation
still pin READ COMMITTED.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The JPA cleanup strategy truncated tables between tests while the poller
kept running. On Oracle that races badly. TRUNCATE fails on any table an
enabled foreign key references (ORA-02266), and it resets a table's
data-object number, so an in-flight poller query against that table dies
with ORA-08103. Leftover state then bled into later tests.

Send Oracle down the same row-level DELETE path PostgreSQL already uses.
DELETE plays well with MVCC, honors the existing child-before-parent
table order, and tolerates the live poller. MySQL keeps using TRUNCATE
behind its foreign-key-check toggle.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The JPA test data manipulator backdates rows with native UPDATEs keyed on
job_id. It pre-converted the UUID to bytes only for MySQL and passed a bare
UUID otherwise. PostgreSQL's native uuid column accepts that, but Oracle's
job_id is RAW(16): Hibernate coerces a UUID parameter into those bytes for a
native query, EclipseLink binds it verbatim, and the predicate then matches
nothing. The backdating UPDATE silently affected zero rows under EclipseLink,
so the archiving and DLQ-purge tests saw no stale rows to act on.

Convert the UUID to its 16 big-endian bytes for Oracle as well, leaving
PostgreSQL on the bare UUID.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The Oracle permit acquire locked the resource-limit row, then read the active
permit count in a separate statement before inserting. Under EclipseLink that
second read can still see the pre-lock statement snapshot even after the
FOR UPDATE wait, so concurrent acquirers all read a zero count and over-admit
past the configured limit. The same code serialized fine on Hibernate.

Move the capacity check inside the insert: lock the limit row, then run a
guarded INSERT ... SELECT whose WHERE clause re-counts the live permits in the
same statement that writes the new row. The count is evaluated against fresh
committed data on the write path, so the limit holds on both Hibernate and
EclipseLink. This mirrors what the PostgreSQL store already does for the same
reason.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Add the two validated Oracle cells to the integration matrix: WildFly for
the Hibernate path and OpenLiberty for EclipseLink. Both run the full suite
against a gvenzl Oracle 23ai container, and ojdbc stays test-scoped. The
other server profiles can run against Oracle too but are not wired here yet.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Oracle columns are TIMESTAMP(6), so a persisted Instant is floored to
microsecond precision. Oracle's JDBC driver binds an Instant or Timestamp
parameter at full nanosecond precision and compares it literally, so a cutoff
taken from the same Instant as a stored row misses an exclusive (<) or
inclusive-lower (>=) boundary by the sub-microsecond remainder. The MySQL and
PostgreSQL drivers floor the bind to the column scale, so they pass the shared
contracts unchanged; the gap only shows on a nanosecond-resolution clock
(Linux), which hid it on macOS and in the store unit tests.

Add OracleTimestamps.floorMicros/microTimestamp and route every < and >=
comparison cutoff through it: DLQ alert dedup, log purge, DLQ job purge,
archive find/count/purge, inactive-node find/delete, orphan-job reset,
recurring annotation cancellation, the since/threshold metric windows, and the
job-query range filters. The <= and > sites already handle the boundary
correctly and are left unchanged.

On Linux the wildfly+oracle integration matrix surfaced two of these as
failures (DlqAlert exact-cutoff in the store suite, DlqPurgeIT exclusive-cutoff
in the testsuite); the rest share the same mechanism and are fixed pre-emptively.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The Oracle store hits the same two Qodana false positives the MySQL and
PostgreSQL stores already suppress, but its paths were never added to the
exclusion blocks.

Every store method reads the container-managed EntityManager through
context.em(), which Qodana flags as an AutoCloseable that should be closed
(154 hits). Closing an injected @PersistenceContext EntityManager would be
wrong, so the warning is a false positive. OracleJobStoreImpl's CDI no-arg
constructor and lazy em init produce the same three nullability warnings
already excluded on the MySQL and PostgreSQL impls.

Add the Oracle paths to the AutoCloseableResource and DataFlowIssue blocks.
A local scan with the community linter confirms the 158 Oracle findings drop
to zero.

Signed-off-by: Jonathan Putney <jonathan@putney.io>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class Oracle Database 23ai support to Ratchet by introducing a new Oracle-backed store module and wiring it into the existing schema migrator, store TCK, integration tests, docs, and CI matrix.

Changes:

  • Introduces ratchet-store-oracle (Oracle 23ai store implementation + schema resources + TCK/IT coverage).
  • Extends store-core + TCK to support Oracle-specific catalog behavior and CLOB/UUID/timestamp edge cases.
  • Wires Oracle into docs, testsuite infrastructure (WildFly/OpenLiberty), and CI server×database matrix.

Reviewed changes

Copilot reviewed 106 out of 106 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
website/docs/getting-started/introduction.md Mentions Oracle as a supported pluggable store.
website/docs/deployment/oracle.md New Oracle 23ai deployment/configuration guide.
website/docs/deployment/database-setup.md Adds Oracle setup guidance + migrator dialect table entry.
website/docs/concepts/persistence.md Updates SPI docs to include Oracle in shipped stores list.
website/docs/advanced/spi-implementation.md Updates SPI implementation docs to include Oracle store.
website/docs/.vitepress/config.ts Adds Oracle deployment page to docs sidebar.
testing/ratchet-testsuite/src/test/resources/wildfly-setup.cli Registers Oracle JDBC driver in WildFly test setup.
testing/ratchet-testsuite/src/test/resources/arquillian.xml Adds Oracle LOB prefetch JVM arg to managed-server test runs.
testing/ratchet-testsuite/src/test/java/run/ratchet/testsuite/util/RatchetArchiveBuilder.java Extends testsuite deployment builder to support Oracle persistence mappings/properties.
testing/ratchet-testsuite/src/test/java/run/ratchet/testsuite/util/DataSourceResourcesTest.java Adds Oracle datasource/driver coordinate assertions; updates rejection test.
testing/ratchet-testsuite/src/test/java/run/ratchet/testsuite/util/DataSourceResources.java Adds Oracle datasource class name and driver coordinates.
testing/ratchet-testsuite/src/test/java/run/ratchet/testsuite/infra/JdbcContainerExtension.java Adds Oracle Testcontainers support + db-type wiring.
testing/ratchet-testsuite/src/main/java/run/ratchet/testsuite/app/JpaTestDataManipulator.java Adjusts UUID native-binding behavior to handle RAW(16) (Oracle) like BINARY(16) (MySQL).
testing/ratchet-testsuite/src/main/java/run/ratchet/testsuite/app/JpaTestCleanupStrategy.java Switches Oracle cleanup to DELETE (like PostgreSQL) to avoid TRUNCATE issues.
testing/ratchet-testsuite/pom.xml Adds Testcontainers Oracle Free dependency + copies ojdbc into WildFly module + adds oracle profile.
testing/ratchet-tck/store/src/main/java/run/ratchet/tck/store/schema/DialectTypeMapper.java Adds metadata identifier normalization, nullability relaxation hook, and dialect-specific delete rule query hook.
testing/ratchet-tck/store/src/main/java/run/ratchet/tck/store/AbstractSchemaConformanceContract.java Uses new dialect hooks for Oracle (identifier casing, FK delete rule introspection, nullable relaxations).
testing/ratchet-coverage/pom.xml Includes Oracle store module in coverage aggregation.
stores/ratchet-store-oracle/src/test/resources/META-INF/persistence.xml Test persistence unit for Oracle store tests.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleWorkflowConditionStoreContractTest.java Oracle TCK contract binding for workflow conditions.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleTransientExceptionTranslationTest.java Tests Oracle transient exception translation behavior.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleTestFixture.java Shared Oracle Testcontainers + JPA fixture for Oracle store TCK.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleTagStoreContractTest.java Oracle TCK contract binding for tags.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleSignalStoreContractTest.java Oracle TCK contract binding for signals.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleSchemaMigratorIT.java Oracle schema migrator integration tests (including parallel migrator locking).
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleSchemaConformanceContractTest.java Oracle schema conformance contract test (catalog/type/DDL expectations).
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleResourcePermitStoreContractTest.java Oracle TCK contract binding for resource permits.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleRecurringJobStoreContractTest.java Oracle TCK contract binding for recurring jobs.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleRecurringClaimConcurrencyContractTest.java Oracle recurring claim concurrency contract test.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OraclePayloadEncryptionContractTest.java Oracle payload encryption contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleNodeStoreContractTest.java Oracle node store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleNodeLockOperationsTest.java Unit tests for Oracle node lock operations SQL behavior.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleLockStoreContractTest.java Oracle lock store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobTerminalStoreContractTest.java Oracle terminal lifecycle contract + extra CAS invariants.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobStoreImplTransactionTest.java Ensures transaction boundary annotations are correct for managed servers.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobRowMapperTest.java Tests Oracle row mapper error-context behavior.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobRetryStoreContractTest.java Oracle retry store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobQueryStoreContractTest.java Oracle query store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobPauseStoreContractTest.java Oracle pause store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobDeleteOperationsTest.java Unit test ensuring orphan reset query handles NULL picked_by correctly.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobCrudStoreContractTest.java Oracle CRUD store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobCountOperationsTest.java Unit tests for Oracle analytics/counting operations.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobClaimStoreContractTest.java Oracle claim store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobClaimOperationsTest.java Verifies claim select column ordering/indexing contract.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobBulkStoreContractTest.java Oracle bulk store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobBatchStatusStoreContractTest.java Oracle batch status store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobAuditStoreContractTest.java Oracle audit store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleJobAnalyticsStoreContractTest.java Oracle analytics store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleExplainPlanCaptureIT.java Captures/asserts Oracle claim candidate select uses the covering index.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleDualWriteInvariantContractTest.java Oracle dual-write (hot/cold split) invariant contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleDlqAlertStoreContractTest.java Oracle DLQ alert store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleDialectMapper.java Oracle dialect mapper for schema conformance introspection.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleConstraintDetectorTest.java Unit tests for Oracle constraint/transient detection.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleBusinessKeyReservationsTest.java Unit tests for Oracle business-key reservation SQL/binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleBatchStoreContractTest.java Oracle batch store contract + concurrency regression coverage.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleAuxiliaryOperationsTest.java Unit tests for permit acquisition idempotency/defaults.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleArchiveStoreContractTest.java Oracle archive store contract binding.
stores/ratchet-store-oracle/src/test/java/run/ratchet/store/oracle/OracleActiveBusinessKeyContractTest.java Oracle active business key contract binding.
stores/ratchet-store-oracle/src/main/resources/META-INF/orm-oracle.xml EclipseLink/non-Hibernate UUID RAW(16) mapping + reserved-word quoting mapping.
stores/ratchet-store-oracle/src/main/resources/META-INF/beans.xml CDI bean discovery descriptor for Oracle store module.
stores/ratchet-store-oracle/src/main/resources/ddl/views/vw_jobs.sql Operator views script (currently MySQL-specific; needs Oracle equivalent).
stores/ratchet-store-oracle/src/main/resources/ddl/README.md DDL readme (currently MySQL-specific; should describe Oracle schema/migrations).
stores/ratchet-store-oracle/src/main/resources/ddl/oracle-debug-indexes.sql Optional debug indexes script (header comments currently MySQL-specific).
stores/ratchet-store-oracle/src/main/resources/ddl/EXPLAIN-PLANS.md EXPLAIN plan notes (currently MySQL-specific; should be Oracle-specific or removed).
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleTimestamps.java Oracle-specific timestamp binding helpers (microsecond flooring).
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleStoreContext.java Oracle store context wiring for metrics/dialect label + constraint detection.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleSignalOperations.java Oracle signal store implementation (WAITING job timeout selection + delivery updates).
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobStore.java Public Oracle store interface advertising capability closure.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobStatusTransitions.java Oracle job status transition operations.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobRowMapper.java Oracle job row mapper + payload/params encryption helpers.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobRecurringAndResetOperations.java Oracle reset + cancel-by-tag operations.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobReadOperations.java Oracle job read/find operations using native SQL hydration.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobLifecycleOperations.java Oracle implementation wiring for pause/retry/terminal/batch status stores.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobDeleteOperations.java Oracle delete + DLQ purge + orphan reset operations.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleJobCrudOperations.java Oracle CRUD/bulk/analytics facade delegating to operation classes.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleEntityManagerProvider.java Default CDI EntityManager provider for Oracle store module.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleConstraintDetector.java Oracle constraint-violation + transient error detector.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleBusinessKeyReservations.java Oracle business-key reservation DML + batch deletion helpers.
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/OracleArchiveOperations.java Oracle archive store operations (batch archive + search + purge).
stores/ratchet-store-oracle/src/main/java/run/ratchet/store/oracle/converter/UuidRawConverter.java UUID <-> RAW(16) attribute converter for non-Hibernate JPA providers.
stores/ratchet-store-oracle/src/main/java/module-info.java Java module descriptor for Oracle store module.
stores/ratchet-store-oracle/README.md Store README (currently MySQL-specific; should document Oracle store).
stores/ratchet-store-oracle/pom.xml New Maven module definition for ratchet-store-oracle.
stores/ratchet-store-core/src/test/java/run/ratchet/store/migration/SchemaMigratorTest.java Extends dialect auto-detection tests to include Oracle.
stores/ratchet-store-core/src/main/java/run/ratchet/store/util/RowValues.java Adds CLOB-aware string extraction for Oracle native queries.
stores/ratchet-store-core/src/main/java/run/ratchet/store/migration/SchemaMigrationLifecycleHook.java Updates lifecycle hook docs to include Oracle and its 2-connection lock requirement.
stores/ratchet-store-core/src/main/java/run/ratchet/store/context/AbstractJobRowMapper.java Adds cron_expr NULL→"" normalization for Oracle empty-string semantics.
stores/ratchet-store-core/src/main/java/module-info.java Exports store context package to Oracle module.
ratchet-bom/pom.xml Adds Oracle store to BOM.
qodana.yaml Excludes Oracle store paths from specific inspections (consistent with other stores).
pom.xml Adds Oracle store module + ojdbc version and dependency management entry.
.github/workflows/ci.yml Adds Oracle runs to the server×database integration matrix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread stores/ratchet-store-oracle/src/main/resources/ddl/README.md Outdated
Comment thread stores/ratchet-store-oracle/src/main/resources/ddl/oracle-debug-indexes.sql Outdated
Comment thread stores/ratchet-store-oracle/src/main/resources/ddl/EXPLAIN-PLANS.md Outdated
Comment thread stores/ratchet-store-oracle/src/main/resources/ddl/views/vw_jobs.sql Outdated
Comment thread stores/ratchet-store-oracle/README.md Outdated
The Oracle store module was seeded from the MySQL store, and several
MySQL references were never converted. The docs pointed at files that do
not exist in this module (mysql-schema.sql, orm-mysql.xml), the operator
views called BIN_TO_UUID(), which Oracle has no equivalent for, and two
comments described an "open InnoDB transaction" that Oracle never has.

Three copy-paste bugs rode along with the docs:

- RowValues.clobToString cast the CLOB length (a long) to int before
  getSubString, which truncates any CLOB over ~2 GB. It now streams
  through the character reader with no cast.
- Two "<= ?" cutoff comparisons against TIMESTAMP(6) columns bound a raw
  Timestamp.from(now) instead of flooring to microseconds via
  OracleTimestamps.microTimestamp. Oracle's driver compares at nanosecond
  precision, so the boundary was missed on nanosecond-resolution clocks.

The operator views now render RAW(16) with RAWTOHEX plus REGEXP_REPLACE,
and the docs describe the Oracle schema, RAW(16) UUID storage, the
two-connection migrator lock, and orm-oracle.xml wiring.

Signed-off-by: Jonathan Putney <jonathan@putney.io>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 106 out of 106 changed files in this pull request and generated 1 comment.

Comment thread testing/ratchet-testsuite/pom.xml Outdated
jcputney added 7 commits June 24, 2026 10:07
testcontainers-oracle-free is already declared as a base test dependency
(ungated, so JdbcContainerExtension compiles under every db profile), and
the base comment says the oracle profile only needs to add the store and
the JDBC driver. The profile's own copy was redundant.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The Oracle store shipped running only two CI cells (WildFly for Hibernate,
Open Liberty for EclipseLink) and was absent from most of the documentation
that lists the supported stores. This brings it level with the MySQL,
PostgreSQL, and MongoDB stores.

CI and conformance:
- Oracle now runs the full server matrix (WildFly, WildFly EE 11, Payara,
  Open Liberty, GlassFish) against the gvenzl Oracle 23ai container, with
  ojdbc kept test-scoped.
- The docs.yml conformance generator and the VitePress sidebar now include
  Oracle, and the conformance index matrices gain an Oracle column.
- Adds the Oracle conformance placeholder pages; the next CI run on main
  fills them with real results.

Docs:
- Adds Oracle to the supported-store lists across the README and the website
  (getting-started, deployment, concepts, comparison, troubleshooting,
  use-cases, SPI reference, homepage trust strip) and CONTRIBUTING.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
GlassFish and Payara instantiate the jdbc-connection-pool datasource-classname
directly, and DataSourceResources handed them oracle.jdbc.datasource.OracleDataSource,
which is an interface. The WAR never deployed -- it failed with "Error
instantiating class oracle.jdbc.datasource.OracleDataSource" -- so every
Payara/Oracle and GlassFish/Oracle test errored before it ran.

Switch to the concrete oracle.jdbc.pool.OracleDataSource, the class Oracle's own
GlassFish administration docs specify for an Oracle connection pool (restype
javax.sql.DataSource). MySQL and PostgreSQL already used concrete classes. WildFly
does not use this value (driver module plus auto-detected datasource), and
OpenLiberty tolerated the interface through driver auto-detection; the concrete
class is correct there too.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
Signed-off-by: Jonathan Putney <jonathan@putney.io>
The Oracle store module was added on this branch while the repo was at
0.1.1-SNAPSHOT. main has since bumped to 0.1.2-SNAPSHOT, but the bump did
not touch the not-yet-merged Oracle pom, so after merging main every module
was 0.1.2-SNAPSHOT except ratchet-store-oracle. ratchet-coverage (and the
license gate) then could not resolve ratchet-store-oracle:0.1.2-SNAPSHOT and
the build failed before any test ran.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
The shorter oracle.jdbc.pool.OracleDataSource literal fits on one line with
its assertEquals argument, so google-java-format collapses it; apply that.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
With the datasource fix the Oracle WAR now deploys on Payara and GlassFish
and 195-205 tests pass per cell, but ojdbc11 is a much heavier driver than
the mysql/pg ones and one deploy OOMs the default 512m domain heap with
'Java heap space' (GlassFish), leaving a stuck app that cascades into
'application name already in use' on the following Payara deploys. Raise the
domain heap to 2048m via the existing domain.xml antrun edit; the 16g runner
has room alongside the gvenzl Oracle container.

Signed-off-by: Jonathan Putney <jonathan@putney.io>
@jcputney jcputney added this pull request to the merge queue Jun 25, 2026
Merged via the queue into main with commit 37da6d1 Jun 25, 2026
38 checks passed
@jcputney jcputney deleted the oracle-store branch June 25, 2026 02:11
github-merge-queue Bot pushed a commit that referenced this pull request Jun 26, 2026
Adds `ratchet-store-sqlserver`, a Microsoft SQL Server implementation of
the store SPI, alongside the existing MySQL/PostgreSQL/Oracle/MongoDB
stores. Branched off the SQL Server port and rebased onto `main` after
the Oracle store (#102) and the `0.1.2-SNAPSHOT` bump landed.

## What's here

- New `stores/ratchet-store-sqlserver` module: full store + capability
operations, `SqlserverConstraintDetector`, `SqlserverJobRowMapper`,
T-SQL dialect translations (two-phase claim, `MERGE` upserts,
`sp_getapplock` migration lock), and DDL (`V001` + consolidated schema +
debug indexes).
- Taught `SchemaMigrator` the `sqlserver` dialect (product-name
detection, lock acquire/release, version-table DDL, `MERGE`
record-version).
- Wired into the integration matrix (all 5 servers × `sqlserver`,
including GlassFish), TCK schema conformance, and the testsuite
Arquillian harness.

## Two SQL Server / GlassFish-specific fixes worth calling out

- **UUIDs stored as `BINARY(16)` (canonical big-endian), not
`UNIQUEIDENTIFIER`.** EclipseLink 5.0 on GlassFish 8 reads
`UNIQUEIDENTIFIER` via `getBytes`, returning `.NET-Guid` mixed-endian
storage bytes that no JPA-layer intercept can redirect. Raw `BINARY(16)`
is provider-independent, so EclipseLink 4/5/2.7 and Hibernate all decode
it identically — same shape as `ratchet-store-mysql`, and time-sortable
for UUIDv7. Hibernate paths set
`hibernate.type.preferred_uuid_jdbc_type=BINARY`.
- **`@Recurring` registration deferred to the managed scheduled
executor.** EclipseLink 5.0 + mssql-jdbc on a non-XA pool does not
autocommit DML run outside JTA, so registration from the mid-deployment
`@Initialized(ApplicationScoped)` observer was rolled back. Registration
now runs post-deployment with a real component context and retries
idempotently until each master is confirmed; SE/plain-CDI still register
inline.

## Validation

- `mvn test-compile` green for store-core + sqlserver + full testsuite
(`-am`).
- `spotless:check` green.
- `SchemaMigratorTest` passes (13/13).
- Full Arquillian matrix (incl. glassfish+sqlserver) left to CI.
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.

2 participants