Skip to content

feat: multi-datasource transaction validation (KOJAK-63)#17

Open
endrju19 wants to merge 7 commits intomainfrom
feat/multi-ds-validation
Open

feat: multi-datasource transaction validation (KOJAK-63)#17
endrju19 wants to merge 7 commits intomainfrom
feat/multi-ds-validation

Conversation

@endrju19
Copy link
Copy Markdown
Collaborator

@endrju19 endrju19 commented Apr 2, 2026

Summary

  • SpringTransactionContextValidator now checks the specific DataSource via TransactionSynchronizationManager.getResource(dataSource) — rejects publishes in transactions on the wrong database
  • ExposedTransactionContextValidator uses database.transactionManager.currentOrNull() for per-database validation in Ktor/Exposed setups
  • New okapi.datasource-qualifier property — one line of YAML to configure multi-datasource (e.g. okapi.datasource-qualifier: storeDataSource)
  • Full autocomplete support via spring-configuration-metadata.json

Motivation

In multi-datasource setups the previous validator only checked isActualTransactionActive() — it accepted ANY active transaction, even from a different DataSource. This silently broke the outbox pattern's atomicity guarantee.

Changes

  • SpringTransactionContextValidator(dataSource) — adds third check: getResource(dataSource) != null
  • SpringOutboxPublisher(delegate, dataSource) — passes DataSource to validator
  • OutboxAutoConfiguration — resolves DataSource by okapi.datasource-qualifier or falls back to primary
  • OkapiProperties — new @ConfigurationProperties(prefix = "okapi") with datasourceQualifier
  • ExposedTransactionContextValidator(database) — Exposed counterpart using per-database currentOrNull()
  • spring-configuration-metadata.json — added okapi.datasource-qualifier for IDE autocomplete

Test plan

  • SpringTransactionContextValidatorTest — 5 scenarios (no tx, correct DS, wrong DS, read-only, both DS)
  • MultiDataSourceTransactionTest — integration test with 2 Postgres Testcontainers + 2 TransactionManagers
  • ExposedTransactionContextValidatorTest — 6 scenarios with H2 (no tx, correct DB, wrong DB, nested both ways, read-only)
  • DataSourceQualifierAutoConfigurationTest — 4 scenarios (single DS, qualifier hit, qualifier miss, multiple DS)
  • All existing tests pass (backward compatible)

endrju19 added 7 commits April 1, 2026 15:06
…ngTransactionContextValidator

SpringTransactionContextValidator now requires a DataSource parameter and
checks TransactionSynchronizationManager.getResource(dataSource) to verify
the outbox DataSource has a connection bound in the current transaction.
This prevents silent atomicity violations in multi-datasource setups where
a transaction on DataSource A would incorrectly satisfy validation when
the outbox lives on DataSource B.

Also updates SpringOutboxPublisher and OutboxAutoConfiguration to pass
the DataSource through, and adapts SpringOutboxPublisherTest accordingly.
…alidation

Verify SpringOutboxPublisher correctly rejects publish when in a
transaction on a different DataSource than the outbox one. Uses two
real PostgreSQL containers (Testcontainers) with separate
SpringTransactionManager (Exposed) and DataSourceTransactionManager.
…ransaction checks

Exposed counterpart to SpringTransactionContextValidator — validates the
current thread is in an active, non-read-only Exposed transaction on the
specific Database instance where the outbox table lives. Uses
database.transactionManager.currentOrNull() which searches the
thread-local transaction stack filtered by Database instance.

Added as compileOnly dependency so the class is available only when
Exposed is on the consumer's classpath.
…lidation

- resolveDataSource now accepts a primaryDataSource parameter (injected
  by Spring as @primary) instead of using dataSources.values.first(),
  which did not respect @primary and depended on bean registration order.
- Add init block to OkapiProperties that rejects blank datasource-qualifier
  values with a clear error message.
- Move H2 test dependency version to gradle/libs.versions.toml catalog.
- Update DataSourceQualifierAutoConfigurationTest to match new 3-param
  resolveDataSource signature and register @primary beans properly.
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