-
Notifications
You must be signed in to change notification settings - Fork 65
feat(mongoreactive): add journal event persistence #955
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -103,3 +103,5 @@ test_*.db-shm | |
| # Generic SQLite artifacts | ||
| *.db-wal | ||
| *.db-shm | ||
| # Local Pi runtime state | ||
| .atl/ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,22 +21,31 @@ | |
| import com.mongodb.reactivestreams.client.ClientSession; | ||
| import com.mongodb.reactivestreams.client.MongoDatabase; | ||
| import io.flamingock.externalsystem.mongodb.reactive.api.MongoDBReactiveExternalSystem; | ||
| import io.flamingock.internal.common.core.audit.AuditPersistenceFactory; | ||
| import io.flamingock.internal.common.core.audit.AuditReader; | ||
| import io.flamingock.internal.common.core.context.ContextResolver; | ||
| import io.flamingock.internal.common.core.error.FlamingockException; | ||
| import io.flamingock.internal.common.core.feature.Features; | ||
| import io.flamingock.internal.core.configuration.community.CommunityConfigurable; | ||
| import io.flamingock.internal.core.external.store.CommunityAuditStore; | ||
| import io.flamingock.internal.core.external.store.audit.community.CommunityAuditPersistence; | ||
| import io.flamingock.internal.core.external.store.lock.community.CommunityLockService; | ||
| import io.flamingock.internal.core.journal.JournalEventSequencer; | ||
| import io.flamingock.internal.core.journal.JournalEventSequencerFactory; | ||
| import io.flamingock.internal.util.Constants; | ||
| import io.flamingock.internal.util.FeatureFlag; | ||
| import io.flamingock.internal.util.TimeService; | ||
| import io.flamingock.internal.util.id.RunnerId; | ||
| import io.flamingock.store.mongodb.reactive.internal.MongoDBReactiveAuditPersistence; | ||
| import io.flamingock.store.mongodb.reactive.internal.MongoDBReactiveAuditRepository; | ||
| import io.flamingock.store.mongodb.reactive.internal.MongoDBReactiveJournalEventStore; | ||
| import io.flamingock.store.mongodb.reactive.internal.MongoDBReactiveLockService; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.HashSet; | ||
| import java.util.Set; | ||
|
|
||
| import static io.flamingock.internal.common.mongodb.journal.JournalEventPersistenceConstants.DEFAULT_JOURNAL_STORE_NAME; | ||
| import static io.flamingock.internal.util.constants.CommunityPersistenceConstants.DEFAULT_AUDIT_STORE_NAME; | ||
| import static io.flamingock.internal.util.constants.CommunityPersistenceConstants.DEFAULT_LOCK_STORE_NAME; | ||
|
|
||
|
|
@@ -46,15 +55,19 @@ public class MongoDBReactiveAuditStore implements CommunityAuditStore { | |
|
|
||
| protected RunnerId runnerId; | ||
| private CommunityConfigurable communityConfiguration; | ||
| private MongoDBReactiveAuditPersistence persistence; | ||
| private CommunityAuditPersistence persistence; | ||
| private MongoDBReactiveLockService lockService; | ||
| private MongoDatabase database; | ||
| private String auditRepositoryName = DEFAULT_AUDIT_STORE_NAME; | ||
| private String lockRepositoryName = DEFAULT_LOCK_STORE_NAME; | ||
| private String journalRepositoryName = DEFAULT_JOURNAL_STORE_NAME; | ||
| private ReadConcern readConcern = ReadConcern.MAJORITY; | ||
| private ReadPreference readPreference = ReadPreference.primary(); | ||
| private WriteConcern writeConcern = WriteConcern.MAJORITY.withJournal(true); | ||
| private boolean autoCreate = true; | ||
| private MongoDBReactiveAuditRepository auditRepository; | ||
| private MongoDBReactiveJournalEventStore journalEventStore; | ||
| private JournalEventSequencerFactory journalEventSequencerFactory; | ||
|
|
||
| private MongoDBReactiveAuditStore(MongoDBReactiveExternalSystem mongoDBTargetSystem) { | ||
| this.mongoDBTargetSystem = mongoDBTargetSystem; | ||
|
|
@@ -89,6 +102,11 @@ public MongoDBReactiveAuditStore withLockRepositoryName(String lockRepositoryNam | |
| return this; | ||
| } | ||
|
|
||
| public MongoDBReactiveAuditStore withJournalRepositoryName(String journalRepositoryName) { | ||
| this.journalRepositoryName = journalRepositoryName; | ||
| return this; | ||
| } | ||
|
|
||
| public MongoDBReactiveAuditStore withReadConcern(ReadConcern readConcern) { | ||
| this.readConcern = readConcern; | ||
| return this; | ||
|
|
@@ -114,40 +132,57 @@ public void initialize(ContextResolver baseContext) { | |
| runnerId = baseContext.getRequiredDependencyValue(RunnerId.class); | ||
| communityConfiguration = baseContext.getRequiredDependencyValue(CommunityConfigurable.class); | ||
| database = mongoDBTargetSystem.getMongoDatabase(); | ||
| this.validate(); | ||
| this.validate(); | ||
|
|
||
| auditRepository = new MongoDBReactiveAuditRepository( | ||
| database, auditRepositoryName, readConcern, readPreference, writeConcern); | ||
| journalEventStore = new MongoDBReactiveJournalEventStore( | ||
| database, journalRepositoryName, readConcern, readPreference, writeConcern); | ||
| journalEventSequencerFactory = new JournalEventSequencerFactory(journalEventStore); | ||
|
|
||
| lockService = new MongoDBReactiveLockService( | ||
| database, | ||
| lockRepositoryName, | ||
| readConcern, | ||
| readPreference, | ||
| writeConcern, | ||
| TimeService.getDefault() | ||
| ); | ||
| lockService.initialize(autoCreate); | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized CommunityAuditPersistence getPersistence() { | ||
| if (persistence == null) { | ||
| persistence = new MongoDBReactiveAuditPersistence( | ||
| public AuditPersistenceFactory<CommunityAuditPersistence> getPersistenceFactory() { | ||
| return stageId -> { | ||
| auditRepository.initialize(autoCreate); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked into this properly. We cannot just move these calls into The duplicate calls are idempotent and currently preserve the required order:
I agree the lifecycle is not clean. The right solution is a cross-audit-store refactor: persistence should receive the stage ID and sequencer factory, initialize the repositories, and then create the sequencer itself. That refactor should be applied consistently across the audit stores, not only MongoDB Reactive. It is therefore better handled separately from this PR, rather than introducing a broader lifecycle-contract change here. |
||
| if (FeatureFlag.isEnabled(Features.JOURNAL_EVENTS, false)) { | ||
| journalEventStore.initialize(autoCreate); | ||
| } | ||
| JournalEventSequencer journalEventSequencer = journalEventSequencerFactory.forStream(stageId); | ||
| MongoDBReactiveAuditPersistence stagePersistence = new MongoDBReactiveAuditPersistence( | ||
| communityConfiguration, | ||
| database, | ||
| auditRepositoryName, | ||
| readConcern, | ||
| readPreference, | ||
| writeConcern, | ||
| auditRepository, | ||
| journalEventStore, | ||
| journalEventSequencer, | ||
| mongoDBTargetSystem.getTxWrapper(), | ||
| autoCreate | ||
| ); | ||
| persistence.initialize(runnerId); | ||
| } | ||
| return persistence; | ||
| stagePersistence.initialize(runnerId); | ||
| if (persistence == null) { | ||
| persistence = stagePersistence; | ||
| } | ||
| return stagePersistence; | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public synchronized CommunityLockService getLockService() { | ||
| if (lockService == null) { | ||
| lockService = new MongoDBReactiveLockService( | ||
| database, | ||
| lockRepositoryName, | ||
| readConcern, | ||
| readPreference, | ||
| writeConcern, | ||
| TimeService.getDefault() | ||
| ); | ||
| lockService.initialize(autoCreate); | ||
| public AuditReader getAuditReader() { | ||
| auditRepository.initialize(autoCreate); | ||
| return () -> auditRepository.getAuditHistory(); | ||
| } | ||
|
|
||
| } | ||
| @Override | ||
| public synchronized CommunityLockService getLockService() { | ||
| return lockService; | ||
| } | ||
|
|
||
|
|
@@ -165,10 +200,22 @@ private void validate() { | |
| throw new FlamingockException("The 'lockRepositoryName' property is required."); | ||
| } | ||
|
|
||
| if (journalRepositoryName == null || journalRepositoryName.trim().isEmpty()) { | ||
| throw new FlamingockException("The 'journalRepositoryName' property is required."); | ||
| } | ||
|
|
||
| if (auditRepositoryName.trim().equalsIgnoreCase(lockRepositoryName.trim())) { | ||
| throw new FlamingockException("The 'auditRepositoryName' and 'lockRepositoryName' properties must not be the same."); | ||
| } | ||
|
|
||
| if (journalRepositoryName.trim().equalsIgnoreCase(auditRepositoryName.trim())) { | ||
| throw new FlamingockException("The 'journalRepositoryName' and 'auditRepositoryName' properties must not be the same."); | ||
| } | ||
|
|
||
| if (journalRepositoryName.trim().equalsIgnoreCase(lockRepositoryName.trim())) { | ||
| throw new FlamingockException("The 'journalRepositoryName' and 'lockRepositoryName' properties must not be the same."); | ||
| } | ||
|
|
||
| if (readConcern == null) { | ||
| throw new FlamingockException("The 'readConcern' property is required."); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stray, unrelated to journal events. Not present in #948/#949 or the sync PR. Please drop this line from this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is intentional. It ignores a directory generated by my local development environment that should never be tracked.
I agree it is unrelated to journal events, but I do not think it warrants a separate PR for a single repository-hygiene entry. It does not affect the implementation or runtime behavior.