@powersync/service-module-mongodb-storage 0.20.0 → 0.22.0
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 +62 -0
- package/dist/storage/MongoBucketStorage.d.ts +36 -3
- package/dist/storage/MongoBucketStorage.js +372 -207
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatch.d.ts +17 -5
- package/dist/storage/implementation/MongoBucketBatch.js +44 -17
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +19 -79
- package/dist/storage/implementation/MongoCompactor.js +27 -238
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- 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 +29 -0
- package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -1
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js +4 -2
- package/dist/storage/implementation/MongoPersistedSyncConfigContent.js.map +1 -1
- package/dist/storage/implementation/MongoStorageProvider.js +7 -3
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +27 -11
- package/dist/storage/implementation/MongoSyncBucketStorage.js +78 -31
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.js +21 -9
- 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 +49 -6
- 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 +27 -1
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +31 -0
- package/dist/storage/implementation/models.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +2 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +33 -4
- 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 +65 -16
- 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 +10 -2
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +100 -26
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +82 -25
- package/dist/storage/implementation/v3/MongoCompactorV3.js +698 -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 +91 -31
- 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 +76 -42
- 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 +107 -0
- package/dist/storage/implementation/v3/compact-utils.js +216 -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 +84 -4
- 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 +6 -6
- package/dist/storage/implementation/v3/source-table-utils.js +30 -37
- 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.js +1 -0
- 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 +507 -269
- package/src/storage/implementation/MongoBucketBatch.ts +67 -24
- package/src/storage/implementation/MongoCompactor.ts +35 -346
- package/src/storage/implementation/MongoParameterCompactor.ts +388 -102
- package/src/storage/implementation/MongoParsedSyncConfigSet.ts +34 -0
- package/src/storage/implementation/MongoPersistedSyncConfigContent.ts +4 -2
- package/src/storage/implementation/MongoStorageProvider.ts +15 -3
- package/src/storage/implementation/MongoSyncBucketStorage.ts +114 -40
- package/src/storage/implementation/MongoSyncRulesLock.ts +22 -9
- 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 +66 -10
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +4 -0
- package/src/storage/implementation/db.ts +35 -2
- package/src/storage/implementation/models.ts +34 -0
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +46 -6
- 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 +87 -27
- 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 +138 -34
- package/src/storage/implementation/v3/MongoCompactorV3.ts +958 -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 +111 -41
- package/src/storage/implementation/v3/MongoWriteCheckpointAPIV3.ts +111 -0
- package/src/storage/implementation/v3/PersistedBatchV3.ts +93 -48
- 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 +320 -0
- package/src/storage/implementation/v3/compaction-constants.ts +5 -0
- package/src/storage/implementation/v3/models.ts +91 -3
- 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 +39 -41
- package/src/storage/storage-index.ts +2 -0
- package/src/types/types.ts +42 -7
- package/src/utils/test-utils.ts +1 -0
- 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 +605 -16
- package/test/src/chunk_compaction_config.test.ts +38 -0
- package/test/src/cleanup-stopped-sync-configs.test.ts +40 -13
- package/test/src/compact-utils.test.ts +226 -0
- package/test/src/helpers/MemoryObjectStorage.ts +14 -6
- package/test/src/object_storage_usage.test.ts +267 -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 +273 -22
- package/test/src/storage_compacting.test.ts +968 -246
- package/test/src/storage_s3_checksums.test.ts +3 -4
- package/test/src/storage_s3_compaction_lifecycle.test.ts +138 -12
- package/test/src/storage_s3_reading.test.ts +290 -2
- package/test/src/storage_s3_writing.test.ts +1 -0
- package/test/src/storage_sync.test.ts +336 -41
- package/test/src/util.ts +1 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { bson, InternalOpId } from '@powersync/service-core';
|
|
3
|
+
import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
|
|
4
|
+
import type { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Incrementally compacts V3 parameter indexes using the stream operation sequence as a work
|
|
8
|
+
* cursor. The cursor is advanced only after every parameter index has completed the same range.
|
|
9
|
+
*/
|
|
10
|
+
export class MongoParameterCompactorV3 extends MongoParameterCompactor {
|
|
11
|
+
declare protected readonly db: VersionedPowerSyncMongoV3;
|
|
12
|
+
|
|
13
|
+
protected async getCollections(): Promise<mongo.Collection<mongo.Document>[]> {
|
|
14
|
+
const collections = await this.db.listParameterIndexCollections(this.replicationStreamId);
|
|
15
|
+
return collections.map(({ collection }) => collection as unknown as mongo.Collection<mongo.Document>);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected shouldCompactDocument(_doc: { _id: bigint; key: mongo.Document }): boolean {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Uses the `{ lookup: 1, key: 1, _id: -1 }` `lookup_op_id` index. */
|
|
23
|
+
protected leadingHistoryDeleteFilter(
|
|
24
|
+
lookup: bson.Binary,
|
|
25
|
+
keys: mongo.Document[],
|
|
26
|
+
before: InternalOpId
|
|
27
|
+
): mongo.Document {
|
|
28
|
+
return {
|
|
29
|
+
lookup,
|
|
30
|
+
key: { $in: keys },
|
|
31
|
+
_id: { $lt: before }
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as lib_mongo from '@powersync/lib-service-mongodb';
|
|
2
2
|
import { Logger, ReplicationAbortedError } from '@powersync/lib-services-framework';
|
|
3
3
|
import { SingleSyncConfigBucketDefinitionMapping, storage } from '@powersync/service-core';
|
|
4
|
-
import { BucketDefinitionId,
|
|
4
|
+
import { BucketDefinitionId, EventDefinitionId, ParameterIndexId } from '@powersync/service-sync-rules';
|
|
5
5
|
import * as bson from 'bson';
|
|
6
6
|
import { clearCollectionInIdRanges, idPrefixFilter } from '../../../utils/util.js';
|
|
7
7
|
import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from './models.js';
|
|
14
14
|
import { ObjectStorage } from './object-storage/ObjectStorage.js';
|
|
15
15
|
import { ObjectStorageLifecycle } from './object-storage/ObjectStorageLifecycle.js';
|
|
16
|
+
import { ObjectStorageUsage } from './object-storage/ObjectStorageUsage.js';
|
|
16
17
|
|
|
17
18
|
type SyncConfigState = ReplicationStreamDocumentV3['sync_configs'][number];
|
|
18
19
|
|
|
@@ -46,8 +47,6 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
46
47
|
private readonly signal: AbortSignal | undefined;
|
|
47
48
|
private readonly logger: Logger;
|
|
48
49
|
private readonly objectStorage: ObjectStorage | undefined;
|
|
49
|
-
private readonly defaultSchema: string;
|
|
50
|
-
private readonly sourceConnectionTag: string;
|
|
51
50
|
private readonly clearBatchThrottleRate: number;
|
|
52
51
|
|
|
53
52
|
constructor(options: MongoStoppedSyncConfigCleanupOptions) {
|
|
@@ -56,8 +55,6 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
56
55
|
this.signal = options.signal;
|
|
57
56
|
this.logger = options.logger;
|
|
58
57
|
this.objectStorage = options.objectStorage;
|
|
59
|
-
this.defaultSchema = options.defaultSchema;
|
|
60
|
-
this.sourceConnectionTag = options.sourceConnectionTag;
|
|
61
58
|
this.clearBatchThrottleRate = options.clearBatchThrottleRate;
|
|
62
59
|
}
|
|
63
60
|
|
|
@@ -91,28 +88,32 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
91
88
|
liveStorageIds.bucketDefinitionIds
|
|
92
89
|
);
|
|
93
90
|
const unusedParameterIndexIds = difference(stoppedStorageIds.parameterIndexIds, liveStorageIds.parameterIndexIds);
|
|
91
|
+
const unusedEventDefinitionIds = difference(
|
|
92
|
+
stoppedStorageIds.eventDefinitionIds,
|
|
93
|
+
liveStorageIds.eventDefinitionIds
|
|
94
|
+
);
|
|
94
95
|
|
|
95
96
|
const result: storage.CleanupStoppedSyncConfigsResult = {
|
|
96
97
|
...EMPTY_RESULT
|
|
97
98
|
};
|
|
98
99
|
|
|
99
|
-
if (
|
|
100
|
+
if (
|
|
101
|
+
unusedBucketDefinitionIds.length > 0 ||
|
|
102
|
+
unusedParameterIndexIds.length > 0 ||
|
|
103
|
+
unusedEventDefinitionIds.length > 0
|
|
104
|
+
) {
|
|
100
105
|
await this.cleanupSourceTableMemberships(
|
|
101
106
|
unusedBucketDefinitionIds,
|
|
102
107
|
unusedParameterIndexIds,
|
|
103
|
-
|
|
108
|
+
unusedEventDefinitionIds,
|
|
104
109
|
result
|
|
105
110
|
);
|
|
111
|
+
await this.dropCustomCheckpointRequestCollections(unusedEventDefinitionIds);
|
|
106
112
|
result.bucketDataCollectionsDropped = await this.dropBucketDataCollections(unusedBucketDefinitionIds);
|
|
107
113
|
result.bucketStateDocumentsDeleted = await this.deleteBucketStateDocuments(unusedBucketDefinitionIds);
|
|
108
114
|
result.parameterIndexCollectionsDropped = await this.dropParameterIndexCollections(unusedParameterIndexIds);
|
|
109
115
|
}
|
|
110
116
|
|
|
111
|
-
// Event-only source tables carry empty membership arrays, so they are never selected by the
|
|
112
|
-
// membership filter above. Clean them up separately, regardless of whether any bucket or
|
|
113
|
-
// parameter ids became unused (a stopped config may have contributed only an event trigger).
|
|
114
|
-
await this.cleanupEventOnlySourceTables(liveConfigDocs, result);
|
|
115
|
-
|
|
116
117
|
result.stoppedSyncConfigsRemoved = await this.pruneStoppedSyncConfigStates(stoppedStates);
|
|
117
118
|
|
|
118
119
|
if (result.stoppedSyncConfigsRemoved > 0) {
|
|
@@ -132,20 +133,21 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
132
133
|
return this.db.syncConfigDefinitions.find({ _id: { $in: ids } }).toArray();
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
private storageIdsFor(configs:
|
|
136
|
+
private storageIdsFor(configs: SyncConfigDefinition[]) {
|
|
136
137
|
const mappings = configs.map((config) =>
|
|
137
138
|
SingleSyncConfigBucketDefinitionMapping.fromPersistedMapping(config.rule_mapping)
|
|
138
139
|
);
|
|
139
140
|
return {
|
|
140
141
|
bucketDefinitionIds: [...new Set(mappings.flatMap((mapping) => mapping.allBucketDefinitionIds()))],
|
|
141
|
-
parameterIndexIds: [...new Set(mappings.flatMap((mapping) => mapping.allParameterIndexIds()))]
|
|
142
|
+
parameterIndexIds: [...new Set(mappings.flatMap((mapping) => mapping.allParameterIndexIds()))],
|
|
143
|
+
eventDefinitionIds: [...new Set(mappings.flatMap((mapping) => mapping.allEventDefinitionIds()))]
|
|
142
144
|
};
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
private async cleanupSourceTableMemberships(
|
|
146
148
|
unusedBucketDefinitionIds: BucketDefinitionId[],
|
|
147
149
|
unusedParameterIndexIds: ParameterIndexId[],
|
|
148
|
-
|
|
150
|
+
unusedEventDefinitionIds: EventDefinitionId[],
|
|
149
151
|
result: storage.CleanupStoppedSyncConfigsResult
|
|
150
152
|
) {
|
|
151
153
|
const update: Record<string, unknown> = {};
|
|
@@ -155,11 +157,18 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
155
157
|
if (unusedParameterIndexIds.length > 0) {
|
|
156
158
|
update.parameter_lookup_source_ids = { $in: unusedParameterIndexIds };
|
|
157
159
|
}
|
|
160
|
+
if (unusedEventDefinitionIds.length > 0) {
|
|
161
|
+
update.event_definition_ids = { $in: unusedEventDefinitionIds };
|
|
162
|
+
}
|
|
158
163
|
|
|
159
164
|
// Keep obsolete membership ids as the durable cleanup marker until each source table is
|
|
160
165
|
// either deleted or retained. If interrupted after dropping a source_records collection,
|
|
161
166
|
// the next run can still rediscover the source table from these obsolete ids and retry.
|
|
162
|
-
const filter = this.sourceTableMembershipFilter(
|
|
167
|
+
const filter = this.sourceTableMembershipFilter(
|
|
168
|
+
unusedBucketDefinitionIds,
|
|
169
|
+
unusedParameterIndexIds,
|
|
170
|
+
unusedEventDefinitionIds
|
|
171
|
+
);
|
|
163
172
|
const candidateSourceTables = await this.db
|
|
164
173
|
.sourceTables(this.replicationStreamId)
|
|
165
174
|
.find(filter, {
|
|
@@ -167,6 +176,7 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
167
176
|
_id: 1,
|
|
168
177
|
bucket_data_source_ids: 1,
|
|
169
178
|
parameter_lookup_source_ids: 1,
|
|
179
|
+
event_definition_ids: 1,
|
|
170
180
|
schema_name: 1,
|
|
171
181
|
table_name: 1
|
|
172
182
|
}
|
|
@@ -175,12 +185,13 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
175
185
|
if (candidateSourceTables.length == 0) {
|
|
176
186
|
return;
|
|
177
187
|
}
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
188
|
+
const deletableSourceTables = candidateSourceTables.filter((sourceTable) =>
|
|
189
|
+
this.membershipsBecomeEmpty(
|
|
190
|
+
sourceTable,
|
|
191
|
+
unusedBucketDefinitionIds,
|
|
192
|
+
unusedParameterIndexIds,
|
|
193
|
+
unusedEventDefinitionIds
|
|
194
|
+
)
|
|
184
195
|
);
|
|
185
196
|
const retainedSourceTables = candidateSourceTables.filter(
|
|
186
197
|
(sourceTable) => !deletableSourceTables.some((deletable) => deletable._id.equals(sourceTable._id))
|
|
@@ -189,19 +200,24 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
189
200
|
|
|
190
201
|
await this.deleteSourceTables(
|
|
191
202
|
deletableSourceTables.map((table) => table._id),
|
|
192
|
-
(ids) =>
|
|
203
|
+
(ids) =>
|
|
204
|
+
this.deletableSourceTableFilter(
|
|
205
|
+
ids,
|
|
206
|
+
unusedBucketDefinitionIds,
|
|
207
|
+
unusedParameterIndexIds,
|
|
208
|
+
unusedEventDefinitionIds
|
|
209
|
+
),
|
|
193
210
|
result
|
|
194
211
|
);
|
|
195
212
|
|
|
196
|
-
// A retained source table whose memberships become empty is
|
|
197
|
-
// (
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
// data sync on the same doc and need current_data again.
|
|
213
|
+
// A retained source table whose data and parameter memberships become empty is event-only.
|
|
214
|
+
// Event-only save() never reads or writes current_data, so its source_records collection is now
|
|
215
|
+
// dead weight. Drop it before the $pull below, so the obsolete membership ids remain as a recovery
|
|
216
|
+
// marker if interrupted. Existing source-table docs only ever shrink their memberships, so this
|
|
217
|
+
// table cannot resume data sync on the same doc and need current_data again.
|
|
202
218
|
const becomingEventOnlySourceTableIds = retainedSourceTables
|
|
203
219
|
.filter((sourceTable) =>
|
|
204
|
-
this.
|
|
220
|
+
this.dataMembershipsBecomeEmpty(sourceTable, unusedBucketDefinitionIds, unusedParameterIndexIds)
|
|
205
221
|
)
|
|
206
222
|
.map((sourceTable) => sourceTable._id);
|
|
207
223
|
for (const sourceTableId of becomingEventOnlySourceTableIds) {
|
|
@@ -223,42 +239,6 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
223
239
|
}
|
|
224
240
|
}
|
|
225
241
|
|
|
226
|
-
/**
|
|
227
|
-
* Clean up event-only source tables that no live sync config still triggers events for.
|
|
228
|
-
*
|
|
229
|
-
* Event-only source tables carry empty membership arrays (see source-table-utils:
|
|
230
|
-
* "Empty memberships indicate an event-only table"), so the membership filter never selects
|
|
231
|
-
* them. Without this, the source_tables row and its source_records collection leak when the
|
|
232
|
-
* config that contributed the event trigger is stopped.
|
|
233
|
-
*/
|
|
234
|
-
private async cleanupEventOnlySourceTables(
|
|
235
|
-
liveConfigDocs: SyncConfigDefinition[],
|
|
236
|
-
result: storage.CleanupStoppedSyncConfigsResult
|
|
237
|
-
) {
|
|
238
|
-
const candidateSourceTables = await this.db
|
|
239
|
-
.sourceTables(this.replicationStreamId)
|
|
240
|
-
.find(this.eventOnlySourceTableFilter(), {
|
|
241
|
-
projection: {
|
|
242
|
-
_id: 1,
|
|
243
|
-
bucket_data_source_ids: 1,
|
|
244
|
-
parameter_lookup_source_ids: 1,
|
|
245
|
-
schema_name: 1,
|
|
246
|
-
table_name: 1
|
|
247
|
-
}
|
|
248
|
-
})
|
|
249
|
-
.toArray();
|
|
250
|
-
if (candidateSourceTables.length == 0) {
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const liveSyncConfigs = this.parseSyncConfigs(liveConfigDocs);
|
|
255
|
-
const deletableSourceTableIds = candidateSourceTables
|
|
256
|
-
.filter((sourceTable) => !this.triggersLiveEvent(sourceTable, liveSyncConfigs))
|
|
257
|
-
.map((sourceTable) => sourceTable._id);
|
|
258
|
-
|
|
259
|
-
await this.deleteSourceTables(deletableSourceTableIds, (ids) => this.eventOnlyDeletableFilter(ids), result);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
242
|
/**
|
|
263
243
|
* Drop the given source tables and their source_records collections.
|
|
264
244
|
*
|
|
@@ -298,6 +278,25 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
298
278
|
}
|
|
299
279
|
|
|
300
280
|
private membershipsBecomeEmpty(
|
|
281
|
+
sourceTable: Pick<
|
|
282
|
+
SourceTableDocumentV3,
|
|
283
|
+
'bucket_data_source_ids' | 'parameter_lookup_source_ids' | 'event_definition_ids'
|
|
284
|
+
>,
|
|
285
|
+
unusedBucketDefinitionIds: BucketDefinitionId[],
|
|
286
|
+
unusedParameterIndexIds: ParameterIndexId[],
|
|
287
|
+
unusedEventDefinitionIds: EventDefinitionId[]
|
|
288
|
+
): boolean {
|
|
289
|
+
const unusedBucketDefinitionIdSet = new Set(unusedBucketDefinitionIds);
|
|
290
|
+
const unusedParameterIndexIdSet = new Set(unusedParameterIndexIds);
|
|
291
|
+
const unusedEventDefinitionIdSet = new Set(unusedEventDefinitionIds);
|
|
292
|
+
return (
|
|
293
|
+
sourceTable.bucket_data_source_ids.every((id) => unusedBucketDefinitionIdSet.has(id)) &&
|
|
294
|
+
sourceTable.parameter_lookup_source_ids.every((id) => unusedParameterIndexIdSet.has(id)) &&
|
|
295
|
+
sourceTable.event_definition_ids.every((id) => unusedEventDefinitionIdSet.has(id))
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private dataMembershipsBecomeEmpty(
|
|
301
300
|
sourceTable: Pick<SourceTableDocumentV3, 'bucket_data_source_ids' | 'parameter_lookup_source_ids'>,
|
|
302
301
|
unusedBucketDefinitionIds: BucketDefinitionId[],
|
|
303
302
|
unusedParameterIndexIds: ParameterIndexId[]
|
|
@@ -313,57 +312,21 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
313
312
|
private deletableSourceTableFilter(
|
|
314
313
|
ids: bson.ObjectId[],
|
|
315
314
|
unusedBucketDefinitionIds: BucketDefinitionId[],
|
|
316
|
-
unusedParameterIndexIds: ParameterIndexId[]
|
|
315
|
+
unusedParameterIndexIds: ParameterIndexId[],
|
|
316
|
+
unusedEventDefinitionIds: EventDefinitionId[]
|
|
317
317
|
): Record<string, unknown> {
|
|
318
318
|
return {
|
|
319
319
|
_id: { $in: ids },
|
|
320
320
|
bucket_data_source_ids: { $not: { $elemMatch: { $nin: unusedBucketDefinitionIds } } },
|
|
321
|
-
parameter_lookup_source_ids: { $not: { $elemMatch: { $nin: unusedParameterIndexIds } } }
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
private eventOnlySourceTableFilter(): Record<string, unknown> {
|
|
326
|
-
return {
|
|
327
|
-
bucket_data_source_ids: { $size: 0 },
|
|
328
|
-
parameter_lookup_source_ids: { $size: 0 }
|
|
321
|
+
parameter_lookup_source_ids: { $not: { $elemMatch: { $nin: unusedParameterIndexIds } } },
|
|
322
|
+
event_definition_ids: { $not: { $elemMatch: { $nin: unusedEventDefinitionIds } } }
|
|
329
323
|
};
|
|
330
324
|
}
|
|
331
325
|
|
|
332
|
-
private eventOnlyDeletableFilter(ids: bson.ObjectId[]): Record<string, unknown> {
|
|
333
|
-
return {
|
|
334
|
-
_id: { $in: ids },
|
|
335
|
-
...this.eventOnlySourceTableFilter()
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
private parseSyncConfigs(configDocs: SyncConfigDefinition[]): SyncConfig[] {
|
|
340
|
-
// This is ugly - we should not need to re-parse to achieve this.
|
|
341
|
-
// Revisit persistence for this later.
|
|
342
|
-
return configDocs.map((config) => {
|
|
343
|
-
return storage.parsePersistedSyncConfigContent({
|
|
344
|
-
content: config.content,
|
|
345
|
-
compiledPlan: config.serialized_plan ?? null,
|
|
346
|
-
storageVersion: config.storage_version,
|
|
347
|
-
parseOptions: {
|
|
348
|
-
defaultSchema: this.defaultSchema
|
|
349
|
-
}
|
|
350
|
-
}).config;
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
private triggersLiveEvent(sourceTable: SourceTableDocumentV3, liveSyncConfigs: SyncConfig[]): boolean {
|
|
355
|
-
return liveSyncConfigs.some((syncConfig) =>
|
|
356
|
-
syncConfig.tableTriggersEvent({
|
|
357
|
-
connectionTag: this.sourceConnectionTag,
|
|
358
|
-
schema: sourceTable.schema_name,
|
|
359
|
-
name: sourceTable.table_name
|
|
360
|
-
})
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
|
|
364
326
|
private sourceTableMembershipFilter(
|
|
365
327
|
bucketDefinitionIds: BucketDefinitionId[],
|
|
366
|
-
parameterIndexIds: ParameterIndexId[]
|
|
328
|
+
parameterIndexIds: ParameterIndexId[],
|
|
329
|
+
eventDefinitionIds: EventDefinitionId[]
|
|
367
330
|
): Partial<SourceTableDocumentV3> | Record<string, unknown> {
|
|
368
331
|
const clauses: Record<string, unknown>[] = [];
|
|
369
332
|
if (bucketDefinitionIds.length > 0) {
|
|
@@ -372,12 +335,27 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
372
335
|
if (parameterIndexIds.length > 0) {
|
|
373
336
|
clauses.push({ parameter_lookup_source_ids: { $in: parameterIndexIds } });
|
|
374
337
|
}
|
|
338
|
+
if (eventDefinitionIds.length > 0) {
|
|
339
|
+
clauses.push({ event_definition_ids: { $in: eventDefinitionIds } });
|
|
340
|
+
}
|
|
375
341
|
if (clauses.length == 0) {
|
|
376
342
|
return { _id: { $exists: false } };
|
|
377
343
|
}
|
|
378
344
|
return { $or: clauses };
|
|
379
345
|
}
|
|
380
346
|
|
|
347
|
+
private async dropCustomCheckpointRequestCollections(eventDefinitionIds: EventDefinitionId[]) {
|
|
348
|
+
for (const eventId of eventDefinitionIds) {
|
|
349
|
+
this.throwIfAborted();
|
|
350
|
+
await this.dropCollection(
|
|
351
|
+
this.db.customCheckpointRequests({
|
|
352
|
+
replicationStreamId: this.replicationStreamId,
|
|
353
|
+
eventId
|
|
354
|
+
})
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
381
359
|
private async dropBucketDataCollections(definitionIds: BucketDefinitionId[]): Promise<number> {
|
|
382
360
|
const lifecycle = this.objectStorage
|
|
383
361
|
? new ObjectStorageLifecycle(this.db, this.replicationStreamId, this.objectStorage)
|
|
@@ -385,6 +363,19 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
385
363
|
for (const definitionId of definitionIds) {
|
|
386
364
|
this.throwIfAborted();
|
|
387
365
|
await this.dropCollection(this.db.bucketData(this.replicationStreamId, definitionId));
|
|
366
|
+
}
|
|
367
|
+
if (definitionIds.length > 0 && this.objectStorage != null) {
|
|
368
|
+
const usage = new ObjectStorageUsage(this.db, this.replicationStreamId);
|
|
369
|
+
await this.db.client.withSession((session) =>
|
|
370
|
+
session.withTransaction(async () => {
|
|
371
|
+
for (const definitionId of definitionIds) {
|
|
372
|
+
await usage.removeDefinition(definitionId, session);
|
|
373
|
+
}
|
|
374
|
+
})
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
for (const definitionId of definitionIds) {
|
|
378
|
+
this.throwIfAborted();
|
|
388
379
|
await lifecycle?.deletePrefix(lifecycle.definitionPrefix(definitionId), { signal: this.signal });
|
|
389
380
|
}
|
|
390
381
|
return definitionIds.length;
|
|
@@ -431,7 +422,7 @@ export class MongoStoppedSyncConfigCleanup {
|
|
|
431
422
|
collection: lib_mongo.mongo.Collection<T>
|
|
432
423
|
): Promise<void> {
|
|
433
424
|
await collection.drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS }).catch((error) => {
|
|
434
|
-
if (lib_mongo.
|
|
425
|
+
if (lib_mongo.isMongoNamespaceNotFoundError(error)) {
|
|
435
426
|
return;
|
|
436
427
|
}
|
|
437
428
|
throw error;
|