Files
document-haness/docs/clean-architecture-backend-template/final/evidence/raw/135-mongo-changestream-manifest-and-probes.txt
T
DongHyeonkaandClaude Opus 5 b2963105a8 docs(keycloak-session-store): import the session-storage lab as a new project
The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.

Follows the import procedure in README.md.

  source/     the originating repository verbatim — 78 documents, 28 SVGs,
              8 manifests, plus .source-revision recording the commit
  final/      the SSOT
    document.md   729 lines written from the 29 experiment documents, not
                  concatenated: what was predicted, what was measured, and
                  where the measurement itself was wrong
    evidence/raw    125 outputs, flattened to <experiment>__<file> because
                    the originals collided (01-baseline.txt appeared three
                    times) and the audit only globs the top level
    evidence/meta   one per raw file; command and exitCode are null and the
                    README says why rather than inventing them
    evidence/browser  22 captures
    assets/       three diagrams through techviz
    .techviz/     their VizSpecs

A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.

Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.

verify-pipeline.py passes. audit-records.py reports no issues.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:51:59 +09:00

249 lines
22 KiB
Plaintext

Sub-scope 08 (mongo) changestream - manifest and negative-space probes
revision=a24ece9cf797f7ea647e33bf846b115208ed1ba5
generatedAt=2026-08-30T00:15:09+00:00
cwd=/shared/codebase/clean-architecture-backend-template/src
=== OWNED FILES ===
55410c37d99e15fd487d45057a2f44fe2b656582 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeEventIdentity.java 97
2a41c567b37fd2cd0f7adcbd0cf1db3583f020bc main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamPipeline.java 85
462398af539336129b4a1f0cf669a94eb0c52120 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamState.java 35
d6a137168ebf884948907dc09a86833bc68a1983 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamSubscription.java 50
b9cb4a344e54678c7c02fc48e71851fc2e84e79c main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoClusterTime.java 53
240d41177c0bf08b810928ed61dbdd07874b4a43 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoResumeCheckpoint.java 100
ccac8af549c5ed7b23ed3be2fa1ad68cc1ef5f48 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoResumeCheckpointStore.java 24
22ee8f0391d93b109f3ffa763f1a2acc03564de8 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoResumePosition.java 18
52b776c4178d40bae2e7b48cfcff2207c25cc3ab main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoResumeTokenCodec.java 38
76e710168870ef9d9e8a464dba087e247315ccff main/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/MongoChangeStreamSource.java 54
5d2c65139d86fefc8f341a120dd3abdff345f368 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ReactiveMongoChangeStreamConsumer.java 221
6d2bd659cc831094d7854fcfc4560846ff03927a main/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/SpringReactiveChangeStreamSource.java 71
8a7ba018966876acb9c64b9e7906ba11407f0c44 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeClaim.java 25
43032013612390ba42527cfa06d5c74f47409a42 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeDeduplicationStore.java 50
ba15c66e206050463073a2c373e88936d6ec6f36 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeProjectionResult.java 65
75e656da9f039e0124dee52b0e4b55555429c943 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeProjector.java 23
8a49dd99ecf66f528ddba3f17c38f5594d3dc3d4 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeStreamRunner.java 124
a956f31fd1f2779fc1063cfd1407ab098f65bb82 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeHistoryLostException.java 35
8b02975c382f8df615b19260076554c64aaabf66 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryDecision.java 36
fe947e6c533518c9560650b808cc29b1b0e23ae6 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicy.java 67
88ca505e553bf03721e270d95cf43de6446f5406 main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoInvalidateRecovery.java 46
c51cc24680d0b2cd33c18ca36d4c87f6518287b8 test/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamPipelineTest.java 153
60a0312b904e8b8ecee216bc84942c2ce8fbe4cc test/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoResumeCheckpointStoreTest.java 85
a9c7b48469df8438d48ae3edfce562c692c99ab8 test/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ChangeStreamConsumerLifecycleTest.java 332
39c83409212c07edc25c72849e35a3488d3fe7df test/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeStreamRunnerTest.java 357
d20e9e4712dae2642af4308eda984f84ff0ce8b0 test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java 69
production: 21
test: 5
production LOC: 1317 total
=== 8.1 reachability: beans and cross-package production references ===
$ grep -n 'ChangeStream\|changestream\|Projector\|Resume' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java
209: public dev.caskeleton.adapter.outbound.mongo.changestream.consumer.MongoChangeStreamSource
210: mongoChangeStreamSource(
213: return new dev.caskeleton.adapter.outbound.mongo.changestream.consumer
214: .SpringReactiveChangeStreamSource(template, collections);
228: dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeStreamSubscription.class,
229: dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumeCheckpointStore.class,
230: dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumeTokenCodec.class,
231: dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeProjector.class,
232: dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeDeduplicationStore
235: public dev.caskeleton.adapter.outbound.mongo.changestream.consumer
236: .ReactiveMongoChangeStreamConsumer
237: reactiveMongoChangeStreamConsumer(
238: dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeStreamSubscription
240: dev.caskeleton.adapter.outbound.mongo.changestream.consumer.MongoChangeStreamSource
242: dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumeCheckpointStore
244: dev.caskeleton.adapter.outbound.mongo.changestream.MongoResumeTokenCodec tokens,
245: dev.caskeleton.adapter.outbound.mongo.changestream.projector.MongoChangeProjector
247: dev.caskeleton.adapter.outbound.mongo.changestream.projector
250: return new dev.caskeleton.adapter.outbound.mongo.changestream.consumer
251: .ReactiveMongoChangeStreamConsumer(
256: new dev.caskeleton.adapter.outbound.mongo.changestream.MongoChangeStreamPipeline(
257: new dev.caskeleton.adapter.outbound.mongo.changestream.projector
258: .MongoChangeStreamRunner(projector, deduplication, checkpoints)),
259: new dev.caskeleton.adapter.outbound.mongo.changestream.recovery
260: .MongoChangeStreamRecoveryPolicy(),
261: new dev.caskeleton.adapter.outbound.mongo.changestream.recovery
exit=0
$ git grep -n 'changeStreams' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformSettings.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoStartupValidator.java
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java:363: properties.changeStreams(),
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformSettings.java:22: * assuming it. Both it and {@code changeStreams} used to be literal {@code true}s passed into
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformSettings.java:30: boolean changeStreams,
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformSettings.java:55: changeStreams = false;
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoStartupValidator.java:34: private final boolean changeStreamsEnabled;
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoStartupValidator.java:44: boolean changeStreamsEnabled,
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoStartupValidator.java:51: this.changeStreamsEnabled = changeStreamsEnabled;
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoStartupValidator.java:104: if (changeStreamsEnabled && !capabilities.isStable(MongoCapability.CHANGE_STREAM)) {
exit=0
$ git grep -ln 'changestream' -- 'adapter/outbound/persistence-mongo/src/main' | grep -v '/changestream/'
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/bridge/MongoChangeMessagingBridge.java
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/bridge/MongoChangeToIntegrationEventMapper.java
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/autoconfigure/MongoPlatformAutoConfiguration.java
exit=0
$ git grep -n 'new ReactiveMongoChangeStreamConsumer\|new SpringReactiveChangeStreamSource\|new MongoChangeStreamPipeline\|new MongoChangeStreamRunner' -- 'adapter/outbound/persistence-mongo/src/main'
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ReactiveMongoChangeStreamConsumer.java:145: new MongoChangeStreamPipeline.MongoChangeEvent(
exit=0
=== 8.1b implementations of the three SPIs a deployment must supply ===
$ git grep -n 'implements MongoResumeTokenCodec\|implements MongoResumeCheckpointStore\|implements MongoChangeDeduplicationStore\|implements MongoChangeProjector' -- 'adapter/outbound/persistence-mongo/src'
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/advanced/bridge/MongoChangeMessagingBridgeTest.java:115: private static final class CountingCheckpointStore implements MongoResumeCheckpointStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamPipelineTest.java:117: private static final class AlwaysClaims implements MongoChangeDeduplicationStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamPipelineTest.java:136: private static final class NoOpCheckpoints implements MongoResumeCheckpointStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ChangeStreamConsumerLifecycleTest.java:211: private static final class PlainTextCodec implements MongoResumeTokenCodec {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ChangeStreamConsumerLifecycleTest.java:261: private static final class RecordingCheckpointStore implements MongoResumeCheckpointStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ChangeStreamConsumerLifecycleTest.java:295: private final class CountingProjector implements MongoChangeProjector {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ChangeStreamConsumerLifecycleTest.java:314: private static final class AlwaysClaims implements MongoChangeDeduplicationStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeStreamRunnerTest.java:101: private static final class RecordingCheckpointStore implements MongoResumeCheckpointStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeStreamRunnerTest.java:290: private static final class ClaimingDeduplicationStore implements MongoChangeDeduplicationStore {
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeStreamRunnerTest.java:339: private static final class EmptyDeduplicationStore implements MongoChangeDeduplicationStore {
exit=0
=== 8.2 the high-water mark: set before processing, kept across a resume ===
$ grep -n 'advancesPosition\|getAndAccumulate\|highWaterMark' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/MongoChangeStreamPipeline.java 2>/dev/null || grep -n 'advancesPosition\|getAndAccumulate\|highWaterMark' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamPipeline.java
32: private final AtomicReference<MongoClusterTime> highWaterMark = new AtomicReference<>();
49: if (!advancesPosition(event.clusterTime())) {
58: private boolean advancesPosition(MongoClusterTime candidate) {
60: highWaterMark.getAndAccumulate(
67: public MongoClusterTime highWaterMark() {
68: return highWaterMark.get();
exit=0
$ grep -n 'allowsCheckpointAdvance' -B 8 adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeProjectionResult.java
38- }
39-
40- /** The event could not be interpreted and was routed to the parking workflow. */
41- public static MongoChangeProjectionResult parked(String reason) {
42- return new MongoChangeProjectionResult(Outcome.PARKED, reason);
43- }
44-
45- /** True when the checkpoint may advance past this event. */
46: public boolean allowsCheckpointAdvance() {
exit=0
$ grep -n 'recoverFrom\|openAndConsume\|Flux.defer' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ReactiveMongoChangeStreamConsumer.java
87: .thenMany(Flux.defer(this::openAndConsume))
88: .onErrorResume(this::recoverFrom);
101: private Flux<MongoChangeProjectionResult> openAndConsume() {
188: private Flux<MongoChangeProjectionResult> recoverFrom(Throwable failure) {
199: return Flux.defer(this::openAndConsume).onErrorResume(this::haltOn);
exit=0
=== 8.2b does any test cover a redelivery after a resume? ===
$ grep -n 'aResumableFailureReopensFromTheCheckpoint' -A 16 adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ChangeStreamConsumerLifecycleTest.java
93: void aResumableFailureReopensFromTheCheckpoint() {
94- checkpoints.stored = Optional.of(checkpoint("token-1", MongoResumePosition.RESUME_AFTER));
95- MongoQueryException resumable = queryFailure(133, "ResumableChangeStreamError");
96- RecordingSource source =
97- new RecordingSource(
98- Flux.error(resumable), Flux.just(event("token-2", 2, OperationType.INSERT)));
99-
100- ReactiveMongoChangeStreamConsumer consumer = consumer(source);
101- StepVerifier.create(consumer.run()).expectNextCount(1).verifyComplete();
102-
103- assertThat(source.opens).isEqualTo(2);
104- assertThat(source.reopenedAt.orElseThrow().token().getString("_data").getValue())
105- .as("the last written position is the last event a projection completed for")
106- .isEqualTo("token-1");
107- }
108-
109- @Test
exit=0
$ grep -n 'anEventBehindTheHighWaterMarkIsDropped' -A 24 adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamPipelineTest.java
62: void anEventBehindTheHighWaterMarkIsDropped() {
63- List<String> projected = new ArrayList<>();
64- MongoChangeStreamPipeline pipeline =
65- new MongoChangeStreamPipeline(
66- new MongoChangeStreamRunner(
67- (identity, change) -> {
68- projected.add(change.getString("id").getValue());
69- return Mono.just(MongoChangeProjectionResult.applied());
70- },
71- new AlwaysClaims(),
72- new NoOpCheckpoints()));
73-
74- pipeline
75- .process(Flux.just(event("a", 5), event("late", 2), event("b", 6)))
76- .collectList()
77- .block();
78-
79- assertThat(projected)
80- .as("running it would move the resume checkpoint backwards over applied work")
81- .containsExactly("a", "b");
82- assertThat(pipeline.highWaterMark()).isEqualTo(new MongoClusterTime(1_700_000_000L, 6));
83- }
84-
85- @Test
86- @DisplayName("cluster time compares numerically, not as text")
exit=0
$ grep -n 'aBusyClaimNeverAdvancesTheCheckpoint' -A 22 adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/projector/MongoChangeStreamRunnerTest.java
173: void aBusyClaimNeverAdvancesTheCheckpoint() {
174- RecordingCheckpointStore checkpoints = new RecordingCheckpointStore();
175- ClaimingDeduplicationStore deduplication = new ClaimingDeduplicationStore();
176- deduplication.claimFor(IDENTITY);
177-
178- MongoChangeStreamRunner runner =
179- new MongoChangeStreamRunner(
180- (identity, change) -> Mono.just(MongoChangeProjectionResult.applied()),
181- deduplication,
182- checkpoints);
183-
184- MongoChangeProjectionResult result =
185- runner.runOne(IDENTITY, new BsonDocument(), CHECKPOINT).block();
186-
187- assertThat(result.outcome()).isEqualTo(MongoChangeProjectionResult.Outcome.CLAIMED_ELSEWHERE);
188- assertThat(checkpoints.saveCount())
189- .as("the holder may still fail, and the stream never replays what the checkpoint passed")
190- .isZero();
191- }
192-
193- @Test
194- @org.junit.jupiter.api.DisplayName("a crash after claiming leaves the event retryable")
195- void aCrashAfterClaimingLeavesTheEventRetryable() {
exit=0
=== 8.3 duplicate mechanisms inside recovery ===
$ git grep -n 'onHistoryLost\|onResumableFailure\|onInvalidate\|checkpointFor\|requireCorrectResumeOption\|autoResumable\|MongoChangeHistoryLostException' -- 'adapter/outbound/persistence-mongo/src'
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/advanced/tenancy/database/MongoTenantMigrationCoordinator.java:85: public Optional<MongoMigrationCheckpoint> checkpointFor(String tenantKey) {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/MongoChangeStreamState.java:32: public boolean autoResumable() {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/consumer/ReactiveMongoChangeStreamConsumer.java:119: invalidates.requireCorrectResumeOption(checkpoint, checkpoint.position());
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeHistoryLostException.java:16:public final class MongoChangeHistoryLostException extends MongoPersistenceException {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeHistoryLostException.java:22: public MongoChangeHistoryLostException(
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicy.java:29: public MongoChangeStreamRecoveryDecision onHistoryLost(String subscriptionProfile) {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicy.java:36: public MongoChangeStreamRecoveryDecision onResumableFailure() {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicy.java:46: public MongoChangeStreamRecoveryDecision onInvalidate() {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoInvalidateRecovery.java:19: public MongoResumeCheckpoint checkpointFor(String subscriptionProfile, byte[] invalidateToken) {
adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoInvalidateRecovery.java:31: public void requireCorrectResumeOption(
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:21: MongoChangeStreamRecoveryDecision decision = policy.onHistoryLost("orders");
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:31: assertThat(policy.onResumableFailure().autoResume()).isTrue();
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:55: MongoResumeCheckpoint checkpoint = recovery.checkpointFor("orders", new byte[] {1, 2, 3});
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:59: () -> recovery.requireCorrectResumeOption(checkpoint, MongoResumePosition.RESUME_AFTER))
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:65: assertThat(MongoChangeStreamState.RUNNING.autoResumable()).isTrue();
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:66: assertThat(MongoChangeStreamState.HISTORY_LOST.autoResumable()).isFalse();
adapter/outbound/persistence-mongo/src/test/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicyTest.java:67: assertThat(MongoChangeStreamState.FAILED.autoResumable()).isFalse();
exit=0
=== 8.4 documentation drift: the runbooks the policy names ===
$ ls ../docs/mongodb/runbooks/ 2>&1
failover.md
history-lost.md
unknown-commit.md
exit=0
$ grep -rn 'HISTORY_LOST_RUNBOOK\|FAILURE_RUNBOOK' adapter/outbound/persistence-mongo/src/main/java/dev/caskeleton/adapter/outbound/mongo/changestream/recovery/MongoChangeStreamRecoveryPolicy.java
18: public static final String HISTORY_LOST_RUNBOOK = "docs/mongodb/runbooks/history-lost.md";
21: public static final String FAILURE_RUNBOOK = "docs/mongodb/runbooks/failover.md";
32: MongoChangeStreamState.HISTORY_LOST, HISTORY_LOST_RUNBOOK);
55: MongoChangeStreamState.HISTORY_LOST, HISTORY_LOST_RUNBOOK);
60: return MongoChangeStreamRecoveryDecision.halt(MongoChangeStreamState.FAILED, FAILURE_RUNBOOK);
exit=0
$ find .. -name 'history-lost.md' -o -name 'failover.md' | head
../docs/mongodb/runbooks/failover.md
../docs/mongodb/runbooks/history-lost.md
exit=0