@powersync/service-module-mongodb-storage 0.21.0 → 0.23.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 +43 -0
- package/dist/storage/MongoBucketStorage.d.ts +10 -1
- package/dist/storage/MongoBucketStorage.js +37 -7
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoBucketBatch.d.ts +24 -10
- package/dist/storage/implementation/MongoBucketBatch.js +134 -89
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +2 -0
- 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/MongoStorageProvider.js +4 -2
- package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +5 -0
- package/dist/storage/implementation/MongoSyncBucketStorage.js +17 -0
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/MongoSyncRulesLock.d.ts +26 -2
- package/dist/storage/implementation/MongoSyncRulesLock.js +38 -1
- 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.js +2 -2
- package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/common/PersistedBatch.d.ts +5 -4
- package/dist/storage/implementation/common/PersistedBatch.js +6 -4
- 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 +5 -0
- package/dist/storage/implementation/db.js +14 -0
- package/dist/storage/implementation/db.js.map +1 -1
- package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +5 -0
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js +149 -139
- package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +2 -1
- package/dist/storage/implementation/v1/MongoCompactorV1.js +23 -17
- package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -1
- package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +1 -1
- 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/v3/MongoBucketBatchV3.d.ts +6 -1
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js +148 -162
- package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +10 -4
- package/dist/storage/implementation/v3/MongoCompactorV3.js +175 -76
- package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
- package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +12 -8
- package/dist/storage/implementation/v3/PersistedBatchV3.js +25 -32
- package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
- package/dist/storage/implementation/v3/compact-utils.d.ts +3 -2
- package/dist/storage/implementation/v3/compact-utils.js +12 -5
- package/dist/storage/implementation/v3/compact-utils.js.map +1 -1
- package/dist/storage/implementation/v3/models.d.ts +1 -1
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +8 -1
- package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.d.ts +3 -3
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +7 -9
- package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.d.ts +3 -2
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js +4 -7
- package/dist/storage/implementation/v3/object-storage/ObjectStorageUsage.js.map +1 -1
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js +21 -3
- package/dist/storage/implementation/v3/object-storage/S3ObjectStorage.js.map +1 -1
- package/dist/types/types.d.ts +5 -0
- package/dist/types/types.js +12 -0
- package/dist/types/types.js.map +1 -1
- package/dist/utils/test-utils.d.ts +2 -1
- package/dist/utils/test-utils.js +5 -3
- package/dist/utils/test-utils.js.map +1 -1
- package/package.json +7 -7
- package/src/storage/MongoBucketStorage.ts +47 -8
- package/src/storage/implementation/MongoBucketBatch.ts +205 -111
- package/src/storage/implementation/MongoCompactor.ts +2 -0
- package/src/storage/implementation/MongoIdSequence.ts +21 -1
- package/src/storage/implementation/MongoOpIdAllocator.ts +105 -0
- package/src/storage/implementation/MongoStorageProvider.ts +12 -2
- package/src/storage/implementation/MongoSyncBucketStorage.ts +19 -0
- package/src/storage/implementation/MongoSyncRulesLock.ts +56 -3
- package/src/storage/implementation/MongoWriteBatch.ts +172 -0
- package/src/storage/implementation/MongoWriteCheckpointAPI.ts +4 -2
- package/src/storage/implementation/common/PersistedBatch.ts +12 -8
- package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +4 -0
- package/src/storage/implementation/db.ts +15 -0
- package/src/storage/implementation/models.ts +1 -0
- package/src/storage/implementation/v1/MongoBucketBatchV1.ts +167 -160
- package/src/storage/implementation/v1/MongoCompactorV1.ts +29 -26
- package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +1 -1
- package/src/storage/implementation/v1/PersistedBatchV1.ts +25 -30
- package/src/storage/implementation/v3/MongoBucketBatchV3.ts +217 -242
- package/src/storage/implementation/v3/MongoCompactorV3.ts +284 -108
- package/src/storage/implementation/v3/PersistedBatchV3.ts +36 -36
- package/src/storage/implementation/v3/compact-utils.ts +16 -7
- package/src/storage/implementation/v3/models.ts +1 -1
- package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +8 -1
- package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +11 -10
- package/src/storage/implementation/v3/object-storage/ObjectStorageUsage.ts +8 -9
- package/src/storage/implementation/v3/object-storage/S3ObjectStorage.ts +23 -3
- package/src/types/types.ts +18 -0
- package/src/utils/test-utils.ts +6 -3
- package/test/src/__snapshots__/storage.test.ts.snap +102 -2
- package/test/src/__snapshots__/storage_compacting.test.ts.snap +2 -2
- package/test/src/__snapshots__/storage_sync.test.ts.snap +12 -12
- package/test/src/chunk_compaction_config.test.ts +38 -0
- package/test/src/cleanup-stopped-sync-configs.test.ts +19 -13
- package/test/src/compact-utils.test.ts +33 -8
- package/test/src/concurrent_writers.test.ts +599 -0
- package/test/src/helpers/concurrentWriter.mjs +29 -0
- package/test/src/helpers/s3TestFactory.ts +12 -12
- package/test/src/object_storage_usage.test.ts +18 -5
- package/test/src/parameter_compacting_v1.test.ts +2 -2
- package/test/src/parameter_compacting_v3.test.ts +2 -2
- package/test/src/parameter_compaction_fence.test.ts +3 -3
- package/test/src/storage.test.ts +32 -33
- package/test/src/storage_compacting.test.ts +402 -24
- package/test/src/storage_object_storage_inline_threshold.test.ts +3 -3
- package/test/src/storage_s3_checksums.test.ts +4 -4
- package/test/src/storage_s3_compaction_lifecycle.test.ts +8 -8
- package/test/src/storage_s3_reading.test.ts +51 -5
- package/test/src/storage_s3_writing.test.ts +3 -3
- package/test/src/storage_sync.test.ts +52 -46
- package/test/src/storeCurrentData.test.ts +5 -5
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { mongo } from '@powersync/lib-service-mongodb';
|
|
2
2
|
import { ReplicationAssertionError, ServiceAssertionError } from '@powersync/lib-services-framework';
|
|
3
|
-
import { addChecksums, formatBytes, InternalOpId,
|
|
3
|
+
import { acquireSemaphoreAbortable, addChecksums, formatBytes, InternalOpId, utils } from '@powersync/service-core';
|
|
4
4
|
import { BucketDefinitionId } from '@powersync/service-sync-rules';
|
|
5
|
+
import { setImmediate } from 'node:timers/promises';
|
|
5
6
|
import { BucketDataDoc } from '../common/BucketDataDoc.js';
|
|
6
7
|
import { BucketDataKey } from '../models.js';
|
|
7
|
-
import { ConcurrentCompactionError, MongoCompactor } from '../MongoCompactor.js';
|
|
8
|
+
import { ConcurrentCompactionError, MongoCompactOptions, MongoCompactor } from '../MongoCompactor.js';
|
|
9
|
+
import { MongoWriteBatch } from '../MongoWriteBatch.js';
|
|
8
10
|
import { cacheKey } from '../OperationBatch.js';
|
|
9
11
|
import { loadBucketDataDocument, maxOpId, serializeBucketData } from './bucket-format.js';
|
|
10
12
|
import { BucketDataContextV3 } from './BucketDataContextV3.js';
|
|
@@ -14,6 +16,7 @@ import {
|
|
|
14
16
|
bucketStats,
|
|
15
17
|
BucketStatsWithChecksum,
|
|
16
18
|
chooseCompactionKind,
|
|
19
|
+
chooseRequestedCompactionKind,
|
|
17
20
|
combineAdjacentStats,
|
|
18
21
|
combineChunkStats,
|
|
19
22
|
CompactIntervalConfig,
|
|
@@ -24,7 +27,6 @@ import {
|
|
|
24
27
|
CompactTargetConfig,
|
|
25
28
|
emptyBucketStats,
|
|
26
29
|
firstUncompactedWrite,
|
|
27
|
-
forcedCompactionKind,
|
|
28
30
|
PendingCompactionGroup,
|
|
29
31
|
readCompactionBatch,
|
|
30
32
|
ScheduledCompactionOptions,
|
|
@@ -69,15 +71,17 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
69
71
|
readonly maxCompactFullIntervalMs: number;
|
|
70
72
|
readonly compactLeaseDurationMs: number;
|
|
71
73
|
readonly maxOpIdCap: InternalOpId | undefined;
|
|
74
|
+
readonly forceChunkCompaction: boolean;
|
|
72
75
|
private readonly objectStorageUsage: ObjectStorageUsage;
|
|
73
76
|
|
|
74
|
-
constructor(bucketStorage: MongoSyncBucketStorageV3, db: VersionedPowerSyncMongoV3, options:
|
|
77
|
+
constructor(bucketStorage: MongoSyncBucketStorageV3, db: VersionedPowerSyncMongoV3, options: MongoCompactOptions) {
|
|
75
78
|
super(bucketStorage, db, options);
|
|
76
79
|
this.minCompactChunkIntervalMs = options.minCompactChunkIntervalMs ?? DEFAULT_MIN_COMPACT_CHUNK_INTERVAL_MS;
|
|
77
80
|
this.minCompactFullIntervalMs = options.minCompactFullIntervalMs ?? DEFAULT_MIN_COMPACT_FULL_INTERVAL_MS;
|
|
78
81
|
this.maxCompactFullIntervalMs = options.maxCompactFullIntervalMs ?? DEFAULT_MAX_COMPACT_FULL_INTERVAL_MS;
|
|
79
82
|
this.compactLeaseDurationMs = options.compactLeaseDurationMs ?? DEFAULT_COMPACT_LEASE_DURATION_MS;
|
|
80
83
|
this.maxOpIdCap = options.maxOpId;
|
|
84
|
+
this.forceChunkCompaction = options.forceChunkCompaction ?? false;
|
|
81
85
|
this.objectStorageUsage = new ObjectStorageUsage(this.db, this.group_id, createObjectStorageUsageWriterId());
|
|
82
86
|
}
|
|
83
87
|
|
|
@@ -104,7 +108,7 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
104
108
|
// processes the work that existed when it started.
|
|
105
109
|
await this.compactScheduledBuckets({
|
|
106
110
|
dueAheadMs: DEFAULT_MIN_COMPACT_CHUNK_INTERVAL_MS,
|
|
107
|
-
|
|
111
|
+
requestedKind: CompactionKind.Chunks
|
|
108
112
|
});
|
|
109
113
|
} else {
|
|
110
114
|
await this.compactScheduledBuckets();
|
|
@@ -161,7 +165,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
161
165
|
* Batching specifically help to cover cases of many buckets where no compaction is required:
|
|
162
166
|
* Instead of sequentially claiming and then rescheduling a bucket, this handles it in bulk.
|
|
163
167
|
*
|
|
164
|
-
*
|
|
168
|
+
* Chunk merges overlap a bounded number of buckets. Full compaction stays
|
|
169
|
+
* sequential because its working set includes operation deduplication state.
|
|
165
170
|
*
|
|
166
171
|
* Any concurrent workers may read the same batch. Rescheduling filters out buckets handled
|
|
167
172
|
* by a concurrent worker or replication write, while buckets that do need compaction are
|
|
@@ -175,55 +180,72 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
175
180
|
// Writers derive next_compact_check from MongoDB's $$NOW. Use the same
|
|
176
181
|
// clock for the fixed job boundary so clock skew cannot exclude work at
|
|
177
182
|
// the exact initial-replication interval.
|
|
178
|
-
const
|
|
179
|
-
.aggregate<{ now: Date }>([{ $documents: [{}] }, { $project: { _id: 0, now: '$$NOW' } }])
|
|
180
|
-
.toArray();
|
|
183
|
+
const jobStartedAt = await this.readCompactionTime();
|
|
181
184
|
const dueBefore = new Date(jobStartedAt.getTime() + (options.dueAheadMs ?? 0));
|
|
182
|
-
const
|
|
185
|
+
const requestedKind = options.requestedKind;
|
|
183
186
|
const rescheduleNotBefore = new Date(dueBefore.getTime() + 1);
|
|
187
|
+
// Keep accounting documents bounded by workers, not buckets or scan batches.
|
|
188
|
+
const workerUsage = Array.from(
|
|
189
|
+
{ length: this.storage.factory.chunkCompactionConcurrency },
|
|
190
|
+
() => new ObjectStorageUsage(this.db, this.group_id, createObjectStorageUsageWriterId())
|
|
191
|
+
);
|
|
184
192
|
while (true) {
|
|
185
193
|
this.signal?.throwIfAborted();
|
|
186
194
|
const states = await this.findScheduledBucketBatch(dueBefore);
|
|
187
195
|
if (states.length == 0) {
|
|
188
196
|
break;
|
|
189
197
|
}
|
|
198
|
+
// Keep eligibility bounded by dueBefore, but classify with the current
|
|
199
|
+
// server time so buckets that age into full compaction can advance.
|
|
200
|
+
const batchStartedAt = await this.readCompactionTime();
|
|
190
201
|
|
|
191
202
|
const scheduled: {
|
|
192
203
|
state: BucketStateDocumentV3;
|
|
193
204
|
decision: CompactionDecision;
|
|
194
|
-
|
|
205
|
+
selectedKind: CompactionKind | null;
|
|
195
206
|
}[] = [];
|
|
196
207
|
for (const state of states) {
|
|
197
208
|
try {
|
|
198
209
|
scheduled.push({
|
|
199
210
|
state,
|
|
200
|
-
decision: chooseCompactionKind(state,
|
|
201
|
-
|
|
211
|
+
decision: chooseCompactionKind(state, batchStartedAt, this),
|
|
212
|
+
selectedKind: chooseRequestedCompactionKind(state, requestedKind, this)
|
|
202
213
|
});
|
|
203
214
|
} catch (error) {
|
|
204
215
|
await this.rescheduleFailedBucket(state, rescheduleNotBefore, error);
|
|
205
216
|
}
|
|
206
217
|
}
|
|
207
218
|
const noOpStates = scheduled.filter(
|
|
208
|
-
({ state, decision,
|
|
209
|
-
state.compact_lease == null && (
|
|
219
|
+
({ state, decision, selectedKind }) =>
|
|
220
|
+
state.compact_lease == null && (requestedKind == null ? decision.kind : selectedKind) == null
|
|
210
221
|
);
|
|
211
222
|
await this.rescheduleUnclaimedBuckets(noOpStates, rescheduleNotBefore);
|
|
212
223
|
|
|
213
|
-
|
|
214
|
-
|
|
224
|
+
const processBucket = async (
|
|
225
|
+
{ state, decision, selectedKind }: (typeof scheduled)[number],
|
|
226
|
+
objectStorageUsage: ObjectStorageUsage,
|
|
227
|
+
chunksOnly = false
|
|
228
|
+
) => {
|
|
229
|
+
const kind = requestedKind == null ? decision.kind : selectedKind;
|
|
215
230
|
if (state.compact_lease == null && kind == null) {
|
|
216
|
-
|
|
231
|
+
return;
|
|
217
232
|
}
|
|
218
233
|
|
|
219
234
|
try {
|
|
220
235
|
await using lease = await this.claimBucket({ _id: state._id, next_compact_check: { $lte: dueBefore } });
|
|
221
236
|
if (lease == null) {
|
|
222
|
-
|
|
237
|
+
return;
|
|
223
238
|
}
|
|
224
239
|
const claimedDecision = chooseCompactionKind(lease.state, lease.startedAt, this);
|
|
225
240
|
const claimedKind =
|
|
226
|
-
|
|
241
|
+
requestedKind == null
|
|
242
|
+
? claimedDecision.kind
|
|
243
|
+
: chooseRequestedCompactionKind(lease.state, requestedKind, this);
|
|
244
|
+
if (chunksOnly && claimedKind === CompactionKind.Full) {
|
|
245
|
+
// The decision changed after scanning. Release the lease without
|
|
246
|
+
// rescheduling; the next batch will classify it with a fresh timestamp.
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
227
249
|
if (claimedKind == null) {
|
|
228
250
|
await this.rescheduleClaimedBucket(lease, claimedDecision, rescheduleNotBefore);
|
|
229
251
|
} else if (this.isCompactionTargetCovered(lease.state, claimedKind)) {
|
|
@@ -231,7 +253,13 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
231
253
|
// already-published progress. Keep any newer work scheduled.
|
|
232
254
|
await this.rescheduleClaimedBucket(lease, claimedDecision, rescheduleNotBefore);
|
|
233
255
|
} else {
|
|
234
|
-
await this.compactClaimedBucket(
|
|
256
|
+
await this.compactClaimedBucket(
|
|
257
|
+
lease,
|
|
258
|
+
claimedKind,
|
|
259
|
+
claimedDecision,
|
|
260
|
+
rescheduleNotBefore,
|
|
261
|
+
objectStorageUsage
|
|
262
|
+
);
|
|
235
263
|
}
|
|
236
264
|
} catch (error) {
|
|
237
265
|
if (this.signal?.aborted) {
|
|
@@ -241,6 +269,87 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
241
269
|
}
|
|
242
270
|
await this.rescheduleFailedBucket(state, rescheduleNotBefore, error);
|
|
243
271
|
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const chunkBuckets = scheduled.filter(
|
|
275
|
+
({ decision, selectedKind }) => (requestedKind == null ? decision.kind : selectedKind) === CompactionKind.Chunks
|
|
276
|
+
);
|
|
277
|
+
const sequentialBuckets = scheduled.filter(
|
|
278
|
+
({ decision, selectedKind }) => (requestedKind == null ? decision.kind : selectedKind) !== CompactionKind.Chunks
|
|
279
|
+
);
|
|
280
|
+
await this.runChunkCompactionWorkers(chunkBuckets, workerUsage, (entry, usage) =>
|
|
281
|
+
processBucket(entry, usage, true)
|
|
282
|
+
);
|
|
283
|
+
// Full compaction cannot overlap chunk workers from this job, and only
|
|
284
|
+
// one full bucket is processed at a time.
|
|
285
|
+
for (const entry of sequentialBuckets) {
|
|
286
|
+
await processBucket(entry, this.objectStorageUsage);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Use MongoDB's clock, matching scheduling and lease timestamps. */
|
|
292
|
+
private async readCompactionTime(): Promise<Date> {
|
|
293
|
+
const [{ now }] = await this.db.db
|
|
294
|
+
.aggregate<{ now: Date }>([{ $documents: [{}] }, { $project: { _id: 0, now: '$$NOW' } }])
|
|
295
|
+
.toArray();
|
|
296
|
+
return now;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/** Process one scheduled batch with a fixed pool of workers. */
|
|
300
|
+
private async runChunkCompactionWorkers<T>(
|
|
301
|
+
buckets: readonly T[],
|
|
302
|
+
workerUsage: readonly ObjectStorageUsage[],
|
|
303
|
+
processBucket: (bucket: T, usage: ObjectStorageUsage) => Promise<void>
|
|
304
|
+
): Promise<void> {
|
|
305
|
+
const signal = this.signal;
|
|
306
|
+
let nextBucket = 0;
|
|
307
|
+
let failed = false;
|
|
308
|
+
|
|
309
|
+
const runWorker = async (usage: ObjectStorageUsage) => {
|
|
310
|
+
try {
|
|
311
|
+
while (!failed && nextBucket < buckets.length) {
|
|
312
|
+
// Taking an entry has no await, so each worker gets a different bucket.
|
|
313
|
+
// A worker takes another only after finishing its current bucket.
|
|
314
|
+
const bucket = buckets[nextBucket++];
|
|
315
|
+
|
|
316
|
+
// This pool bounds one job; the factory semaphore bounds all jobs together.
|
|
317
|
+
// Acquire before claiming the bucket lease, and hold until it is released.
|
|
318
|
+
const acquired = await acquireSemaphoreAbortable(this.storage.factory.chunkCompactionSlots, signal);
|
|
319
|
+
if (acquired === 'aborted') {
|
|
320
|
+
signal?.throwIfAborted();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const [, releaseSlot] = acquired;
|
|
324
|
+
try {
|
|
325
|
+
// A sibling may have failed while this worker waited for a slot.
|
|
326
|
+
if (failed) return;
|
|
327
|
+
signal?.throwIfAborted();
|
|
328
|
+
await processBucket(bucket, usage);
|
|
329
|
+
} finally {
|
|
330
|
+
releaseSlot();
|
|
331
|
+
}
|
|
332
|
+
// Let replication and other event-loop work run between buckets.
|
|
333
|
+
await setImmediate();
|
|
334
|
+
}
|
|
335
|
+
} catch (error) {
|
|
336
|
+
// Drain work already started, but do not let siblings start new buckets.
|
|
337
|
+
failed = true;
|
|
338
|
+
throw error;
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
// Concurrent transactions must not all increment the same usage document.
|
|
343
|
+
// Reuse one writer for each worker instead of creating one per bucket.
|
|
344
|
+
const workers = workerUsage.map(runWorker);
|
|
345
|
+
|
|
346
|
+
// Do not release the caller's replication lock or run cleanup while a
|
|
347
|
+
// sibling worker still owns a bucket lease or is finishing a replacement.
|
|
348
|
+
// Wait for every worker even on failure, then propagate the first error.
|
|
349
|
+
const results = await Promise.allSettled(workers);
|
|
350
|
+
for (const result of results) {
|
|
351
|
+
if (result.status === 'rejected') {
|
|
352
|
+
throw result.reason;
|
|
244
353
|
}
|
|
245
354
|
}
|
|
246
355
|
}
|
|
@@ -314,7 +423,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
314
423
|
lease: CompactionLease,
|
|
315
424
|
kind: CompactionKind,
|
|
316
425
|
decision: CompactionDecision,
|
|
317
|
-
rescheduleNotBefore?: Date
|
|
426
|
+
rescheduleNotBefore?: Date,
|
|
427
|
+
objectStorageUsage = this.objectStorageUsage
|
|
318
428
|
) {
|
|
319
429
|
const context = new CompactionContext(
|
|
320
430
|
lease,
|
|
@@ -324,7 +434,7 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
324
434
|
this.compactionTarget(lease.state)
|
|
325
435
|
);
|
|
326
436
|
lease.startRenewal();
|
|
327
|
-
await this.compactSingleBucket(context);
|
|
437
|
+
await this.compactSingleBucket(context, objectStorageUsage);
|
|
328
438
|
}
|
|
329
439
|
|
|
330
440
|
private async rescheduleClaimedBucket(lease: CompactionLease, decision: CompactionDecision, notBefore?: Date) {
|
|
@@ -360,12 +470,12 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
360
470
|
return new ObjectStorageLifecycle(this.db, this.group_id, this.storage.objectStorage);
|
|
361
471
|
}
|
|
362
472
|
|
|
363
|
-
private async compactSingleBucket(context: CompactionContext) {
|
|
473
|
+
private async compactSingleBucket(context: CompactionContext, objectStorageUsage: ObjectStorageUsage) {
|
|
364
474
|
if (context.kind == CompactionKind.Chunks) {
|
|
365
|
-
return this.compactSingleBucketChunks(context);
|
|
475
|
+
return this.compactSingleBucketChunks(context, objectStorageUsage);
|
|
366
476
|
}
|
|
367
477
|
|
|
368
|
-
return this.compactSingleBucketFully(context);
|
|
478
|
+
return this.compactSingleBucketFully(context, objectStorageUsage);
|
|
369
479
|
}
|
|
370
480
|
|
|
371
481
|
/**
|
|
@@ -374,7 +484,7 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
374
484
|
* update the persisted checksum state and to decide whether a group can fit
|
|
375
485
|
* in one chunk.
|
|
376
486
|
*/
|
|
377
|
-
private async compactSingleBucketChunks(context: CompactionContext) {
|
|
487
|
+
private async compactSingleBucketChunks(context: CompactionContext, objectStorageUsage: ObjectStorageUsage) {
|
|
378
488
|
const bucket = context.state._id.b;
|
|
379
489
|
const resolvedDefinitionId = context.state._id.d;
|
|
380
490
|
const bucketContext = new BucketDataContextV3(this.db, {
|
|
@@ -467,7 +577,14 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
467
577
|
|
|
468
578
|
const nextSize = pendingSize + doc.size;
|
|
469
579
|
if (pendingChunks.length > 0 && nextSize > DEFAULT_MAX_DOC_SIZE_BYTES) {
|
|
470
|
-
const groupStats = await this.flushChunkMerge(
|
|
580
|
+
const groupStats = await this.flushChunkMerge(
|
|
581
|
+
bucket,
|
|
582
|
+
pendingChunks,
|
|
583
|
+
collection,
|
|
584
|
+
dataContext,
|
|
585
|
+
bucketContext,
|
|
586
|
+
objectStorageUsage
|
|
587
|
+
);
|
|
471
588
|
compactedTail = combineAdjacentStats(compactedTail, groupStats);
|
|
472
589
|
pendingChunks = [];
|
|
473
590
|
pendingSize = 0;
|
|
@@ -484,7 +601,14 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
484
601
|
}
|
|
485
602
|
|
|
486
603
|
if (pendingChunks.length > 0) {
|
|
487
|
-
const groupStats = await this.flushChunkMerge(
|
|
604
|
+
const groupStats = await this.flushChunkMerge(
|
|
605
|
+
bucket,
|
|
606
|
+
pendingChunks,
|
|
607
|
+
collection,
|
|
608
|
+
dataContext,
|
|
609
|
+
bucketContext,
|
|
610
|
+
objectStorageUsage
|
|
611
|
+
);
|
|
488
612
|
compactedTail = combineAdjacentStats(compactedTail, groupStats);
|
|
489
613
|
}
|
|
490
614
|
|
|
@@ -518,44 +642,57 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
518
642
|
inputs: BucketDataDocumentV3[],
|
|
519
643
|
collection: mongo.Collection<BucketDataDocumentV3>,
|
|
520
644
|
context: { replicationStreamId: number; definitionId: string },
|
|
521
|
-
bucketContext: BucketDataContextV3
|
|
645
|
+
bucketContext: BucketDataContextV3,
|
|
646
|
+
objectStorageUsage: ObjectStorageUsage
|
|
522
647
|
): Promise<BucketStatsWithChecksum> {
|
|
523
648
|
if (inputs.length == 1) {
|
|
524
649
|
return statsForDocument(inputs[0]);
|
|
525
650
|
}
|
|
526
651
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
652
|
+
try {
|
|
653
|
+
this.signal?.throwIfAborted();
|
|
654
|
+
|
|
655
|
+
// The metadata scan deliberately excluded ops. Read inline payloads only
|
|
656
|
+
// for this merge group; object-storage payloads are fetched below using
|
|
657
|
+
// the same rule.
|
|
658
|
+
const inlineInputs = inputs.filter((input) => input.storage_ref == null);
|
|
659
|
+
if (inlineInputs.length > 0) {
|
|
660
|
+
const inlineDocuments = await collection
|
|
661
|
+
.find({ _id: { $in: inlineInputs.map((input) => input._id) } }, { projection: { _id: 1, ops: 1 } })
|
|
662
|
+
.toArray();
|
|
663
|
+
const opsById = new Map(inlineDocuments.map((document) => [document._id.o.toString(), document.ops]));
|
|
664
|
+
for (const input of inlineInputs) {
|
|
665
|
+
input.ops = opsById.get(input._id.o.toString());
|
|
666
|
+
}
|
|
538
667
|
}
|
|
539
|
-
|
|
540
|
-
await hydrateBucketDataDocuments(inputs, this.storage.objectStorage, { signal: this.signal });
|
|
668
|
+
await hydrateBucketDataDocuments(inputs, this.storage.objectStorage, { signal: this.signal });
|
|
541
669
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
670
|
+
const operations = inputs.flatMap((input) => Array.from(loadBucketDataDocument(context, input)));
|
|
671
|
+
const targetOp = inputs.reduce<InternalOpId | null>(
|
|
672
|
+
(maxTarget, input) => maxOpId(maxTarget, input.target_op),
|
|
673
|
+
null
|
|
674
|
+
);
|
|
675
|
+
const result = await this.flushCompactionGroup(
|
|
676
|
+
bucket,
|
|
677
|
+
{
|
|
678
|
+
inputs,
|
|
679
|
+
ops: operations,
|
|
680
|
+
changed: true,
|
|
681
|
+
targetOp
|
|
682
|
+
},
|
|
683
|
+
bucketContext,
|
|
684
|
+
context,
|
|
685
|
+
objectStorageUsage
|
|
686
|
+
);
|
|
687
|
+
return result.stats;
|
|
688
|
+
} finally {
|
|
689
|
+
// The scan batch also references these documents. Do not retain hydrated
|
|
690
|
+
// operations after finishing this merge group.
|
|
691
|
+
for (const input of inputs) {
|
|
692
|
+
delete input.ops;
|
|
693
|
+
}
|
|
694
|
+
await setImmediate();
|
|
695
|
+
}
|
|
559
696
|
}
|
|
560
697
|
|
|
561
698
|
private async finalizeCompactedBucket({
|
|
@@ -692,7 +829,7 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
692
829
|
};
|
|
693
830
|
}
|
|
694
831
|
|
|
695
|
-
private async compactSingleBucketFully(context: CompactionContext) {
|
|
832
|
+
private async compactSingleBucketFully(context: CompactionContext, objectStorageUsage: ObjectStorageUsage) {
|
|
696
833
|
const bucket = context.state._id.b;
|
|
697
834
|
const resolvedDefinitionId = context.state._id.d;
|
|
698
835
|
const bucketContext = new BucketDataContextV3(this.db, {
|
|
@@ -849,7 +986,13 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
849
986
|
};
|
|
850
987
|
} else {
|
|
851
988
|
const flushedGroup = pendingGroup;
|
|
852
|
-
const result = await this.flushCompactionGroup(
|
|
989
|
+
const result = await this.flushCompactionGroup(
|
|
990
|
+
bucket,
|
|
991
|
+
flushedGroup,
|
|
992
|
+
bucketContext,
|
|
993
|
+
dataContext,
|
|
994
|
+
objectStorageUsage
|
|
995
|
+
);
|
|
853
996
|
compactedStats = combineAdjacentStats(compactedStats, result.stats);
|
|
854
997
|
if (
|
|
855
998
|
lastNotPut != null &&
|
|
@@ -874,7 +1017,13 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
874
1017
|
}
|
|
875
1018
|
|
|
876
1019
|
if (pendingGroup != null) {
|
|
877
|
-
const result = await this.flushCompactionGroup(
|
|
1020
|
+
const result = await this.flushCompactionGroup(
|
|
1021
|
+
bucket,
|
|
1022
|
+
pendingGroup,
|
|
1023
|
+
bucketContext,
|
|
1024
|
+
dataContext,
|
|
1025
|
+
objectStorageUsage
|
|
1026
|
+
);
|
|
878
1027
|
compactedStats = combineAdjacentStats(compactedStats, result.stats);
|
|
879
1028
|
if (
|
|
880
1029
|
lastNotPut != null &&
|
|
@@ -900,7 +1049,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
900
1049
|
clearBoundary.documentId,
|
|
901
1050
|
bucketContext,
|
|
902
1051
|
collection,
|
|
903
|
-
dataContext
|
|
1052
|
+
dataContext,
|
|
1053
|
+
objectStorageUsage
|
|
904
1054
|
);
|
|
905
1055
|
totalOpCount += clearResult.opCountDiff;
|
|
906
1056
|
compactedStats = applyStatsReplacement(compactedStats, clearResult.before, clearResult.after);
|
|
@@ -934,7 +1084,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
934
1084
|
bucket: string,
|
|
935
1085
|
group: PendingCompactionGroup,
|
|
936
1086
|
bucketContext: BucketDataContextV3,
|
|
937
|
-
context: { replicationStreamId: number; definitionId: string }
|
|
1087
|
+
context: { replicationStreamId: number; definitionId: string },
|
|
1088
|
+
objectStorageUsage: ObjectStorageUsage
|
|
938
1089
|
): Promise<CompactionGroupResult> {
|
|
939
1090
|
if (group.inputs.length == 1 && !group.changed) {
|
|
940
1091
|
return {
|
|
@@ -987,10 +1138,19 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
987
1138
|
);
|
|
988
1139
|
}
|
|
989
1140
|
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1141
|
+
// Replacement documents can reuse deleted IDs, so retain delete-before-insert ordering.
|
|
1142
|
+
const writes = this.db.createWriteBatch(session, { ordered: true });
|
|
1143
|
+
writes.deleteMany(bucketContext.collection, { _id: { $in: idsToDelete } });
|
|
1144
|
+
writes.insertMany(bucketContext.collection, documents);
|
|
1145
|
+
this.finishObjectStorageReplacement(oldStoragePaths, newStoragePaths, uploads, writes);
|
|
1146
|
+
this.recordObjectStorageReplacement(
|
|
1147
|
+
oldStorageBytes,
|
|
1148
|
+
documents,
|
|
1149
|
+
context.definitionId,
|
|
1150
|
+
writes,
|
|
1151
|
+
objectStorageUsage
|
|
1152
|
+
);
|
|
1153
|
+
await writes.execute();
|
|
994
1154
|
},
|
|
995
1155
|
{
|
|
996
1156
|
writeConcern: { w: 'majority' },
|
|
@@ -1020,7 +1180,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1020
1180
|
boundaryDocId: BucketDataKey,
|
|
1021
1181
|
bucketContext: BucketDataContextV3,
|
|
1022
1182
|
collection: mongo.Collection<BucketDataDocumentV3 & { bsonSize?: number | bigint }>,
|
|
1023
|
-
context: { replicationStreamId: number; definitionId: string }
|
|
1183
|
+
context: { replicationStreamId: number; definitionId: string },
|
|
1184
|
+
objectStorageUsage: ObjectStorageUsage
|
|
1024
1185
|
): Promise<ClearCompactionResult> {
|
|
1025
1186
|
let opCountDiff = 0;
|
|
1026
1187
|
let before = emptyBucketStats();
|
|
@@ -1037,7 +1198,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1037
1198
|
boundaryDocId,
|
|
1038
1199
|
bucketContext,
|
|
1039
1200
|
collection,
|
|
1040
|
-
context
|
|
1201
|
+
context,
|
|
1202
|
+
objectStorageUsage
|
|
1041
1203
|
);
|
|
1042
1204
|
done = batch.done;
|
|
1043
1205
|
opCountDiff += batch.opCountDiff;
|
|
@@ -1053,7 +1215,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1053
1215
|
boundaryDocId,
|
|
1054
1216
|
bucketContext,
|
|
1055
1217
|
collection,
|
|
1056
|
-
context
|
|
1218
|
+
context,
|
|
1219
|
+
objectStorageUsage
|
|
1057
1220
|
);
|
|
1058
1221
|
opCountDiff += boundaryResult.opCountDiff;
|
|
1059
1222
|
before = combineAdjacentStats(before, boundaryResult.before);
|
|
@@ -1071,7 +1234,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1071
1234
|
boundaryDocId: BucketDataKey,
|
|
1072
1235
|
bucketContext: BucketDataContextV3,
|
|
1073
1236
|
collection: mongo.Collection<BucketDataDocumentV3 & { bsonSize?: number | bigint }>,
|
|
1074
|
-
context: { replicationStreamId: number; definitionId: string }
|
|
1237
|
+
context: { replicationStreamId: number; definitionId: string },
|
|
1238
|
+
objectStorageUsage: ObjectStorageUsage
|
|
1075
1239
|
): Promise<{ done: boolean; opCountDiff: number } & CompactionStatsReplacement> {
|
|
1076
1240
|
const bucket = bucketContext.key.bucket;
|
|
1077
1241
|
this.signal?.throwIfAborted();
|
|
@@ -1165,15 +1329,13 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1165
1329
|
|
|
1166
1330
|
prepared ??= await this.prepareCompactionUploads(bucket, context, [lastNotPut]);
|
|
1167
1331
|
this.logger.info(`Flushing CLEAR for ${clearedOpCount} ops at ${lastDocId?.o}`);
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
{ session }
|
|
1176
|
-
);
|
|
1332
|
+
const writes = this.db.createWriteBatch(session, { ordered: true });
|
|
1333
|
+
writes.deleteMany(collection, {
|
|
1334
|
+
_id: {
|
|
1335
|
+
$gte: bucketContext.minId,
|
|
1336
|
+
$lte: lastDocId!
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1177
1339
|
|
|
1178
1340
|
const clearOp = {
|
|
1179
1341
|
bucketKey: { ...context, bucket },
|
|
@@ -1185,9 +1347,16 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1185
1347
|
const persisted = await this.persistBucketData(bucket, [[clearOp]], context, prepared, {
|
|
1186
1348
|
targetOp: maxTargetOp
|
|
1187
1349
|
});
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1350
|
+
writes.insertOne(collection, persisted.documents[0]);
|
|
1351
|
+
this.finishObjectStorageReplacement(oldStoragePaths, persisted.storagePaths, persisted.uploads, writes);
|
|
1352
|
+
this.recordObjectStorageReplacement(
|
|
1353
|
+
oldStorageBytes,
|
|
1354
|
+
persisted.documents,
|
|
1355
|
+
context.definitionId,
|
|
1356
|
+
writes,
|
|
1357
|
+
objectStorageUsage
|
|
1358
|
+
);
|
|
1359
|
+
await writes.execute();
|
|
1191
1360
|
|
|
1192
1361
|
opCountDiff = -clearedOpCount + 1;
|
|
1193
1362
|
before = inputStats;
|
|
@@ -1208,7 +1377,8 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1208
1377
|
boundaryDocId: BucketDataKey,
|
|
1209
1378
|
bucketContext: BucketDataContextV3,
|
|
1210
1379
|
collection: mongo.Collection<BucketDataDocumentV3 & { bsonSize?: number | bigint }>,
|
|
1211
|
-
context: { replicationStreamId: number; definitionId: string }
|
|
1380
|
+
context: { replicationStreamId: number; definitionId: string },
|
|
1381
|
+
objectStorageUsage: ObjectStorageUsage
|
|
1212
1382
|
): Promise<ClearCompactionResult> {
|
|
1213
1383
|
const bucket = bucketContext.key.bucket;
|
|
1214
1384
|
this.signal?.throwIfAborted();
|
|
@@ -1307,15 +1477,13 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1307
1477
|
}
|
|
1308
1478
|
|
|
1309
1479
|
this.logger.info(`Flushing CLEAR for ${clearedOpCount} ops at ${lastNotPut}`);
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
{ session }
|
|
1318
|
-
);
|
|
1480
|
+
const writes = this.db.createWriteBatch(session, { ordered: true });
|
|
1481
|
+
writes.deleteMany(collection, {
|
|
1482
|
+
_id: {
|
|
1483
|
+
$gte: bucketContext.minId,
|
|
1484
|
+
$lte: boundaryDocId
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1319
1487
|
|
|
1320
1488
|
const clearOp = {
|
|
1321
1489
|
bucketKey: { ...context, bucket },
|
|
@@ -1333,9 +1501,16 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1333
1501
|
const persisted = await this.persistBucketData(bucket, chunks, context, prepared, {
|
|
1334
1502
|
targetOp: maxTargetOp ?? undefined
|
|
1335
1503
|
});
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1504
|
+
writes.insertMany(collection, persisted.documents);
|
|
1505
|
+
this.finishObjectStorageReplacement(oldStoragePaths, persisted.storagePaths, persisted.uploads, writes);
|
|
1506
|
+
this.recordObjectStorageReplacement(
|
|
1507
|
+
oldStorageBytes,
|
|
1508
|
+
persisted.documents,
|
|
1509
|
+
context.definitionId,
|
|
1510
|
+
writes,
|
|
1511
|
+
objectStorageUsage
|
|
1512
|
+
);
|
|
1513
|
+
await writes.execute();
|
|
1339
1514
|
|
|
1340
1515
|
opCountDiff = -clearedOpCount + 1;
|
|
1341
1516
|
before = inputStats;
|
|
@@ -1374,28 +1549,29 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1374
1549
|
}
|
|
1375
1550
|
|
|
1376
1551
|
/** Publish replacement uploads and retire superseded objects in the same transaction. */
|
|
1377
|
-
private
|
|
1552
|
+
private finishObjectStorageReplacement(
|
|
1378
1553
|
oldStoragePaths: Iterable<string>,
|
|
1379
1554
|
newStoragePaths: Set<string>,
|
|
1380
1555
|
uploads: PreparedObjectStorageUpload[],
|
|
1381
|
-
|
|
1382
|
-
):
|
|
1556
|
+
writes: MongoWriteBatch
|
|
1557
|
+
): void {
|
|
1383
1558
|
if (!this.storage.objectStorage) {
|
|
1384
1559
|
return;
|
|
1385
1560
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1561
|
+
this.objectStorageLifecycle.publishUploads(uploads, writes);
|
|
1562
|
+
this.objectStorageLifecycle.retire(
|
|
1388
1563
|
Array.from(oldStoragePaths).filter((path) => !newStoragePaths.has(path)),
|
|
1389
|
-
|
|
1564
|
+
writes
|
|
1390
1565
|
);
|
|
1391
1566
|
}
|
|
1392
1567
|
|
|
1393
|
-
private
|
|
1568
|
+
private recordObjectStorageReplacement(
|
|
1394
1569
|
oldBytes: bigint,
|
|
1395
1570
|
newDocuments: Iterable<Pick<BucketDataDocumentV3, 'storage_ref'>>,
|
|
1396
1571
|
definitionId: BucketDefinitionId,
|
|
1397
|
-
|
|
1398
|
-
|
|
1572
|
+
writes: MongoWriteBatch,
|
|
1573
|
+
objectStorageUsage: ObjectStorageUsage
|
|
1574
|
+
): void {
|
|
1399
1575
|
if (!this.storage.objectStorage) {
|
|
1400
1576
|
return;
|
|
1401
1577
|
}
|
|
@@ -1403,7 +1579,7 @@ export class MongoCompactorV3 extends MongoCompactor implements CompactIntervalC
|
|
|
1403
1579
|
for (const document of newDocuments) {
|
|
1404
1580
|
newBytes += ObjectStorageUsage.bytes(document);
|
|
1405
1581
|
}
|
|
1406
|
-
|
|
1582
|
+
objectStorageUsage.applyDelta(definitionId, newBytes - oldBytes, writes);
|
|
1407
1583
|
}
|
|
1408
1584
|
|
|
1409
1585
|
private async persistBucketData(
|