@powersync/service-module-mongodb-storage 0.23.2 → 0.24.1

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +201 -67
  3. package/dist/storage/MongoBucketStorage.d.ts +1 -0
  4. package/dist/storage/MongoBucketStorage.js +45 -83
  5. package/dist/storage/MongoBucketStorage.js.map +1 -1
  6. package/dist/storage/implementation/MongoBucketBatch.d.ts +60 -39
  7. package/dist/storage/implementation/MongoBucketBatch.js +123 -296
  8. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  9. package/dist/storage/implementation/MongoOpIdAllocator.d.ts +6 -1
  10. package/dist/storage/implementation/MongoOpIdAllocator.js +12 -5
  11. package/dist/storage/implementation/MongoOpIdAllocator.js.map +1 -1
  12. package/dist/storage/implementation/MongoParsedSyncConfigSet.d.ts +7 -0
  13. package/dist/storage/implementation/MongoParsedSyncConfigSet.js +28 -2
  14. package/dist/storage/implementation/MongoParsedSyncConfigSet.js.map +1 -1
  15. package/dist/storage/implementation/MongoReplicationOptions.d.ts +27 -0
  16. package/dist/storage/implementation/MongoReplicationOptions.js +26 -0
  17. package/dist/storage/implementation/MongoReplicationOptions.js.map +1 -0
  18. package/dist/storage/implementation/MongoSyncBucketStorage.js +2 -3
  19. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  20. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +15 -2
  21. package/dist/storage/implementation/MongoSyncRulesLock.js +36 -14
  22. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  23. package/dist/storage/implementation/common/BatchOperationBuffer.d.ts +79 -0
  24. package/dist/storage/implementation/common/BatchOperationBuffer.js +286 -0
  25. package/dist/storage/implementation/common/BatchOperationBuffer.js.map +1 -0
  26. package/dist/storage/implementation/common/PersistedBatch.d.ts +10 -103
  27. package/dist/storage/implementation/common/PersistedBatch.js +1 -291
  28. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -1
  29. package/dist/storage/implementation/common/PreparedPublication.d.ts +43 -0
  30. package/dist/storage/implementation/common/PreparedPublication.js +2 -0
  31. package/dist/storage/implementation/common/PreparedPublication.js.map +1 -0
  32. package/dist/storage/implementation/common/SourceRecordStore.d.ts +0 -2
  33. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +17 -13
  34. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +259 -18
  35. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -1
  36. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +21 -15
  37. package/dist/storage/implementation/v1/PersistedBatchV1.js +58 -24
  38. package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -1
  39. package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +0 -2
  40. package/dist/storage/implementation/v1/SourceRecordStoreV1.js +0 -3
  41. package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -1
  42. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +25 -14
  43. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +350 -23
  44. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -1
  45. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +1 -0
  46. package/dist/storage/implementation/v3/MongoCompactorV3.js +10 -4
  47. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -1
  48. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +39 -28
  49. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -1
  50. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +46 -24
  51. package/dist/storage/implementation/v3/PersistedBatchV3.js +159 -83
  52. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -1
  53. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +12 -0
  54. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +41 -13
  55. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -1
  56. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.d.ts +4 -0
  57. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js +4 -0
  58. package/dist/storage/implementation/v3/object-storage/BucketDataObjectStorage.js.map +1 -1
  59. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.d.ts +27 -2
  60. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js +61 -5
  61. package/dist/storage/implementation/v3/object-storage/ObjectStorageLifecycle.js.map +1 -1
  62. package/package.json +9 -10
  63. package/src/storage/MongoBucketStorage.ts +55 -122
  64. package/src/storage/implementation/MongoBucketBatch.ts +185 -392
  65. package/src/storage/implementation/MongoOpIdAllocator.ts +12 -5
  66. package/src/storage/implementation/MongoParsedSyncConfigSet.ts +40 -1
  67. package/src/storage/implementation/MongoReplicationOptions.ts +28 -0
  68. package/src/storage/implementation/MongoSyncBucketStorage.ts +2 -3
  69. package/src/storage/implementation/MongoSyncRulesLock.ts +39 -17
  70. package/src/storage/implementation/common/BatchOperationBuffer.ts +353 -0
  71. package/src/storage/implementation/common/PersistedBatch.ts +12 -390
  72. package/src/storage/implementation/common/PreparedPublication.ts +49 -0
  73. package/src/storage/implementation/common/SourceRecordStore.ts +0 -2
  74. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +339 -27
  75. package/src/storage/implementation/v1/PersistedBatchV1.ts +74 -32
  76. package/src/storage/implementation/v1/SourceRecordStoreV1.ts +0 -5
  77. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +430 -32
  78. package/src/storage/implementation/v3/MongoCompactorV3.ts +13 -4
  79. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +43 -29
  80. package/src/storage/implementation/v3/PersistedBatchV3.ts +209 -108
  81. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +45 -15
  82. package/src/storage/implementation/v3/object-storage/BucketDataObjectStorage.ts +9 -0
  83. package/src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts +79 -5
  84. package/test/src/checkpoint_cleanup.test.ts +75 -0
  85. package/test/src/concurrent_writers.test.ts +127 -53
  86. package/test/src/helpers/PublicationCommandMonitor.ts +119 -0
  87. package/test/src/helpers/concurrentWriter.mjs +2 -2
  88. package/test/src/invalid_sync_config.test.ts +9 -0
  89. package/test/src/object_storage_lifecycle.test.ts +72 -2
  90. package/test/src/prepared_publication.test.ts +388 -0
  91. package/test/src/replication_preparation.test.ts +829 -0
  92. package/test/src/replication_publication_baseline.test.ts +203 -0
  93. package/test/src/replication_publication_batching.test.ts +212 -0
  94. package/test/src/replication_uploads.test.ts +393 -0
  95. package/test/src/source_table_config.test.ts +4 -4
  96. package/test/src/storage_object_storage_inline_threshold.test.ts +1 -1
  97. package/test/src/storage_s3_compaction_lifecycle.test.ts +93 -1
  98. package/test/src/storage_sync.test.ts +101 -2
  99. package/tsconfig.tsbuildinfo +1 -1
@@ -1,3 +1,4 @@
1
+ import { mongo } from '@powersync/lib-service-mongodb';
1
2
  import { Logger } from '@powersync/lib-services-framework';
2
3
  import * as bson from 'bson';
3
4
  import { MongoWriteBatch } from '../../MongoWriteBatch.js';
@@ -5,6 +6,12 @@ import { VersionedPowerSyncMongoV3 } from '../VersionedPowerSyncMongoV3.js';
5
6
  import { BucketDataObjectStorage } from './BucketDataObjectStorage.js';
6
7
  import { ObjectStorage, ObjectStorageOperationOptions } from './ObjectStorage.js';
7
8
  export declare const OBJECT_STORAGE_UPLOAD_LEASE_MS: number;
9
+ /**
10
+ * Leave time for the bounded publication transaction and clock skew before
11
+ * cleanup can select a marker. Publication must finish within this margin after
12
+ * its eligibility check; checked marker deletion alone cannot protect an object
13
+ * from a cleanup worker that already selected an expired marker.
14
+ */
8
15
  export declare const OBJECT_STORAGE_PUBLICATION_SAFETY_MARGIN_MS: number;
9
16
  export declare const OBJECT_STORAGE_REFERENCE_GRACE_MS: number;
10
17
  export interface PreparedObjectStorageUpload {
@@ -12,6 +19,10 @@ export interface PreparedObjectStorageUpload {
12
19
  path: string;
13
20
  deleteAfter: Date;
14
21
  }
22
+ export interface ObjectStorageUploadPayload {
23
+ upload: PreparedObjectStorageUpload;
24
+ payload: Uint8Array;
25
+ }
15
26
  /**
16
27
  * Coordinates the MongoDB outbox used to make object-storage changes recoverable.
17
28
  *
@@ -35,10 +46,24 @@ export declare class ObjectStorageLifecycle {
35
46
  * Indicate that files will be uploaded to S3.
36
47
  *
37
48
  * These markers are expected to be removed using publishUploads(). If that does not happen within
38
- * the grace period, the files will be deleted.
49
+ * the grace period, the files will be deleted. Registration must be majority
50
+ * acknowledged before any PUT starts: losing a marker on failover would lose
51
+ * the only cleanup obligation for an unpublished object. A failed/ambiguous
52
+ * insert must therefore prevent uploads, even if some markers were inserted.
39
53
  */
40
- prepareUploads(paths: string[], now?: Date): Promise<PreparedObjectStorageUpload[]>;
54
+ prepareUploads(paths: string[], now?: Date, session?: mongo.ClientSession): Promise<PreparedObjectStorageUpload[]>;
41
55
  canPublish(upload: PreparedObjectStorageUpload, now?: Date): boolean;
56
+ /**
57
+ * Upload a durably registered generation, joining every started PUT on failure.
58
+ * The shared adapter limits concurrency; its queue must observe the same signal
59
+ * as an active request so no queued PUT can start after the protection deadline.
60
+ *
61
+ * Failed/ambiguous PUTs retain their original markers. S3's bounded request
62
+ * timeouts are far shorter than the one-hour orphan grace, including when a
63
+ * response is lost or the process dies. Cancellation does not prove an object
64
+ * is absent, so never delete an object or its marker on upload failure.
65
+ */
66
+ storeUploads(uploads: ObjectStorageUploadPayload[], options?: ObjectStorageOperationOptions): Promise<void>;
42
67
  /**
43
68
  * Remove markers for files that have finished uploading, and are now referenced from other documents.
44
69
  *
@@ -1,7 +1,14 @@
1
1
  import * as bson from 'bson';
2
2
  import { createHash, randomUUID } from 'node:crypto';
3
+ import { REPLICATION_WRITE_OPTIONS } from '../../MongoReplicationOptions.js';
3
4
  import { BucketDataObjectStorage } from './BucketDataObjectStorage.js';
4
5
  export const OBJECT_STORAGE_UPLOAD_LEASE_MS = 60 * 60 * 1000;
6
+ /**
7
+ * Leave time for the bounded publication transaction and clock skew before
8
+ * cleanup can select a marker. Publication must finish within this margin after
9
+ * its eligibility check; checked marker deletion alone cannot protect an object
10
+ * from a cleanup worker that already selected an expired marker.
11
+ */
5
12
  export const OBJECT_STORAGE_PUBLICATION_SAFETY_MARGIN_MS = 2 * 60 * 1000;
6
13
  export const OBJECT_STORAGE_REFERENCE_GRACE_MS = 15 * 60 * 1000;
7
14
  /**
@@ -50,23 +57,71 @@ export class ObjectStorageLifecycle {
50
57
  * Indicate that files will be uploaded to S3.
51
58
  *
52
59
  * These markers are expected to be removed using publishUploads(). If that does not happen within
53
- * the grace period, the files will be deleted.
60
+ * the grace period, the files will be deleted. Registration must be majority
61
+ * acknowledged before any PUT starts: losing a marker on failover would lose
62
+ * the only cleanup obligation for an unpublished object. A failed/ambiguous
63
+ * insert must therefore prevent uploads, even if some markers were inserted.
54
64
  */
55
- async prepareUploads(paths, now = new Date()) {
65
+ async prepareUploads(paths, now = new Date(), session) {
56
66
  const deleteAfter = new Date(now.getTime() + OBJECT_STORAGE_UPLOAD_LEASE_MS);
57
67
  const uploads = paths.map((path) => ({ markerId: new bson.ObjectId(), path, deleteAfter }));
58
- if (uploads.length) {
68
+ if (uploads.length > 0) {
59
69
  await this.db.pendingObjectStorageDeletes(this.replicationStreamId).insertMany(uploads.map((upload) => ({
60
70
  _id: upload.markerId,
61
71
  path: upload.path,
62
72
  delete_after: upload.deleteAfter
63
- })));
73
+ })), { ...REPLICATION_WRITE_OPTIONS, session });
64
74
  }
65
75
  return uploads;
66
76
  }
67
77
  canPublish(upload, now = new Date()) {
68
78
  return now.getTime() < upload.deleteAfter.getTime() - OBJECT_STORAGE_PUBLICATION_SAFETY_MARGIN_MS;
69
79
  }
80
+ /**
81
+ * Upload a durably registered generation, joining every started PUT on failure.
82
+ * The shared adapter limits concurrency; its queue must observe the same signal
83
+ * as an active request so no queued PUT can start after the protection deadline.
84
+ *
85
+ * Failed/ambiguous PUTs retain their original markers. S3's bounded request
86
+ * timeouts are far shorter than the one-hour orphan grace, including when a
87
+ * response is lost or the process dies. Cancellation does not prove an object
88
+ * is absent, so never delete an object or its marker on upload failure.
89
+ */
90
+ async storeUploads(uploads, options) {
91
+ if (uploads.length === 0) {
92
+ return;
93
+ }
94
+ const deadline = Math.min(...uploads.map(({ upload }) => upload.deleteAfter.getTime())) -
95
+ OBJECT_STORAGE_PUBLICATION_SAFETY_MARGIN_MS;
96
+ const remaining = deadline - Date.now();
97
+ if (remaining <= 0) {
98
+ throw new Error('Object storage upload lease expired before PUT');
99
+ }
100
+ const expiration = new AbortController();
101
+ const timer = setTimeout(() => expiration.abort(new Error('Object storage upload lease expired during PUT')), remaining);
102
+ timer.unref();
103
+ const signal = options?.signal ? AbortSignal.any([options.signal, expiration.signal]) : expiration.signal;
104
+ let results;
105
+ try {
106
+ results = await Promise.allSettled(uploads.map(async ({ upload, payload }) => {
107
+ signal.throwIfAborted();
108
+ // The timer handles time in the adapter's queue; this check also catches
109
+ // wall-clock changes before dispatch. Publication checks again later.
110
+ if (!this.canPublish(upload)) {
111
+ throw new Error(`Object storage upload lease expired for ${upload.path}`);
112
+ }
113
+ await this.bucketData.storeSerialized(upload.path, payload, { signal });
114
+ }));
115
+ }
116
+ finally {
117
+ clearTimeout(timer);
118
+ }
119
+ const failure = results.find((result) => result.status === 'rejected');
120
+ if (failure?.status === 'rejected') {
121
+ throw failure.reason;
122
+ }
123
+ signal.throwIfAborted();
124
+ }
70
125
  /**
71
126
  * Remove markers for files that have finished uploading, and are now referenced from other documents.
72
127
  *
@@ -82,8 +137,9 @@ export class ObjectStorageLifecycle {
82
137
  return;
83
138
  }
84
139
  writes.deleteMany(this.db.pendingObjectStorageDeletes(this.replicationStreamId), { _id: { $in: uploads.map((upload) => upload.markerId) } }, (deletedCount) => {
85
- if (deletedCount !== uploads.length)
140
+ if (deletedCount !== uploads.length) {
86
141
  throw new Error('Missing object storage publication markers');
142
+ }
87
143
  });
88
144
  }
89
145
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"ObjectStorageLifecycle.js","sourceRoot":"","sources":["../../../../../src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ObjectStorageLifecycle.js","sourceRoot":"","sources":["../../../../../src/storage/implementation/v3/object-storage/ObjectStorageLifecycle.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@powersync/service-module-mongodb-storage",
3
3
  "repository": "https://github.com/powersync-ja/powersync-service",
4
- "version": "0.23.2",
5
- "license": "FSL-1.1-ALv2",
4
+ "version": "0.24.1",
5
+ "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "publishConfig": {
8
8
  "access": "public"
@@ -17,19 +17,18 @@
17
17
  "@smithy/node-http-handler": "^4.11.3",
18
18
  "async-mutex": "^0.5.0",
19
19
  "bson": "^6.10.4",
20
- "ix": "^5.0.0",
21
20
  "lru-cache": "^10.2.2",
22
21
  "ts-codec": "^1.3.0",
23
22
  "uuid": "^14.0.0",
24
- "@powersync/lib-service-mongodb": "0.6.34",
25
- "@powersync/lib-services-framework": "0.10.4",
26
- "@powersync/service-core": "1.27.2",
27
- "@powersync/service-jsonbig": "0.17.13",
28
- "@powersync/service-sync-rules": "0.44.0",
29
- "@powersync/service-types": "0.20.0"
23
+ "@powersync/lib-service-mongodb": "0.6.36",
24
+ "@powersync/lib-services-framework": "0.10.5",
25
+ "@powersync/service-core": "1.28.1",
26
+ "@powersync/service-jsonbig": "0.17.14",
27
+ "@powersync/service-sync-rules": "0.45.0",
28
+ "@powersync/service-types": "0.21.0"
30
29
  },
31
30
  "devDependencies": {
32
- "@powersync/service-core-tests": "0.18.4"
31
+ "@powersync/service-core-tests": "0.18.5"
33
32
  },
34
33
  "scripts": {
35
34
  "build": "tsc -b",
@@ -652,6 +652,10 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
652
652
  *
653
653
  * Use in the same transaction that the replacement stream is created.
654
654
  */
655
+ async stopProcessingSyncConfigs(): Promise<void> {
656
+ await this.withTransaction((session) => this.stopExistingProcessingWork(session));
657
+ }
658
+
655
659
  private async stopExistingProcessingWork(session: mongo.ClientSession) {
656
660
  await this.db.sync_rules.updateMany(
657
661
  {
@@ -824,89 +828,45 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
824
828
  }
825
829
 
826
830
  async getStorageMetrics(): Promise<storage.StorageMetrics> {
827
- const ignoreNotExisting = (e: unknown) => {
828
- if (lib_mongo.isMongoNamespaceNotFoundError(e)) {
829
- // Collection doesn't exist - return 0
830
- return [{ storageStats: { size: 0 } }];
831
- } else {
832
- return Promise.reject(e);
833
- }
834
- };
835
-
836
- const aggregateStaticCollection = async <T extends mongo.Document>(collection: mongo.Collection<T>) => {
837
- // We check whether the collection exists before getting the statistics. This avoids repeated
838
- // errors in the MongoDB logs if the collection hasn't been created yet.
839
- const exists =
840
- (await this.db.db.listCollections({ name: collection.collectionName }, { nameOnly: true }).toArray()).length >
841
- 0;
842
- if (!exists) {
843
- return [{ storageStats: { size: 0 } }];
844
- }
845
-
846
- return collection
847
- .aggregate([
848
- {
849
- $collStats: {
850
- storageStats: {}
851
- }
831
+ // We check which collections exist before getting statistics to avoid repeated errors in
832
+ // MongoDB logs for collections that have not been created yet.
833
+ const collections = await this.db.db
834
+ .listCollections(
835
+ {
836
+ name: {
837
+ $regex: '^(bucket_data|bucket_parameters|current_data)$|^(bucket_data_|parameter_index_|source_records_)'
852
838
  }
853
- ])
854
- .toArray()
855
- .catch(ignoreNotExisting);
856
- };
857
-
858
- const operations_aggregate = await aggregateStaticCollection(this.db.bucket_data);
859
- const v3OperationCollections = await this.db.listBucketDataCollectionsV3();
860
- const v3_operation_aggregates = await Promise.all(
861
- v3OperationCollections.map((collection) =>
862
- collection
863
- .aggregate([
864
- {
865
- $collStats: {
866
- storageStats: {}
867
- }
868
- }
869
- ])
870
- .toArray()
871
- .catch(ignoreNotExisting)
872
- )
873
- );
874
-
875
- const parameters_aggregate = await aggregateStaticCollection(this.db.bucket_parameters);
876
-
877
- const v3ParameterCollections = await this.db.listAllParameterIndexCollectionsV3();
878
- const v3_parameter_aggregates = await Promise.all(
879
- v3ParameterCollections.map((collection) =>
880
- collection
881
- .aggregate([
882
- {
883
- $collStats: {
884
- storageStats: {}
885
- }
886
- }
887
- ])
888
- .toArray()
889
- .catch(ignoreNotExisting)
890
- )
891
- );
892
-
893
- const v1_source_record_aggregate = await aggregateStaticCollection(this.db.current_data);
894
-
895
- const v3SourceRecordCollections = await this.db.listAllSourceRecordCollectionsV3();
896
- const source_record_aggregates = await Promise.all(
897
- v3SourceRecordCollections.map((collection) =>
898
- collection
899
- .aggregate([
900
- {
901
- $collStats: {
902
- storageStats: {}
903
- }
904
- }
905
- ])
906
- .toArray()
907
- .catch(ignoreNotExisting)
839
+ },
840
+ { nameOnly: true }
908
841
  )
909
- );
842
+ .toArray();
843
+ const sizesByCollection = new Map<string, number>();
844
+ const statsPipeline: mongo.Document[] = [
845
+ { $collStats: { storageStats: {} } },
846
+ { $project: { ns: 1, size: '$storageStats.size' } }
847
+ ];
848
+ // MongoDB has a limit of 1000 stages per aggregation pipeline. Additionally, we want
849
+ // to avoid too much work per aggregation, so we limit the number of collections per batch.
850
+ const COLLECTIONS_PER_BATCH = 100;
851
+ // Query batches sequentially so metrics collection does not fill the connection pool on standby pods.
852
+ // If a collection disappears during aggregation, let the next metrics collection retry.
853
+ for (let offset = 0; offset < collections.length; offset += COLLECTIONS_PER_BATCH) {
854
+ const batch = collections.slice(offset, offset + COLLECTIONS_PER_BATCH);
855
+ const pipeline = [
856
+ ...statsPipeline,
857
+ ...batch.slice(1).map((collection) => ({
858
+ $unionWith: { coll: collection.name, pipeline: statsPipeline }
859
+ }))
860
+ ];
861
+ const stats = await this.db.db
862
+ .collection(batch[0].name)
863
+ .aggregate<{ ns: string; size: number | bigint }>(pipeline)
864
+ .toArray();
865
+ for (const stat of stats) {
866
+ const name = stat.ns.slice(this.db.db.databaseName.length + 1);
867
+ sizesByCollection.set(name, (sizesByCollection.get(name) ?? 0) + Number(stat.size));
868
+ }
869
+ }
910
870
 
911
871
  const v3StorageConfig = getMongoStorageConfig(storage.STORAGE_VERSION_3) as StorageConfig & {
912
872
  incrementalReprocessing: true;
@@ -918,29 +878,8 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
918
878
  .find({ storage_version: { $gte: storage.STORAGE_VERSION_3 } }, { projection: { _id: 1, sync_configs: 1 } })
919
879
  .toArray()) as unknown as Pick<ReplicationStreamDocumentV3, '_id' | 'sync_configs'>[];
920
880
 
921
- const collectionSizes = new Map<string, number>();
922
- const addCollectionSize = (
923
- collection: { collectionName: string },
924
- aggregate: { storageStats?: { size?: number | bigint } }[],
925
- prefix: string,
926
- sizes: Map<string, number>
927
- ) => {
928
- const match = collection.collectionName.match(new RegExp(`^${prefix}(\\d+)_(.+)$`));
929
- if (match == null) {
930
- return;
931
- }
932
- sizes.set(`${prefix}${match[1]}:${match[2]}`, Number(aggregate[0]?.storageStats?.size ?? 0));
933
- };
934
-
935
- v3OperationCollections.forEach((collection, index) =>
936
- addCollectionSize(collection, v3_operation_aggregates[index], 'bucket_data_', collectionSizes)
937
- );
938
- v3ParameterCollections.forEach((collection, index) =>
939
- addCollectionSize(collection, v3_parameter_aggregates[index], 'parameter_index_', collectionSizes)
940
- );
941
- v3SourceRecordCollections.forEach((collection, index) =>
942
- addCollectionSize(collection, source_record_aggregates[index], 'source_records_', collectionSizes)
943
- );
881
+ const sumCollectionSizesByPrefix = (prefix: string) =>
882
+ [...sizesByCollection].reduce((total, [name, size]) => total + (name.startsWith(prefix) ? size : 0), 0);
944
883
 
945
884
  const syncConfigIds = v3StreamDocs.flatMap((stream) => (stream.sync_configs ?? []).map((config) => config._id));
946
885
  const syncConfigDefinitions =
@@ -957,15 +896,13 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
957
896
  number,
958
897
  { _id: ObjectId; bucket_data_source_ids: string[]; parameter_lookup_source_ids: string[] }[]
959
898
  >();
960
- await Promise.all(
961
- v3StreamDocs.map(async (stream) => {
962
- const sourceTables = await v3Db
963
- .sourceTables(stream._id)
964
- .find({}, { projection: { _id: 1, bucket_data_source_ids: 1, parameter_lookup_source_ids: 1 } })
965
- .toArray();
966
- sourceTablesByStream.set(stream._id, sourceTables);
967
- })
968
- );
899
+ for (const stream of v3StreamDocs) {
900
+ const sourceTables = await v3Db
901
+ .sourceTables(stream._id)
902
+ .find({}, { projection: { _id: 1, bucket_data_source_ids: 1, parameter_lookup_source_ids: 1 } })
903
+ .toArray();
904
+ sourceTablesByStream.set(stream._id, sourceTables);
905
+ }
969
906
 
970
907
  const objectStorageSizeByDefinition = new Map<string, number>();
971
908
  for (const usage of objectStorageDefinitionUsage) {
@@ -976,7 +913,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
976
913
  }
977
914
 
978
915
  const sumCollectionSizes = (prefix: string, streamId: number, ids: ReadonlySet<string>) =>
979
- [...ids].reduce((total, id) => total + (collectionSizes.get(`${prefix}${streamId}:${id}`) ?? 0), 0);
916
+ [...ids].reduce((total, id) => total + (sizesByCollection.get(`${prefix}${streamId}_${id}`) ?? 0), 0);
980
917
 
981
918
  const syncConfigMetrics: storage.StorageSyncConfigMetrics[] = [];
982
919
  for (const stream of v3StreamDocs) {
@@ -999,7 +936,7 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
999
936
  ) {
1000
937
  return total;
1001
938
  }
1002
- return total + (collectionSizes.get(`source_records_${stream._id}:${sourceTable._id.toHexString()}`) ?? 0);
939
+ return total + (sizesByCollection.get(`source_records_${stream._id}_${sourceTable._id.toHexString()}`) ?? 0);
1003
940
  }, 0);
1004
941
 
1005
942
  syncConfigMetrics.push({
@@ -1022,15 +959,11 @@ export class MongoBucketStorage extends storage.BucketStorageFactory {
1022
959
  0n
1023
960
  );
1024
961
  return {
1025
- operations_size_bytes:
1026
- Number(operations_aggregate[0].storageStats.size) +
1027
- v3_operation_aggregates.reduce((total, aggregate) => total + Number(aggregate[0].storageStats.size), 0),
962
+ operations_size_bytes: (sizesByCollection.get('bucket_data') ?? 0) + sumCollectionSizesByPrefix('bucket_data_'),
1028
963
  parameters_size_bytes:
1029
- Number(parameters_aggregate[0].storageStats.size) +
1030
- v3_parameter_aggregates.reduce((total, aggregate) => total + Number(aggregate[0].storageStats.size), 0),
964
+ (sizesByCollection.get('bucket_parameters') ?? 0) + sumCollectionSizesByPrefix('parameter_index_'),
1031
965
  replication_size_bytes:
1032
- Number(v1_source_record_aggregate[0]?.storageStats?.size ?? 0) +
1033
- source_record_aggregates.reduce((total, aggregate) => total + Number(aggregate[0]?.storageStats?.size ?? 0), 0),
966
+ (sizesByCollection.get('current_data') ?? 0) + sumCollectionSizesByPrefix('source_records_'),
1034
967
  object_storage_size_bytes: Number(totalObjectStorageSize),
1035
968
  sync_config_metrics: syncConfigMetrics
1036
969
  };