@powersync/service-module-mongodb-storage 0.0.0-dev-20260803144812 → 0.0.0-dev-20260929095716
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -7
- package/dist/storage/MongoBucketStorage.d.ts +42 -5
- package/dist/storage/MongoBucketStorage.js +404 -215
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatch.d.ts +39 -14
- package/dist/storage/implementation/MongoBucketBatch.js +169 -98
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +21 -79
- package/dist/storage/implementation/MongoCompactor.js +27 -238
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoIdSequence.d.ts +10 -1
- package/dist/storage/implementation/MongoIdSequence.js +14 -1
- package/dist/storage/implementation/MongoIdSequence.js.map +1 -1
- package/dist/storage/implementation/MongoOpIdAllocator.d.ts +27 -0
- package/dist/storage/implementation/MongoOpIdAllocator.js +97 -0
- package/dist/storage/implementation/MongoOpIdAllocator.js.map +1 -0
- package/dist/storage/implementation/MongoParameterCompactor.d.ts +83 -10
- package/dist/storage/implementation/MongoParameterCompactor.js +309 -96
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +9 -1
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js +35 -2
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -1
- package/dist/storage/implementation/MongoPersistedReplicationStream.d.ts +2 -1
- package/dist/storage/implementation/MongoPersistedReplicationStream.js +7 -4
- package/dist/storage/implementation/MongoPersistedReplicationStream.js.map +1 -1
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.d.ts +3 -3
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +10 -8
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -1
- package/dist/storage/implementation/MongoStorageProvider.js +8 -4
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +32 -11
- package/dist/storage/implementation/MongoSyncBucketStorage.js +95 -31
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +26 -2
- package/dist/storage/implementation/MongoSyncRulesLock.js +59 -10
- package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/implementation/MongoWriteBatch.d.ts +49 -0
- package/dist/storage/implementation/MongoWriteBatch.js +149 -0
- package/dist/storage/implementation/MongoWriteBatch.js.map +1 -0
- package/dist/storage/implementation/MongoWriteCheckpointAPI.d.ts +9 -7
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js +9 -30
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/SyncRuleStateUpdate.d.ts +11 -5
- package/dist/storage/implementation/SyncRuleStateUpdate.js +14 -6
- package/dist/storage/implementation/SyncRuleStateUpdate.js.map +1 -1
- package/dist/storage/implementation/common/MongoSyncBucketStorageCheckpoint.d.ts +22 -2
- package/dist/storage/implementation/common/PersistedBatch.d.ts +32 -4
- package/dist/storage/implementation/common/PersistedBatch.js +57 -14
- package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +3 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +3 -0
- package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -1
- package/dist/storage/implementation/db.d.ts +9 -1
- package/dist/storage/implementation/db.js +28 -1
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +35 -0
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +11 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +227 -221
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +38 -6
- package/dist/storage/implementation/v1/MongoCompactorV1.js +247 -20
- package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +16 -2
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -9
- package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +18 -11
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +66 -17
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -1
- package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +7 -5
- package/dist/storage/implementation/v1/PersistedBatchV1.js +22 -28
- package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -1
- package/dist/storage/implementation/v1/models.js +1 -0
- package/dist/storage/implementation/v1/models.js.map +1 -1
- package/dist/storage/implementation/v3/CompactionLease.d.ts +50 -0
- package/dist/storage/implementation/v3/CompactionLease.js +131 -0
- package/dist/storage/implementation/v3/CompactionLease.js.map +1 -0
- package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +14 -2
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +261 -194
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +83 -25
- package/dist/storage/implementation/v3/MongoCompactorV3.js +702 -159
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +18 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +23 -0
- package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.d.ts +2 -15
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js +60 -92
- package/dist/storage/implementation/v3/MongoStoppedSyncConfigCleanup.js.map +1 -1
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +19 -9
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +92 -32
- package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.d.ts +26 -0
- package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.js +82 -0
- package/dist/storage/implementation/v3/MongoWriteCheckpointAPIV3.js.map +1 -0
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +14 -8
- package/dist/storage/implementation/v3/PersistedBatchV3.js +77 -45
- package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +12 -2
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +42 -3
- package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -1
- package/dist/storage/implementation/v3/bucket-format.js +2 -0
- package/dist/storage/implementation/v3/bucket-format.js.map +1 -1
- package/dist/storage/implementation/v3/compact-utils.d.ts +108 -0
- package/dist/storage/implementation/v3/compact-utils.js +223 -0
- package/dist/storage/implementation/v3/compact-utils.js.map +1 -0
- package/dist/storage/implementation/v3/compaction-constants.d.ts +5 -0
- package/dist/storage/implementation/v3/compaction-constants.js +6 -0
- package/dist/storage/implementation/v3/compaction-constants.js.map +1 -0
- package/dist/storage/implementation/v3/models.d.ts +90 -6
- package/dist/storage/implementation/v3/models.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.d.ts +7 -8
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +16 -5
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/ObjectStorage.d.ts +14 -11
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.d.ts +6 -8
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +17 -11
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.d.ts +38 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js +164 -0
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js.map +1 -0
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.d.ts +37 -12
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js +197 -25
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js.map +1 -1
- package/dist/storage/implementation/v3/source-table-utils.d.ts +15 -7
- package/dist/storage/implementation/v3/source-table-utils.js +49 -57
- package/dist/storage/implementation/v3/source-table-utils.js.map +1 -1
- package/dist/storage/storage-index.d.ts +2 -1
- package/dist/storage/storage-index.js +1 -0
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/types/types.d.ts +28 -1
- package/dist/types/types.js +28 -7
- package/dist/types/types.js.map +1 -1
- package/dist/utils/test-utils.d.ts +4 -2
- package/dist/utils/test-utils.js +7 -4
- package/dist/utils/test-utils.js.map +1 -1
- package/dist/utils/util.js +3 -1
- package/dist/utils/util.js.map +1 -1
- package/package.json +10 -9
- package/src/storage/MongoBucketStorage.ts +545 -277
- package/src/storage/implementation/MongoBucketBatch.ts +258 -124
- package/src/storage/implementation/MongoCompactor.ts +37 -346
- package/src/storage/implementation/MongoIdSequence.ts +21 -1
- package/src/storage/implementation/MongoOpIdAllocator.ts +105 -0
- package/src/storage/implementation/MongoParameterCompactor.ts +388 -102
- package/src/storage/implementation/MongoParsedSyncConfigSet.ts +46 -1
- package/src/storage/implementation/MongoPersistedReplicationStream.ts +12 -4
- package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +48 -29
- package/src/storage/implementation/MongoStorageProvider.ts +16 -4
- package/src/storage/implementation/MongoSyncBucketStorage.ts +133 -40
- package/src/storage/implementation/MongoSyncRulesLock.ts +78 -12
- package/src/storage/implementation/MongoWriteBatch.ts +172 -0
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +16 -46
- package/src/storage/implementation/SyncRuleStateUpdate.ts +13 -6
- package/src/storage/implementation/common/MongoSyncBucketStorageCheckpoint.ts +24 -2
- package/src/storage/implementation/common/PersistedBatch.ts +76 -20
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +4 -0
- package/src/storage/implementation/db.ts +36 -2
- package/src/storage/implementation/models.ts +39 -0
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +272 -254
- package/src/storage/implementation/v1/MongoCompactorV1.ts +327 -40
- package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +25 -9
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +88 -28
- package/src/storage/implementation/v1/PersistedBatchV1.ts +25 -30
- package/src/storage/implementation/v1/models.ts +1 -0
- package/src/storage/implementation/v3/CompactionLease.ts +152 -0
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +339 -247
- package/src/storage/implementation/v3/MongoCompactorV3.ts +955 -225
- package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +34 -0
- package/src/storage/implementation/v3/MongoStoppedSyncConfigCleanup.ts +100 -109
- package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +112 -42
- package/src/storage/implementation/v3/MongoWriteCheckpointAPIV3.ts +111 -0
- package/src/storage/implementation/v3/PersistedBatchV3.ts +94 -51
- package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +55 -4
- package/src/storage/implementation/v3/bucket-format.ts +2 -0
- package/src/storage/implementation/v3/compact-utils.ts +329 -0
- package/src/storage/implementation/v3/compaction-constants.ts +5 -0
- package/src/storage/implementation/v3/models.ts +97 -4
- package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +24 -9
- package/src/storage/implementation/v3/object-storage/ObjectStorage.ts +20 -5
- package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +26 -14
- package/src/storage/implementation/v3/object-storage/ObjectStorageUsage.ts +229 -0
- package/src/storage/implementation/v3/object-storage/S3ObjectStorage.ts +279 -29
- package/src/storage/implementation/v3/source-table-utils.ts +82 -76
- package/src/storage/storage-index.ts +2 -0
- package/src/types/types.ts +42 -7
- package/src/utils/test-utils.ts +9 -5
- package/src/utils/util.ts +3 -1
- package/test/src/__snapshots__/storage.test.ts.snap +25 -0
- package/test/src/__snapshots__/storage_sync.test.ts.snap +794 -205
- package/test/src/chunk_compaction_config.test.ts +38 -0
- package/test/src/cleanup-stopped-sync-configs.test.ts +59 -26
- package/test/src/compact-utils.test.ts +251 -0
- package/test/src/concurrent_writers.test.ts +599 -0
- package/test/src/connection_config.test.ts +159 -0
- package/test/src/helpers/MemoryObjectStorage.ts +14 -6
- package/test/src/helpers/concurrentWriter.mjs +29 -0
- package/test/src/helpers/s3TestFactory.ts +12 -12
- package/test/src/object_storage_usage.test.ts +268 -0
- package/test/src/parameter_compacting_v1.test.ts +120 -0
- package/test/src/parameter_compacting_v3.test.ts +192 -0
- package/test/src/parameter_compaction_fence.test.ts +276 -0
- package/test/src/storage.test.ts +280 -24
- package/test/src/storage_compacting.test.ts +1097 -253
- package/test/src/storage_object_storage_inline_threshold.test.ts +3 -3
- package/test/src/storage_s3_checksums.test.ts +6 -7
- package/test/src/storage_s3_compaction_lifecycle.test.ts +143 -17
- package/test/src/storage_s3_reading.test.ts +294 -6
- package/test/src/storage_s3_writing.test.ts +4 -3
- package/test/src/storage_sync.test.ts +412 -80
- package/test/src/storeCurrentData.test.ts +5 -5
- package/test/src/util.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/test/src/storage.test.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { mongoTestStorageFactoryGenerator } from '@module/utils/test-utils.js';
|
|
2
2
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
3
3
|
import { storage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
4
|
-
import { register, test_utils } from '@powersync/service-core-tests';
|
|
4
|
+
import { compactActive, getTestStorage, register, test_utils } from '@powersync/service-core-tests';
|
|
5
5
|
import { describe, expect, test } from 'vitest';
|
|
6
|
+
import { MongoSyncBucketStorage } from '../../src/storage/implementation/createMongoSyncBucketStorage.js';
|
|
7
|
+
import { VersionedPowerSyncMongoV3 } from '../../src/storage/implementation/v3/VersionedPowerSyncMongoV3.js';
|
|
6
8
|
import { env } from './env.js';
|
|
7
9
|
import { INITIALIZED_MONGO_STORAGE_FACTORY, TEST_STORAGE_VERSIONS } from './util.js';
|
|
8
10
|
|
|
@@ -33,7 +35,7 @@ bucket_definitions:
|
|
|
33
35
|
{ storageVersion }
|
|
34
36
|
)
|
|
35
37
|
);
|
|
36
|
-
const bucketStorage = factory
|
|
38
|
+
const bucketStorage = await getTestStorage(factory, syncRules);
|
|
37
39
|
|
|
38
40
|
// user1 has no existing row, so this covers updateMany with upsert.
|
|
39
41
|
// The initial request stores checkpoint id 42 at source head 5/0.
|
|
@@ -115,36 +117,290 @@ bucket_definitions:
|
|
|
115
117
|
await managedWriter.markAllSnapshotDone('1/1');
|
|
116
118
|
await managedWriter.keepalive('8/0');
|
|
117
119
|
|
|
118
|
-
await
|
|
120
|
+
await compactActive(factory, {
|
|
119
121
|
compactBuckets: [],
|
|
120
122
|
deleteCheckpointRequestsBefore: new Date('2024-02-01T00:00:00.000Z')
|
|
121
123
|
});
|
|
122
124
|
// The request is now both expired and processed, so it can be removed.
|
|
123
125
|
await expect(factory.db.write_checkpoints.findOne({ user_id: 'user2' })).resolves.toBeNull();
|
|
124
126
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
127
|
+
if (storageVersion < 3) {
|
|
128
|
+
bucketStorage.setWriteCheckpointMode({
|
|
129
|
+
mode: storage.WriteCheckpointMode.CUSTOM
|
|
130
|
+
});
|
|
131
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
132
|
+
await writer.markAllSnapshotDone('1/1');
|
|
133
|
+
const customCheckpointRequestedAt = new Date('2024-01-01T00:00:00.000Z');
|
|
134
|
+
writer.addCustomWriteCheckpoint({
|
|
135
|
+
user_id: 'custom1',
|
|
136
|
+
checkpoint: 51n,
|
|
137
|
+
checkpoint_requested_at: customCheckpointRequestedAt
|
|
138
|
+
});
|
|
139
|
+
await writer.flush();
|
|
140
|
+
const customRequested = await factory.db.custom_write_checkpoints.findOne({ user_id: 'custom1' });
|
|
141
|
+
expect(customRequested?.checkpoint_requested_at).toEqual(customCheckpointRequestedAt);
|
|
137
142
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
writer.addCustomWriteCheckpoint({
|
|
144
|
+
user_id: 'custom1',
|
|
145
|
+
checkpoint: 52n
|
|
146
|
+
});
|
|
147
|
+
// A custom-checkpoint-only commit uses the combined final flush path too.
|
|
148
|
+
await writer.commit('8/1');
|
|
149
|
+
const customGenerated = await factory.db.custom_write_checkpoints.findOne({ user_id: 'custom1' });
|
|
150
|
+
expect(customGenerated).not.toBeNull();
|
|
151
|
+
expect(customGenerated?.checkpoint_requested_at).toBeUndefined();
|
|
152
|
+
}
|
|
146
153
|
});
|
|
147
154
|
|
|
155
|
+
test.runIf(storageVersion >= 3)(
|
|
156
|
+
'stores and streams checkpoints from different event definitions independently',
|
|
157
|
+
async (context) => {
|
|
158
|
+
await using factory = await INITIALIZED_MONGO_STORAGE_FACTORY.factory();
|
|
159
|
+
const syncRules = await factory.updateSyncRules(
|
|
160
|
+
updateSyncRulesFromYaml(
|
|
161
|
+
`
|
|
162
|
+
config:
|
|
163
|
+
edition: 3
|
|
164
|
+
|
|
165
|
+
streams:
|
|
166
|
+
global:
|
|
167
|
+
query: SELECT * FROM checkpoints
|
|
168
|
+
|
|
169
|
+
event_definitions:
|
|
170
|
+
checkpoint_a:
|
|
171
|
+
payloads:
|
|
172
|
+
- SELECT id FROM checkpoints WHERE kind = 'a'
|
|
173
|
+
checkpoint_b:
|
|
174
|
+
payloads:
|
|
175
|
+
- SELECT id FROM checkpoints WHERE kind = 'b'
|
|
176
|
+
unrelated_event:
|
|
177
|
+
payloads:
|
|
178
|
+
- SELECT id FROM checkpoints WHERE kind = 'unrelated'
|
|
179
|
+
`,
|
|
180
|
+
{ storageVersion }
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
const bucketStorage = await getTestStorage(factory, syncRules);
|
|
184
|
+
const db = bucketStorage.db as VersionedPowerSyncMongoV3;
|
|
185
|
+
const activeSyncConfig = bucketStorage.getParsedSyncRules({ defaultSchema: 'public' });
|
|
186
|
+
const eventA = activeSyncConfig.eventDescriptors.find((event) => event.name == 'checkpoint_a')!;
|
|
187
|
+
const eventB = activeSyncConfig.eventDescriptors.find((event) => event.name == 'checkpoint_b')!;
|
|
188
|
+
const unrelatedEvent = activeSyncConfig.eventDescriptors.find((event) => event.name == 'unrelated_event')!;
|
|
189
|
+
const mapping = syncRules.syncConfigContent[0].mapping;
|
|
190
|
+
const eventAId = mapping.eventId(eventA);
|
|
191
|
+
const eventBId = mapping.eventId(eventB);
|
|
192
|
+
const unrelatedEventId = mapping.eventId(unrelatedEvent);
|
|
193
|
+
expect(() =>
|
|
194
|
+
bucketStorage.setWriteCheckpointMode({
|
|
195
|
+
mode: storage.WriteCheckpointMode.CUSTOM
|
|
196
|
+
})
|
|
197
|
+
).toThrow('eventName');
|
|
198
|
+
bucketStorage.setWriteCheckpointMode({
|
|
199
|
+
mode: storage.WriteCheckpointMode.CUSTOM,
|
|
200
|
+
eventName: 'unknown-event'
|
|
201
|
+
});
|
|
202
|
+
await expect(bucketStorage.lastWriteCheckpoint({ user_id: 'user1' })).rejects.toThrow(
|
|
203
|
+
'No mapping found for event definition unknown-event'
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
bucketStorage.setWriteCheckpointMode({
|
|
207
|
+
mode: storage.WriteCheckpointMode.CUSTOM,
|
|
208
|
+
eventName: eventA.name
|
|
209
|
+
});
|
|
210
|
+
// Reads before the first checkpoint must behave like an empty result;
|
|
211
|
+
// they must not require the lazily-created collection to exist.
|
|
212
|
+
await expect(bucketStorage.lastWriteCheckpoint({ user_id: 'user1' })).resolves.toBeNull();
|
|
213
|
+
|
|
214
|
+
await using invalidWriter = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
215
|
+
invalidWriter.addCustomWriteCheckpoint({ user_id: 'invalid', checkpoint: 1n });
|
|
216
|
+
await expect(invalidWriter.flush()).rejects.toThrow('require an event definition id');
|
|
217
|
+
await expect(db.listCustomCheckpointRequestCollections(syncRules.replicationStreamId)).resolves.toEqual([]);
|
|
218
|
+
|
|
219
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
220
|
+
await writer.markAllSnapshotDone('1/1');
|
|
221
|
+
const abortController = new AbortController();
|
|
222
|
+
context.onTestFinished(() => abortController.abort());
|
|
223
|
+
const iter = bucketStorage
|
|
224
|
+
.watchCheckpointChanges({
|
|
225
|
+
user_id: 'user1',
|
|
226
|
+
signal: abortController.signal
|
|
227
|
+
})
|
|
228
|
+
[Symbol.asyncIterator]();
|
|
229
|
+
|
|
230
|
+
writer.addCustomWriteCheckpoint({ user_id: 'user1', checkpoint: 5n, event_id: eventAId });
|
|
231
|
+
writer.addCustomWriteCheckpoint({ user_id: 'user1', checkpoint: 8n, event_id: eventBId });
|
|
232
|
+
// Collections must be prepared before the combined flush/checkpoint transaction.
|
|
233
|
+
await writer.keepalive('5/0');
|
|
234
|
+
|
|
235
|
+
const eventACollection = db.customCheckpointRequests({
|
|
236
|
+
replicationStreamId: syncRules.replicationStreamId,
|
|
237
|
+
eventId: eventAId
|
|
238
|
+
});
|
|
239
|
+
const eventBCollection = db.customCheckpointRequests({
|
|
240
|
+
replicationStreamId: syncRules.replicationStreamId,
|
|
241
|
+
eventId: eventBId
|
|
242
|
+
});
|
|
243
|
+
await expect(eventACollection.findOne({ user_id: 'user1' })).resolves.toMatchObject({ checkpoint: 5n });
|
|
244
|
+
await expect(eventBCollection.findOne({ user_id: 'user1' })).resolves.toMatchObject({ checkpoint: 8n });
|
|
245
|
+
const checkpointCollectionNames = (
|
|
246
|
+
await db.listCustomCheckpointRequestCollections(syncRules.replicationStreamId)
|
|
247
|
+
)
|
|
248
|
+
.map((collection) => collection.collectionName)
|
|
249
|
+
.sort();
|
|
250
|
+
expect(checkpointCollectionNames).toEqual(
|
|
251
|
+
[eventACollection.collectionName, eventBCollection.collectionName].sort()
|
|
252
|
+
);
|
|
253
|
+
expect(checkpointCollectionNames).not.toContain(
|
|
254
|
+
db.customCheckpointRequests({
|
|
255
|
+
replicationStreamId: syncRules.replicationStreamId,
|
|
256
|
+
eventId: unrelatedEventId
|
|
257
|
+
}).collectionName
|
|
258
|
+
);
|
|
259
|
+
await expect(eventACollection.indexExists(['user_unique', 'op_id', 'checkpoint_requested_at'])).resolves.toBe(
|
|
260
|
+
true
|
|
261
|
+
);
|
|
262
|
+
await expect(bucketStorage.lastWriteCheckpoint({ user_id: 'user1' })).resolves.toEqual(5n);
|
|
263
|
+
await expect(iter.next()).resolves.toMatchObject({
|
|
264
|
+
done: false,
|
|
265
|
+
value: { writeCheckpoint: 5n }
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
writer.addCustomWriteCheckpoint({ user_id: 'user1', checkpoint: 9n, event_id: eventBId });
|
|
269
|
+
await writer.flush();
|
|
270
|
+
await writer.keepalive('6/0');
|
|
271
|
+
// The processing event's record does not advance clients still served by
|
|
272
|
+
// the active event definition.
|
|
273
|
+
await expect(iter.next()).resolves.toMatchObject({
|
|
274
|
+
done: false,
|
|
275
|
+
value: { writeCheckpoint: 5n }
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
bucketStorage.setWriteCheckpointMode({
|
|
279
|
+
mode: storage.WriteCheckpointMode.CUSTOM,
|
|
280
|
+
eventName: eventB.name
|
|
281
|
+
});
|
|
282
|
+
await expect(bucketStorage.lastWriteCheckpoint({ user_id: 'user1' })).resolves.toEqual(9n);
|
|
283
|
+
|
|
284
|
+
writer.addCustomWriteCheckpoint({
|
|
285
|
+
user_id: 'temporary',
|
|
286
|
+
checkpoint: 10n,
|
|
287
|
+
event_id: eventBId,
|
|
288
|
+
checkpoint_requested_at: new Date('2024-01-01T00:00:00.000Z')
|
|
289
|
+
});
|
|
290
|
+
await writer.flush();
|
|
291
|
+
await compactActive(factory, {
|
|
292
|
+
compactBuckets: [],
|
|
293
|
+
deleteCheckpointRequestsBefore: new Date('2024-01-02T00:00:00.000Z')
|
|
294
|
+
});
|
|
295
|
+
await expect(eventBCollection.findOne({ user_id: 'temporary' })).resolves.toBeNull();
|
|
296
|
+
|
|
297
|
+
// A fresh storage instance has an empty initialization cache, just like
|
|
298
|
+
// one created after a service restart. Recreating the existing indexes
|
|
299
|
+
// must be idempotent before another checkpoint is written.
|
|
300
|
+
const freshBucketStorage = await getTestStorage(factory, syncRules);
|
|
301
|
+
await using freshWriter = await freshBucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
302
|
+
freshWriter.addCustomWriteCheckpoint({
|
|
303
|
+
user_id: 'after-restart',
|
|
304
|
+
checkpoint: 11n,
|
|
305
|
+
event_id: eventAId
|
|
306
|
+
});
|
|
307
|
+
await freshWriter.flush();
|
|
308
|
+
await expect(eventACollection.findOne({ user_id: 'after-restart' })).resolves.toMatchObject({
|
|
309
|
+
checkpoint: 11n
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
await bucketStorage.clear();
|
|
313
|
+
await expect(db.listCustomCheckpointRequestCollections(syncRules.replicationStreamId)).resolves.toEqual([]);
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
test.runIf(storageVersion >= 3)(
|
|
318
|
+
'resolves custom checkpoints through the active event mapping across deployments',
|
|
319
|
+
async () => {
|
|
320
|
+
await using factory = await INITIALIZED_MONGO_STORAGE_FACTORY.factory();
|
|
321
|
+
const syncConfigYaml = (predicate: string, includeExtraStream: boolean) => `
|
|
322
|
+
config:
|
|
323
|
+
edition: 3
|
|
324
|
+
|
|
325
|
+
streams:
|
|
326
|
+
global:
|
|
327
|
+
query: SELECT * FROM checkpoints
|
|
328
|
+
${includeExtraStream ? ' todos:\n query: SELECT * FROM todos' : ''}
|
|
329
|
+
|
|
330
|
+
event_definitions:
|
|
331
|
+
write_checkpoints:
|
|
332
|
+
payloads:
|
|
333
|
+
- SELECT user_id, checkpoint FROM checkpoints WHERE ${predicate}
|
|
334
|
+
`;
|
|
335
|
+
|
|
336
|
+
const first = await factory.updateSyncRules(
|
|
337
|
+
updateSyncRulesFromYaml(syncConfigYaml("kind = 'write'", false), { storageVersion })
|
|
338
|
+
);
|
|
339
|
+
const firstEventId = first.syncConfigContent[0].mapping.eventDefinitionIdByName('write_checkpoints');
|
|
340
|
+
const firstStorage = await getTestStorage(factory, first);
|
|
341
|
+
await using firstWriter = await firstStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
342
|
+
firstWriter.addCustomWriteCheckpoint({ user_id: 'user1', checkpoint: 5n, event_id: firstEventId });
|
|
343
|
+
await firstWriter.markAllSnapshotDone('1/1');
|
|
344
|
+
await firstWriter.commit('1/1');
|
|
345
|
+
|
|
346
|
+
const unchanged = await factory.updateSyncRules(
|
|
347
|
+
updateSyncRulesFromYaml(syncConfigYaml("kind = 'write'", true), { storageVersion })
|
|
348
|
+
);
|
|
349
|
+
const unchangedEventId = unchanged.syncConfigContent[1].mapping.eventDefinitionIdByName('write_checkpoints');
|
|
350
|
+
expect(unchangedEventId).toBe(firstEventId);
|
|
351
|
+
|
|
352
|
+
await using unchangedWriter = await (
|
|
353
|
+
await getTestStorage(factory, unchanged)
|
|
354
|
+
).createWriter(test_utils.BATCH_OPTIONS);
|
|
355
|
+
await unchangedWriter.markAllSnapshotDone('2/1');
|
|
356
|
+
await unchangedWriter.commit('2/1');
|
|
357
|
+
|
|
358
|
+
const unchangedActiveStorage = (await factory.getActiveSyncConfig())!.storage as MongoSyncBucketStorage;
|
|
359
|
+
unchangedActiveStorage.setWriteCheckpointMode({
|
|
360
|
+
mode: storage.WriteCheckpointMode.CUSTOM,
|
|
361
|
+
eventName: 'write_checkpoints'
|
|
362
|
+
});
|
|
363
|
+
await expect(
|
|
364
|
+
unchangedActiveStorage.lastWriteCheckpoint({
|
|
365
|
+
user_id: 'user1'
|
|
366
|
+
})
|
|
367
|
+
).resolves.toBe(5n);
|
|
368
|
+
|
|
369
|
+
const changed = await factory.updateSyncRules(
|
|
370
|
+
updateSyncRulesFromYaml(syncConfigYaml("kind = 'processed'", true), { storageVersion })
|
|
371
|
+
);
|
|
372
|
+
const changedEventId = changed.syncConfigContent[1].mapping.eventDefinitionIdByName('write_checkpoints');
|
|
373
|
+
expect(changedEventId).not.toBe(unchangedEventId);
|
|
374
|
+
|
|
375
|
+
await using changedWriter = await (
|
|
376
|
+
await getTestStorage(factory, changed)
|
|
377
|
+
).createWriter(test_utils.BATCH_OPTIONS);
|
|
378
|
+
changedWriter.addCustomWriteCheckpoint({ user_id: 'user1', checkpoint: 9n, event_id: changedEventId });
|
|
379
|
+
await changedWriter.flush();
|
|
380
|
+
|
|
381
|
+
// Until activation, reads still use the previous config's assigned id and collection.
|
|
382
|
+
await expect(
|
|
383
|
+
unchangedActiveStorage.lastWriteCheckpoint({
|
|
384
|
+
user_id: 'user1'
|
|
385
|
+
})
|
|
386
|
+
).resolves.toBe(5n);
|
|
387
|
+
|
|
388
|
+
await changedWriter.markAllSnapshotDone('3/1');
|
|
389
|
+
await changedWriter.commit('3/1');
|
|
390
|
+
|
|
391
|
+
const changedActiveStorage = (await factory.getActiveSyncConfig())!.storage as MongoSyncBucketStorage;
|
|
392
|
+
changedActiveStorage.setWriteCheckpointMode({
|
|
393
|
+
mode: storage.WriteCheckpointMode.CUSTOM,
|
|
394
|
+
eventName: 'write_checkpoints'
|
|
395
|
+
});
|
|
396
|
+
await expect(
|
|
397
|
+
changedActiveStorage.lastWriteCheckpoint({
|
|
398
|
+
user_id: 'user1'
|
|
399
|
+
})
|
|
400
|
+
).resolves.toBe(9n);
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
|
|
148
404
|
/**
|
|
149
405
|
* It's extremely rare (but technically possible) to have duplicate write checkpoint records
|
|
150
406
|
* for a full user_id. This is normally not an issue for checkpoints requested from `write-checkpoint2.json`,
|
|
@@ -164,7 +420,7 @@ bucket_definitions:
|
|
|
164
420
|
{ storageVersion }
|
|
165
421
|
)
|
|
166
422
|
);
|
|
167
|
-
const bucketStorage = factory
|
|
423
|
+
const bucketStorage = await getTestStorage(factory, syncRules);
|
|
168
424
|
|
|
169
425
|
// Replicate an extremely rare possibility where there are multiple records
|
|
170
426
|
await factory.db.write_checkpoints.insertMany([
|