feat: multi-datasource transaction validation (KOJAK-63)#17
Open
feat: multi-datasource transaction validation (KOJAK-63)#17
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SpringTransactionContextValidatornow checks the specific DataSource viaTransactionSynchronizationManager.getResource(dataSource)— rejects publishes in transactions on the wrong databaseExposedTransactionContextValidatorusesdatabase.transactionManager.currentOrNull()for per-database validation in Ktor/Exposed setupsokapi.datasource-qualifierproperty — one line of YAML to configure multi-datasource (e.g.okapi.datasource-qualifier: storeDataSource)spring-configuration-metadata.jsonMotivation
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) != nullSpringOutboxPublisher(delegate, dataSource)— passes DataSource to validatorOutboxAutoConfiguration— resolves DataSource byokapi.datasource-qualifieror falls back to primaryOkapiProperties— new@ConfigurationProperties(prefix = "okapi")withdatasourceQualifierExposedTransactionContextValidator(database)— Exposed counterpart using per-databasecurrentOrNull()spring-configuration-metadata.json— addedokapi.datasource-qualifierfor IDE autocompleteTest plan
SpringTransactionContextValidatorTest— 5 scenarios (no tx, correct DS, wrong DS, read-only, both DS)MultiDataSourceTransactionTest— integration test with 2 Postgres Testcontainers + 2 TransactionManagersExposedTransactionContextValidatorTest— 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)