@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
|
@@ -6,6 +6,7 @@ import * as bson from 'bson';
|
|
|
6
6
|
import { BucketDataSource, BucketDefinitionId } from '@powersync/service-sync-rules';
|
|
7
7
|
import { mongoTableId } from '../../../utils/util.js';
|
|
8
8
|
import { EMPTY_DATA } from '../MongoBucketBatchShared.js';
|
|
9
|
+
import { MongoWriteBatch } from '../MongoWriteBatch.js';
|
|
9
10
|
import {
|
|
10
11
|
BucketStateUpdate,
|
|
11
12
|
PersistedBatch,
|
|
@@ -25,7 +26,9 @@ import {
|
|
|
25
26
|
export class PersistedBatchV1 extends PersistedBatch {
|
|
26
27
|
declare protected readonly db: VersionedPowerSyncMongoV1;
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
// Each upsert supplies the complete source-record state. Only its final
|
|
30
|
+
// state matters within a flush; bucket and parameter history is kept separately.
|
|
31
|
+
currentData = new Map<string, mongo.AnyBulkWriteOperation<CurrentDataDocument>>();
|
|
29
32
|
|
|
30
33
|
protected checkDefinitionId(_definitionId: BucketDefinitionId | null): BucketDefinitionId {
|
|
31
34
|
// V1 storage doesn't persist the id, and we don't use it.
|
|
@@ -94,7 +97,7 @@ export class PersistedBatchV1 extends PersistedBatch {
|
|
|
94
97
|
}
|
|
95
98
|
|
|
96
99
|
hardDeleteCurrentData(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId) {
|
|
97
|
-
this.currentData.
|
|
100
|
+
this.currentData.set(this.currentDataKey(sourceTableId, replicaId), {
|
|
98
101
|
deleteOne: {
|
|
99
102
|
filter: { _id: this.currentDataId(sourceTableId, replicaId) }
|
|
100
103
|
}
|
|
@@ -124,7 +127,7 @@ export class PersistedBatchV1 extends PersistedBatch {
|
|
|
124
127
|
return lookup.lookup;
|
|
125
128
|
});
|
|
126
129
|
|
|
127
|
-
this.currentData.
|
|
130
|
+
this.currentData.set(this.currentDataKey(values.sourceTableId, values.replicaId), {
|
|
128
131
|
updateOne: {
|
|
129
132
|
filter: { _id: this.currentDataId(values.sourceTableId, values.replicaId) },
|
|
130
133
|
update: {
|
|
@@ -141,57 +144,45 @@ export class PersistedBatchV1 extends PersistedBatch {
|
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
protected get currentDataCount() {
|
|
144
|
-
return this.currentData.
|
|
147
|
+
return this.currentData.size;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
protected async
|
|
148
|
-
|
|
150
|
+
protected async queueBucketData(writes: MongoWriteBatch) {
|
|
151
|
+
writes.bulkWriteUnordered(
|
|
152
|
+
this.db.bucketDataV1,
|
|
149
153
|
this.bucketData.map((document) => ({
|
|
150
154
|
insertOne: {
|
|
151
155
|
document: serializeBucketDataV1(document)
|
|
152
156
|
}
|
|
153
|
-
}))
|
|
154
|
-
{
|
|
155
|
-
session,
|
|
156
|
-
ordered: false
|
|
157
|
-
}
|
|
157
|
+
}))
|
|
158
158
|
);
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
protected
|
|
162
|
-
|
|
161
|
+
protected queueBucketParameters(writes: MongoWriteBatch): void {
|
|
162
|
+
writes.bulkWriteUnordered(
|
|
163
|
+
this.db.parameterIndexV1,
|
|
163
164
|
this.bucketParameters.map((document) => ({
|
|
164
165
|
insertOne: {
|
|
165
166
|
document: taggedBucketParameterDocumentToV1(document)
|
|
166
167
|
}
|
|
167
|
-
}))
|
|
168
|
-
{
|
|
169
|
-
session,
|
|
170
|
-
ordered: false
|
|
171
|
-
}
|
|
168
|
+
}))
|
|
172
169
|
);
|
|
173
170
|
}
|
|
174
171
|
|
|
175
|
-
protected
|
|
176
|
-
if (this.currentData.
|
|
172
|
+
protected queueCurrentData(writes: MongoWriteBatch): void {
|
|
173
|
+
if (this.currentData.size == 0) {
|
|
177
174
|
return;
|
|
178
175
|
}
|
|
179
176
|
|
|
180
|
-
|
|
181
|
-
session,
|
|
182
|
-
ordered: true
|
|
183
|
-
});
|
|
177
|
+
writes.bulkWriteUnordered(this.db.sourceRecordsV1, [...this.currentData.values()]);
|
|
184
178
|
}
|
|
185
179
|
|
|
186
|
-
protected
|
|
187
|
-
|
|
188
|
-
session,
|
|
189
|
-
ordered: false
|
|
190
|
-
});
|
|
180
|
+
protected queueBucketStates(writes: MongoWriteBatch): void {
|
|
181
|
+
writes.bulkWriteUnordered(this.db.bucketStateV1, this.getBucketStateUpdates());
|
|
191
182
|
}
|
|
192
183
|
|
|
193
184
|
protected resetCurrentData() {
|
|
194
|
-
this.currentData
|
|
185
|
+
this.currentData.clear();
|
|
195
186
|
}
|
|
196
187
|
|
|
197
188
|
private getBucketStateUpdates(): mongo.AnyBulkWriteOperation<BucketStateDocumentV1>[] {
|
|
@@ -219,6 +210,10 @@ export class PersistedBatchV1 extends PersistedBatch {
|
|
|
219
210
|
});
|
|
220
211
|
}
|
|
221
212
|
|
|
213
|
+
private currentDataKey(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId): string {
|
|
214
|
+
return Buffer.from(bson.serialize({ t: sourceTableId, k: replicaId })).toString('base64');
|
|
215
|
+
}
|
|
216
|
+
|
|
222
217
|
private currentDataId(sourceTableId: bson.ObjectId, replicaId: storage.ReplicaId): SourceKey {
|
|
223
218
|
return {
|
|
224
219
|
g: this.group_id,
|
|
@@ -48,6 +48,7 @@ export function serializeBucketDataV1(document: BucketDataDoc): BucketDataDocume
|
|
|
48
48
|
op: document.op,
|
|
49
49
|
source_table: document.source_table,
|
|
50
50
|
source_key: document.source_key,
|
|
51
|
+
subkey: document.subkey,
|
|
51
52
|
table: document.table,
|
|
52
53
|
row_id: document.row_id,
|
|
53
54
|
checksum: document.checksum,
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
|
+
import { InternalOpId } from '@powersync/service-core';
|
|
3
|
+
import { CompactionLeaseLostError } from '../MongoCompactor.js';
|
|
4
|
+
import { BucketStateDocumentV3 } from './models.js';
|
|
5
|
+
|
|
6
|
+
const LEASE_RENEW_INTERVAL_MS = 60 * 1000;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Filters for buckets that have no lease or an expired lease.
|
|
10
|
+
*/
|
|
11
|
+
export const AVAILABLE_LEASE_EXPR = {
|
|
12
|
+
// $$NOW is evaluated by MongoDB, avoiding lease expiry races due
|
|
13
|
+
// to clocks on separate compact workers.
|
|
14
|
+
$expr: {
|
|
15
|
+
$or: [{ $eq: [{ $type: '$compact_lease' }, 'missing'] }, { $lte: ['$compact_lease.expires_at', '$$NOW'] }]
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Owns one V3 bucket-compaction lease, including its server-time renewal and
|
|
21
|
+
* the owner-fenced operations which release it.
|
|
22
|
+
*
|
|
23
|
+
* This is intended as a way to reduce redundant work if multiple jobs attempt to
|
|
24
|
+
* compact the same bucket concurrently, but it's not an absolute safety guarantee.
|
|
25
|
+
* The individual operations must still be designed to be safe with concurrent compacting.
|
|
26
|
+
*/
|
|
27
|
+
export class CompactionLease implements AsyncDisposable {
|
|
28
|
+
readonly startedAt: Date;
|
|
29
|
+
readonly lastOp: InternalOpId;
|
|
30
|
+
|
|
31
|
+
private timer: NodeJS.Timeout | undefined;
|
|
32
|
+
private renewalInFlight = false;
|
|
33
|
+
private renewalError: unknown;
|
|
34
|
+
private finalizing = false;
|
|
35
|
+
private finished = false;
|
|
36
|
+
|
|
37
|
+
private constructor(
|
|
38
|
+
private readonly collection: mongo.Collection<BucketStateDocumentV3>,
|
|
39
|
+
readonly state: BucketStateDocumentV3,
|
|
40
|
+
readonly id: mongo.ObjectId,
|
|
41
|
+
private readonly durationMs: number
|
|
42
|
+
) {
|
|
43
|
+
this.startedAt = new Date(state.compact_lease!.expires_at.getTime() - durationMs);
|
|
44
|
+
this.lastOp = state.last_op;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
static async claim(
|
|
48
|
+
collection: mongo.Collection<BucketStateDocumentV3>,
|
|
49
|
+
filter: mongo.Filter<BucketStateDocumentV3>,
|
|
50
|
+
sort: mongo.Sort | undefined,
|
|
51
|
+
durationMs: number
|
|
52
|
+
): Promise<CompactionLease | null> {
|
|
53
|
+
const id = new mongo.ObjectId();
|
|
54
|
+
const state = await collection.findOneAndUpdate(
|
|
55
|
+
{
|
|
56
|
+
$and: [filter, AVAILABLE_LEASE_EXPR]
|
|
57
|
+
},
|
|
58
|
+
[
|
|
59
|
+
{
|
|
60
|
+
$set: {
|
|
61
|
+
compact_lease: {
|
|
62
|
+
id,
|
|
63
|
+
expires_at: { $dateAdd: { startDate: '$$NOW', unit: 'millisecond', amount: durationMs } }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
{ sort, returnDocument: 'after' }
|
|
69
|
+
);
|
|
70
|
+
return state == null ? null : new CompactionLease(collection, state, id, durationMs);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
startRenewal() {
|
|
74
|
+
const interval = Math.max(1, Math.min(LEASE_RENEW_INTERVAL_MS, Math.floor(this.durationMs / 2)));
|
|
75
|
+
this.timer = setInterval(() => {
|
|
76
|
+
if (this.renewalInFlight) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.renewalInFlight = true;
|
|
80
|
+
void this.renew()
|
|
81
|
+
.catch((error) => {
|
|
82
|
+
this.renewalError = error;
|
|
83
|
+
})
|
|
84
|
+
.finally(() => {
|
|
85
|
+
this.renewalInFlight = false;
|
|
86
|
+
});
|
|
87
|
+
}, interval);
|
|
88
|
+
this.timer.unref();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async throwIfLost() {
|
|
92
|
+
if (this.renewalError != null) {
|
|
93
|
+
throw this.renewalError;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async reschedule(nextCompactCheck: mongo.Document) {
|
|
98
|
+
await this.finish([{ $set: { next_compact_check: nextCompactCheck } }, { $unset: 'compact_lease' }]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async finalize(update: mongo.Document) {
|
|
102
|
+
await this.finish([{ $set: update }, { $unset: 'compact_lease' }]);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async [Symbol.asyncDispose]() {
|
|
106
|
+
if (this.timer != null) {
|
|
107
|
+
clearInterval(this.timer);
|
|
108
|
+
this.timer = undefined;
|
|
109
|
+
}
|
|
110
|
+
if (this.finished) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.finalizing = true;
|
|
114
|
+
await this.collection.updateOne(this.filter, { $unset: { compact_lease: '' } });
|
|
115
|
+
this.finished = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private get filter(): mongo.Filter<BucketStateDocumentV3> {
|
|
119
|
+
return { _id: this.state._id, 'compact_lease.id': this.id };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async renew() {
|
|
123
|
+
const result = await this.collection.updateOne(this.filter, [
|
|
124
|
+
{
|
|
125
|
+
$set: {
|
|
126
|
+
'compact_lease.expires_at': {
|
|
127
|
+
$dateAdd: { startDate: '$$NOW', unit: 'millisecond', amount: this.durationMs }
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
]);
|
|
132
|
+
// A finalization in progress owns the authoritative fenced result.
|
|
133
|
+
if (result.matchedCount != 1 && !this.finalizing) {
|
|
134
|
+
throw new CompactionLeaseLostError(`Lost compaction lease for bucket ${this.state._id.b}`);
|
|
135
|
+
}
|
|
136
|
+
// A successful renewal confirms that a transient failure has passed. A
|
|
137
|
+
// lease-loss error remains sticky: it means another worker may own it.
|
|
138
|
+
if (!(this.renewalError instanceof CompactionLeaseLostError)) {
|
|
139
|
+
this.renewalError = undefined;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private async finish(update: mongo.Document[]) {
|
|
144
|
+
await this.throwIfLost();
|
|
145
|
+
this.finalizing = true;
|
|
146
|
+
const result = await this.collection.updateOne(this.filter, update);
|
|
147
|
+
if (result.matchedCount != 1) {
|
|
148
|
+
throw new CompactionLeaseLostError(`Lost compaction lease for bucket ${this.state._id.b}`);
|
|
149
|
+
}
|
|
150
|
+
this.finished = true;
|
|
151
|
+
}
|
|
152
|
+
}
|