@powersync/service-module-mongodb-storage 0.0.0-dev-20260313100403 → 0.0.0-dev-20260515144844

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 (207) hide show
  1. package/CHANGELOG.md +96 -8
  2. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js +1 -1
  3. package/dist/migrations/db/migrations/1688556755264-initial-sync-rules.js.map +1 -1
  4. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +3 -3
  5. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  6. package/dist/migrations/db/migrations/1770213298299-storage-version.js.map +1 -1
  7. package/dist/storage/MongoBucketStorage.d.ts +5 -3
  8. package/dist/storage/MongoBucketStorage.js +50 -36
  9. package/dist/storage/MongoBucketStorage.js.map +1 -1
  10. package/dist/storage/MongoReportStorage.d.ts +3 -3
  11. package/dist/storage/MongoReportStorage.js +5 -5
  12. package/dist/storage/MongoReportStorage.js.map +1 -1
  13. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +17 -0
  14. package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
  15. package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
  16. package/dist/storage/implementation/MongoBucketBatch.d.ts +16 -14
  17. package/dist/storage/implementation/MongoBucketBatch.js +80 -115
  18. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  19. package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
  20. package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
  21. package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
  22. package/dist/storage/implementation/MongoChecksums.d.ts +28 -17
  23. package/dist/storage/implementation/MongoChecksums.js +13 -72
  24. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  25. package/dist/storage/implementation/MongoCompactor.d.ts +98 -52
  26. package/dist/storage/implementation/MongoCompactor.js +256 -295
  27. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  28. package/dist/storage/implementation/MongoParameterCompactor.d.ts +11 -6
  29. package/dist/storage/implementation/MongoParameterCompactor.js +24 -9
  30. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  31. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
  32. package/dist/storage/implementation/MongoPersistedSyncRules.js +64 -0
  33. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
  34. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +3 -0
  35. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +9 -0
  36. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
  37. package/dist/storage/implementation/MongoStorageProvider.js +1 -1
  38. package/dist/storage/implementation/MongoStorageProvider.js.map +1 -1
  39. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +49 -30
  40. package/dist/storage/implementation/MongoSyncBucketStorage.js +100 -456
  41. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  42. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +5 -3
  43. package/dist/storage/implementation/MongoSyncRulesLock.js +12 -10
  44. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  45. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
  46. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  47. package/dist/storage/implementation/OperationBatch.js +1 -1
  48. package/dist/storage/implementation/common/BucketDataDoc.d.ts +35 -0
  49. package/dist/storage/implementation/common/BucketDataDoc.js +2 -0
  50. package/dist/storage/implementation/common/BucketDataDoc.js.map +1 -0
  51. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +13 -0
  52. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +2 -0
  53. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +1 -0
  54. package/dist/storage/implementation/common/PersistedBatch.d.ts +108 -0
  55. package/dist/storage/implementation/common/PersistedBatch.js +237 -0
  56. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -0
  57. package/dist/storage/implementation/common/SingleBucketStore.d.ts +54 -0
  58. package/dist/storage/implementation/common/SingleBucketStore.js +3 -0
  59. package/dist/storage/implementation/common/SingleBucketStore.js.map +1 -0
  60. package/dist/storage/implementation/common/SourceRecordStore.d.ts +36 -0
  61. package/dist/storage/implementation/common/SourceRecordStore.js +2 -0
  62. package/dist/storage/implementation/common/SourceRecordStore.js.map +1 -0
  63. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +27 -0
  64. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +57 -0
  65. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -0
  66. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +7 -0
  67. package/dist/storage/implementation/createMongoSyncBucketStorage.js +9 -0
  68. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -0
  69. package/dist/storage/implementation/db.d.ts +34 -34
  70. package/dist/storage/implementation/db.js +78 -98
  71. package/dist/storage/implementation/db.js.map +1 -1
  72. package/dist/storage/implementation/models.d.ts +65 -36
  73. package/dist/storage/implementation/models.js +21 -2
  74. package/dist/storage/implementation/models.js.map +1 -1
  75. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +13 -0
  76. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +22 -0
  77. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -0
  78. package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +12 -0
  79. package/dist/storage/implementation/v1/MongoChecksumsV1.js +56 -0
  80. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -0
  81. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +23 -0
  82. package/dist/storage/implementation/v1/MongoCompactorV1.js +52 -0
  83. package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -0
  84. package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +9 -0
  85. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -0
  86. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -0
  87. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +41 -0
  88. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +283 -0
  89. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
  90. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +26 -0
  91. package/dist/storage/implementation/v1/PersistedBatchV1.js +183 -0
  92. package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -0
  93. package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +18 -0
  94. package/dist/storage/implementation/v1/SingleBucketStoreV1.js +57 -0
  95. package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -0
  96. package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +19 -0
  97. package/dist/storage/implementation/v1/SourceRecordStoreV1.js +105 -0
  98. package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -0
  99. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +12 -0
  100. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +20 -0
  101. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -0
  102. package/dist/storage/implementation/v1/models.d.ts +34 -0
  103. package/dist/storage/implementation/v1/models.js +37 -0
  104. package/dist/storage/implementation/v1/models.js.map +1 -0
  105. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +13 -0
  106. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +34 -0
  107. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -0
  108. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +15 -0
  109. package/dist/storage/implementation/v3/MongoChecksumsV3.js +84 -0
  110. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -0
  111. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +23 -0
  112. package/dist/storage/implementation/v3/MongoCompactorV3.js +68 -0
  113. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -0
  114. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +9 -0
  115. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +18 -0
  116. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
  117. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +5 -0
  118. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +9 -0
  119. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +1 -0
  120. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +41 -0
  121. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +407 -0
  122. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
  123. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +29 -0
  124. package/dist/storage/implementation/v3/PersistedBatchV3.js +259 -0
  125. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -0
  126. package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +18 -0
  127. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +48 -0
  128. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -0
  129. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +22 -0
  130. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +164 -0
  131. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -0
  132. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +21 -0
  133. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +71 -0
  134. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
  135. package/dist/storage/implementation/v3/models.d.ts +43 -0
  136. package/dist/storage/implementation/v3/models.js +34 -0
  137. package/dist/storage/implementation/v3/models.js.map +1 -0
  138. package/dist/storage/storage-index.d.ts +8 -5
  139. package/dist/storage/storage-index.js +8 -5
  140. package/dist/storage/storage-index.js.map +1 -1
  141. package/dist/utils/util.d.ts +11 -4
  142. package/dist/utils/util.js +25 -4
  143. package/dist/utils/util.js.map +1 -1
  144. package/package.json +9 -9
  145. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
  146. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -7
  147. package/src/migrations/db/migrations/1770213298299-storage-version.ts +1 -1
  148. package/src/storage/MongoBucketStorage.ts +97 -62
  149. package/src/storage/MongoReportStorage.ts +7 -7
  150. package/src/storage/implementation/BucketDefinitionMapping.ts +72 -0
  151. package/src/storage/implementation/MongoBucketBatch.ts +110 -144
  152. package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
  153. package/src/storage/implementation/MongoChecksums.ts +53 -76
  154. package/src/storage/implementation/MongoCompactor.ts +401 -403
  155. package/src/storage/implementation/MongoParameterCompactor.ts +51 -25
  156. package/src/storage/implementation/MongoPersistedSyncRules.ts +76 -0
  157. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +18 -1
  158. package/src/storage/implementation/MongoStorageProvider.ts +1 -1
  159. package/src/storage/implementation/MongoSyncBucketStorage.ts +190 -457
  160. package/src/storage/implementation/MongoSyncRulesLock.ts +12 -14
  161. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +4 -2
  162. package/src/storage/implementation/OperationBatch.ts +1 -1
  163. package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
  164. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
  165. package/src/storage/implementation/common/PersistedBatch.ts +364 -0
  166. package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
  167. package/src/storage/implementation/common/SourceRecordStore.ts +49 -0
  168. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
  169. package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
  170. package/src/storage/implementation/db.ts +107 -128
  171. package/src/storage/implementation/models.ts +86 -40
  172. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +32 -0
  173. package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
  174. package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
  175. package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
  176. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +448 -0
  177. package/src/storage/implementation/v1/PersistedBatchV1.ts +230 -0
  178. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
  179. package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
  180. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
  181. package/src/storage/implementation/v1/models.ts +84 -0
  182. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +44 -0
  183. package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
  184. package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
  185. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
  186. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +12 -0
  187. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +550 -0
  188. package/src/storage/implementation/v3/PersistedBatchV3.ts +318 -0
  189. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
  190. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
  191. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +112 -0
  192. package/src/storage/implementation/v3/models.ts +96 -0
  193. package/src/storage/storage-index.ts +8 -5
  194. package/src/utils/util.ts +36 -7
  195. package/test/src/__snapshots__/{client-connections-storage.test.ts.snap → connection-report-storage.test.ts.snap} +68 -68
  196. package/test/src/__snapshots__/storage_sync.test.ts.snap +282 -0
  197. package/test/src/{client-connections-storage.test.ts → connection-report-storage.test.ts} +4 -4
  198. package/test/src/setup.ts +1 -1
  199. package/test/src/storage.test.ts +2 -2
  200. package/test/src/storage_compacting.test.ts +94 -3
  201. package/test/src/storage_sync.test.ts +351 -5
  202. package/test/tsconfig.json +0 -1
  203. package/tsconfig.tsbuildinfo +1 -1
  204. package/dist/storage/implementation/PersistedBatch.d.ts +0 -71
  205. package/dist/storage/implementation/PersistedBatch.js +0 -354
  206. package/dist/storage/implementation/PersistedBatch.js.map +0 -1
  207. package/src/storage/implementation/PersistedBatch.ts +0 -432
@@ -1,14 +1,11 @@
1
1
  import { mongo } from '@powersync/lib-service-mongodb';
2
2
  import { HydratedSyncRules, SqlEventDescriptor } from '@powersync/service-sync-rules';
3
3
  import { BaseObserver, Logger } from '@powersync/lib-services-framework';
4
- import { BucketStorageMarkRecordUnavailable, CheckpointResult, InternalOpId, storage } from '@powersync/service-core';
5
- import { VersionedPowerSyncMongo } from './db.js';
6
- import { CurrentBucket } from './models.js';
7
- /**
8
- * 15MB
9
- */
10
- export declare const MAX_ROW_SIZE: number;
11
- export declare const EMPTY_DATA: mongo.Binary;
4
+ import { BucketStorageMarkRecordUnavailable, CheckpointResult, InternalOpId, PerformanceTracer, storage } from '@powersync/service-core';
5
+ import { BucketDefinitionMapping } from './BucketDefinitionMapping.js';
6
+ import { PersistedBatch } from './common/PersistedBatch.js';
7
+ import { SourceRecordStore } from './common/SourceRecordStore.js';
8
+ import type { VersionedPowerSyncMongo } from './db.js';
12
9
  export interface MongoBucketBatchOptions {
13
10
  db: VersionedPowerSyncMongo;
14
11
  syncRules: HydratedSyncRules;
@@ -18,27 +15,31 @@ export interface MongoBucketBatchOptions {
18
15
  keepaliveOp: InternalOpId | null;
19
16
  resumeFromLsn: string | null;
20
17
  storeCurrentData: boolean;
18
+ mapping: BucketDefinitionMapping;
21
19
  /**
22
20
  * Set to true for initial replication.
23
21
  */
24
22
  skipExistingRows: boolean;
25
23
  markRecordUnavailable: BucketStorageMarkRecordUnavailable | undefined;
26
- logger?: Logger;
24
+ logger: Logger;
25
+ tracer?: PerformanceTracer<'storage' | 'evaluate'>;
27
26
  }
28
- export declare class MongoBucketBatch extends BaseObserver<storage.BucketBatchStorageListener> implements storage.BucketStorageBatch {
29
- private logger;
27
+ export declare abstract class MongoBucketBatch extends BaseObserver<storage.BucketBatchStorageListener> implements storage.BucketStorageBatch {
28
+ protected logger: Logger;
30
29
  private readonly client;
31
30
  readonly db: VersionedPowerSyncMongo;
32
31
  readonly session: mongo.ClientSession;
33
32
  private readonly sync_rules;
34
- private readonly group_id;
33
+ protected readonly group_id: number;
35
34
  private readonly slot_name;
36
35
  private readonly storeCurrentData;
37
36
  private readonly skipExistingRows;
37
+ protected readonly mapping: BucketDefinitionMapping;
38
38
  private batch;
39
39
  private write_checkpoint_batch;
40
40
  private markRecordUnavailable;
41
41
  private clearedError;
42
+ private tracer;
42
43
  /**
43
44
  * Last LSN received associated with a checkpoint.
44
45
  *
@@ -67,6 +68,9 @@ export declare class MongoBucketBatch extends BaseObserver<storage.BucketBatchSt
67
68
  constructor(options: MongoBucketBatchOptions);
68
69
  addCustomWriteCheckpoint(checkpoint: storage.BatchedCustomWriteCheckpointOptions): void;
69
70
  get lastCheckpointLsn(): string | null;
71
+ protected abstract createPersistedBatch(writtenSize: number): PersistedBatch;
72
+ protected abstract get sourceRecordStore(): SourceRecordStore;
73
+ protected abstract cleanupDroppedSourceTables(sourceTables: storage.SourceTable[]): Promise<void>;
70
74
  flush(options?: storage.BatchBucketFlushOptions): Promise<storage.FlushedResult | null>;
71
75
  private flushInner;
72
76
  private replicateBatch;
@@ -77,7 +81,6 @@ export declare class MongoBucketBatch extends BaseObserver<storage.BucketBatchSt
77
81
  dispose(): Promise<void>;
78
82
  private lastWaitingLogThottled;
79
83
  commit(lsn: string, options?: storage.BucketBatchCommitOptions): Promise<CheckpointResult>;
80
- private cleanupCurrentData;
81
84
  /**
82
85
  * Switch from processing -> active if relevant.
83
86
  *
@@ -103,4 +106,3 @@ export declare class MongoBucketBatch extends BaseObserver<storage.BucketBatchSt
103
106
  */
104
107
  protected getTableEvents(table: storage.SourceTable): SqlEventDescriptor[];
105
108
  }
106
- export declare function currentBucketKey(b: CurrentBucket): string;
@@ -1,24 +1,19 @@
1
1
  import { mongo } from '@powersync/lib-service-mongodb';
2
2
  import * as bson from 'bson';
3
- import { BaseObserver, container, logger as defaultLogger, ErrorCode, errors, ReplicationAssertionError, ServiceError } from '@powersync/lib-services-framework';
4
- import { deserializeBson, isCompleteRow, SaveOperationTag, storage, SyncRuleState, utils } from '@powersync/service-core';
3
+ import { BaseObserver, container, ErrorCode, errors, ReplicationAssertionError, ServiceError } from '@powersync/lib-services-framework';
4
+ import { deserializeBson, isCompleteRow, PerformanceTracer, SaveOperationTag, storage, SyncRuleState, utils } from '@powersync/service-core';
5
5
  import * as timers from 'node:timers/promises';
6
- import { idPrefixFilter, mongoTableId } from '../../utils/util.js';
6
+ import { mongoTableId } from '../../utils/util.js';
7
+ import { MAX_ROW_SIZE } from './MongoBucketBatchShared.js';
7
8
  import { MongoIdSequence } from './MongoIdSequence.js';
8
9
  import { batchCreateCustomWriteCheckpoints } from './MongoWriteCheckpointAPI.js';
9
- import { cacheKey, OperationBatch, RecordOperation } from './OperationBatch.js';
10
- import { PersistedBatch } from './PersistedBatch.js';
11
- /**
12
- * 15MB
13
- */
14
- export const MAX_ROW_SIZE = 15 * 1024 * 1024;
10
+ import { OperationBatch, RecordOperation } from './OperationBatch.js';
15
11
  // Currently, we can only have a single flush() at a time, since it locks the op_id sequence.
16
12
  // While the MongoDB transaction retry mechanism handles this okay, using an in-process Mutex
17
13
  // makes it more fair and has less overhead.
18
14
  //
19
15
  // In the future, we can investigate allowing multiple replication streams operating independently.
20
16
  const replicationMutex = new utils.Mutex();
21
- export const EMPTY_DATA = new bson.Binary(bson.serialize({}));
22
17
  export class MongoBucketBatch extends BaseObserver {
23
18
  logger;
24
19
  client;
@@ -29,10 +24,12 @@ export class MongoBucketBatch extends BaseObserver {
29
24
  slot_name;
30
25
  storeCurrentData;
31
26
  skipExistingRows;
27
+ mapping;
32
28
  batch = null;
33
29
  write_checkpoint_batch = [];
34
30
  markRecordUnavailable;
35
31
  clearedError = false;
32
+ tracer;
36
33
  /**
37
34
  * Last LSN received associated with a checkpoint.
38
35
  *
@@ -60,7 +57,7 @@ export class MongoBucketBatch extends BaseObserver {
60
57
  needsActivation = true;
61
58
  constructor(options) {
62
59
  super();
63
- this.logger = options.logger ?? defaultLogger;
60
+ this.logger = options.logger;
64
61
  this.client = options.db.client;
65
62
  this.db = options.db;
66
63
  this.group_id = options.groupId;
@@ -70,10 +67,12 @@ export class MongoBucketBatch extends BaseObserver {
70
67
  this.slot_name = options.slotName;
71
68
  this.sync_rules = options.syncRules;
72
69
  this.storeCurrentData = options.storeCurrentData;
70
+ this.mapping = options.mapping;
73
71
  this.skipExistingRows = options.skipExistingRows;
74
72
  this.markRecordUnavailable = options.markRecordUnavailable;
75
73
  this.batch = new OperationBatch();
76
74
  this.persisted_op = options.keepaliveOp ?? null;
75
+ this.tracer = options.tracer ?? new PerformanceTracer('MongoDB storage');
77
76
  }
78
77
  addCustomWriteCheckpoint(checkpoint) {
79
78
  this.write_checkpoint_batch.push({
@@ -100,6 +99,7 @@ export class MongoBucketBatch extends BaseObserver {
100
99
  const batch = this.batch;
101
100
  let last_op = null;
102
101
  let resumeBatch = null;
102
+ using _ = this.tracer.span('storage', 'flush');
103
103
  await this.withReplicationTransaction(`Flushing ${batch?.length ?? 0} ops`, async (session, opSeq) => {
104
104
  if (batch != null) {
105
105
  resumeBatch = await this.replicateBatch(session, batch, opSeq, options);
@@ -122,6 +122,7 @@ export class MongoBucketBatch extends BaseObserver {
122
122
  }
123
123
  async replicateBatch(session, batch, op_seq, options) {
124
124
  let sizes = undefined;
125
+ using _ = this.tracer.span('storage', 'replicate_batch');
125
126
  if (this.storeCurrentData && !this.skipExistingRows) {
126
127
  // We skip this step if we don't store current_data, since the sizes will
127
128
  // always be small in that case.
@@ -134,27 +135,11 @@ export class MongoBucketBatch extends BaseObserver {
134
135
  // (automatically limited to 48MB(?) per batch by MongoDB). The issue is that it changes
135
136
  // the order of processing, which then becomes really tricky to manage.
136
137
  // This now takes 2+ queries, but doesn't have any issues with order of operations.
137
- const sizeLookups = batch.batch.map((r) => {
138
- return { g: this.group_id, t: mongoTableId(r.record.sourceTable.id), k: r.beforeId };
139
- });
140
- sizes = new Map();
141
- const sizeCursor = this.db.common_current_data.aggregate([
142
- {
143
- $match: {
144
- _id: { $in: sizeLookups }
145
- }
146
- },
147
- {
148
- $project: {
149
- _id: 1,
150
- size: { $bsonSize: '$$ROOT' }
151
- }
152
- }
153
- ], { session });
154
- for await (let doc of sizeCursor.stream()) {
155
- const key = cacheKey(doc._id.t, doc._id.k);
156
- sizes.set(key, doc.size);
157
- }
138
+ const sizeLookups = batch.batch.map((r) => ({
139
+ sourceTableId: mongoTableId(r.record.sourceTable.id),
140
+ replicaId: r.beforeId
141
+ }));
142
+ sizes = await this.sourceRecordStore.loadSizes(session, sizeLookups);
158
143
  }
159
144
  // If set, we need to start a new transaction with this batch.
160
145
  let resumeBatch = null;
@@ -169,40 +154,38 @@ export class MongoBucketBatch extends BaseObserver {
169
154
  }
170
155
  continue;
171
156
  }
172
- const lookups = b.map((r) => {
173
- return { g: this.group_id, t: mongoTableId(r.record.sourceTable.id), k: r.beforeId };
174
- });
175
- let current_data_lookup = new Map();
176
- // With skipExistingRows, we only need to know whether or not the row exists.
177
- const projection = this.skipExistingRows ? { _id: 1 } : undefined;
178
- const cursor = this.db.common_current_data.find({
179
- _id: { $in: lookups }
180
- }, { session, projection });
181
- for await (let doc of cursor.stream()) {
182
- current_data_lookup.set(cacheKey(doc._id.t, doc._id.k), doc);
183
- }
184
- let persistedBatch = new PersistedBatch(this.db, this.group_id, transactionSize, {
185
- logger: this.logger
186
- });
157
+ using lookupSpan = this.tracer.span('storage', 'lookup');
158
+ const lookups = b.map((r) => ({
159
+ sourceTableId: mongoTableId(r.record.sourceTable.id),
160
+ replicaId: r.beforeId
161
+ }));
162
+ let sourceRecordLookup = await this.sourceRecordStore.loadDocuments(session, lookups, this.skipExistingRows);
163
+ lookupSpan.end();
164
+ let persistedBatch = this.createPersistedBatch(transactionSize);
165
+ // The current code structure makes it tricky to cleanly split this span from the one
166
+ // where fluhsing. So we manually end and re-create this span whenever we flush.
167
+ let evalSpan = this.tracer.span('evaluate');
187
168
  for (let op of b) {
188
169
  if (resumeBatch) {
189
170
  resumeBatch.push(op);
190
171
  continue;
191
172
  }
192
- const currentData = current_data_lookup.get(op.internalBeforeKey) ?? null;
193
- if (currentData != null) {
173
+ const sourceRecord = sourceRecordLookup.get(op.internalBeforeKey) ?? null;
174
+ if (sourceRecord != null) {
194
175
  // If it will be used again later, it will be set again using nextData below
195
- current_data_lookup.delete(op.internalBeforeKey);
176
+ sourceRecordLookup.delete(op.internalBeforeKey);
196
177
  }
197
- const nextData = this.saveOperation(persistedBatch, op, currentData, op_seq);
178
+ const nextData = this.saveOperation(persistedBatch, op, sourceRecord, op_seq);
198
179
  if (nextData != null) {
199
180
  // Update our current_data and size cache
200
- current_data_lookup.set(op.internalAfterKey, nextData);
201
- sizes?.set(op.internalAfterKey, nextData.data.length());
181
+ sourceRecordLookup.set(op.internalAfterKey, nextData);
182
+ sizes?.set(op.internalAfterKey, nextData.data?.length() ?? 0);
202
183
  }
203
184
  if (persistedBatch.shouldFlushTransaction()) {
185
+ evalSpan.end();
204
186
  // Transaction is getting big.
205
187
  // Flush, and resume in a new transaction.
188
+ using persistSpan = this.tracer.span('storage', 'persist_flush');
206
189
  const { flushedAny } = await persistedBatch.flush(this.session, options);
207
190
  didFlush ||= flushedAny;
208
191
  persistedBatch = null;
@@ -210,20 +193,25 @@ export class MongoBucketBatch extends BaseObserver {
210
193
  // we're stopping in the middle of a batch.
211
194
  // We create a new batch, and push any remaining operations to it.
212
195
  resumeBatch = new OperationBatch();
196
+ persistSpan.end();
197
+ evalSpan = this.tracer.span('evaluate');
213
198
  }
214
199
  }
200
+ evalSpan.end();
215
201
  if (persistedBatch) {
216
202
  transactionSize = persistedBatch.currentSize;
203
+ using _ = this.tracer.span('storage', 'persist_flush');
217
204
  const { flushedAny } = await persistedBatch.flush(this.session, options);
218
205
  didFlush ||= flushedAny;
219
206
  }
220
207
  }
221
208
  if (didFlush) {
209
+ using _ = this.tracer.span('storage', 'clear_error');
222
210
  await this.clearError();
223
211
  }
224
212
  return resumeBatch?.hasData() ? resumeBatch : null;
225
213
  }
226
- saveOperation(batch, operation, current_data, opSeq) {
214
+ saveOperation(batch, operation, sourceRecord, opSeq) {
227
215
  const record = operation.record;
228
216
  const beforeId = operation.beforeId;
229
217
  const afterId = operation.afterId;
@@ -233,10 +221,10 @@ export class MongoBucketBatch extends BaseObserver {
233
221
  let new_buckets = [];
234
222
  let existing_lookups = [];
235
223
  let new_lookups = [];
236
- const before_key = { g: this.group_id, t: mongoTableId(record.sourceTable.id), k: beforeId };
224
+ const sourceTableId = mongoTableId(record.sourceTable.id);
237
225
  if (this.skipExistingRows) {
238
226
  if (record.tag == SaveOperationTag.INSERT) {
239
- if (current_data != null) {
227
+ if (sourceRecord != null) {
240
228
  // Initial replication, and we already have the record.
241
229
  // This may be a different version of the record, but streaming replication
242
230
  // will take care of that.
@@ -249,7 +237,7 @@ export class MongoBucketBatch extends BaseObserver {
249
237
  }
250
238
  }
251
239
  if (record.tag == SaveOperationTag.UPDATE) {
252
- const result = current_data;
240
+ const result = sourceRecord;
253
241
  if (result == null) {
254
242
  // Not an error if we re-apply a transaction
255
243
  existing_buckets = [];
@@ -270,14 +258,14 @@ export class MongoBucketBatch extends BaseObserver {
270
258
  else {
271
259
  existing_buckets = result.buckets;
272
260
  existing_lookups = result.lookups;
273
- if (this.storeCurrentData) {
261
+ if (this.storeCurrentData && result.data != null) {
274
262
  const data = deserializeBson(result.data.buffer);
275
263
  after = storage.mergeToast(after, data);
276
264
  }
277
265
  }
278
266
  }
279
267
  else if (record.tag == SaveOperationTag.DELETE) {
280
- const result = current_data;
268
+ const result = sourceRecord;
281
269
  if (result == null) {
282
270
  // Not an error if we re-apply a transaction
283
271
  existing_buckets = [];
@@ -292,9 +280,9 @@ export class MongoBucketBatch extends BaseObserver {
292
280
  existing_lookups = result.lookups;
293
281
  }
294
282
  }
295
- let afterData;
283
+ let afterData = null;
296
284
  if (afterId != null && !this.storeCurrentData) {
297
- afterData = EMPTY_DATA;
285
+ afterData = null;
298
286
  }
299
287
  else if (afterId != null) {
300
288
  try {
@@ -378,13 +366,7 @@ export class MongoBucketBatch extends BaseObserver {
378
366
  table: sourceTable,
379
367
  before_buckets: existing_buckets
380
368
  });
381
- new_buckets = evaluated.map((e) => {
382
- return {
383
- bucket: e.bucket,
384
- table: e.table,
385
- id: e.id
386
- };
387
- });
369
+ new_buckets = this.sourceRecordStore.mapEvaluatedBuckets(evaluated);
388
370
  }
389
371
  if (sourceTable.syncParameters) {
390
372
  // Parameters
@@ -406,26 +388,27 @@ export class MongoBucketBatch extends BaseObserver {
406
388
  evaluated: paramEvaluated,
407
389
  existing_lookups
408
390
  });
409
- new_lookups = paramEvaluated.map((p) => {
410
- return storage.serializeLookup(p.lookup);
411
- });
391
+ new_lookups = this.sourceRecordStore.mapParameterLookups(paramEvaluated);
412
392
  }
413
393
  }
414
394
  let result = null;
415
395
  // 5. TOAST: Update current data and bucket list.
416
396
  if (afterId) {
417
397
  // Insert or update
418
- const after_key = { g: this.group_id, t: mongoTableId(sourceTable.id), k: afterId };
419
- batch.upsertCurrentData(after_key, {
398
+ batch.upsertCurrentData({
399
+ sourceTableId,
400
+ replicaId: afterId,
420
401
  data: afterData,
421
402
  buckets: new_buckets,
422
403
  lookups: new_lookups
423
404
  });
424
405
  result = {
425
- _id: after_key,
406
+ sourceTableId,
407
+ replicaId: afterId,
426
408
  data: afterData,
427
409
  buckets: new_buckets,
428
- lookups: new_lookups
410
+ lookups: new_lookups,
411
+ cacheKey: operation.internalAfterKey
429
412
  };
430
413
  }
431
414
  if (afterId == null || !storage.replicaIdEquals(beforeId, afterId)) {
@@ -433,12 +416,14 @@ export class MongoBucketBatch extends BaseObserver {
433
416
  // Note that this is a soft delete.
434
417
  // We don't specifically need a new or unique op_id here, but it must be greater than the
435
418
  // last checkpoint, so we use next().
436
- batch.softDeleteCurrentData(before_key, opSeq.next());
419
+ batch.softDeleteCurrentData(sourceTableId, beforeId, opSeq.next());
437
420
  }
438
421
  return result;
439
422
  }
440
423
  async withTransaction(cb) {
424
+ using lockSpan = this.tracer.span('storage', 'internal_lock');
441
425
  await replicationMutex.exclusiveLock(async () => {
426
+ lockSpan.end();
442
427
  await this.session.withTransaction(async () => {
443
428
  try {
444
429
  await cb();
@@ -450,7 +435,9 @@ export class MongoBucketBatch extends BaseObserver {
450
435
  else {
451
436
  this.logger.warn('Transaction error', e);
452
437
  }
453
- await timers.setTimeout(Math.random() * 50);
438
+ const delay = Math.random() * 50;
439
+ using _ = this.tracer.span('storage', 'retry_delay');
440
+ await timers.setTimeout(delay);
454
441
  throw e;
455
442
  }
456
443
  }, { maxCommitTimeMS: 10000 });
@@ -656,21 +643,12 @@ export class MongoBucketBatch extends BaseObserver {
656
643
  await this.db.notifyCheckpoint();
657
644
  this.persisted_op = null;
658
645
  this.last_checkpoint_lsn = lsn;
659
- if (this.db.storageConfig.softDeleteCurrentData && newLastCheckpoint != null) {
660
- await this.cleanupCurrentData(newLastCheckpoint);
646
+ if (newLastCheckpoint != null) {
647
+ await this.sourceRecordStore.postCommitCleanup(newLastCheckpoint, this.logger);
661
648
  }
662
649
  }
663
650
  return { checkpointBlocked, checkpointCreated };
664
651
  }
665
- async cleanupCurrentData(lastCheckpoint) {
666
- const result = await this.db.v3_current_data.deleteMany({
667
- '_id.g': this.group_id,
668
- pending_delete: { $exists: true, $lte: lastCheckpoint }
669
- });
670
- if (result.deletedCount > 0) {
671
- this.logger.info(`Cleaned up ${result.deletedCount} pending delete current_data records for checkpoint ${lastCheckpoint}`);
672
- }
673
- }
674
652
  /**
675
653
  * Switch from processing -> active if relevant.
676
654
  *
@@ -709,7 +687,7 @@ export class MongoBucketBatch extends BaseObserver {
709
687
  }
710
688
  });
711
689
  if (activated) {
712
- this.logger.info(`Activated new sync rules at ${lsn}`);
690
+ this.logger.info(`Activated new replication stream at ${lsn}`);
713
691
  await this.db.notifyCheckpoint();
714
692
  this.needsActivation = false;
715
693
  }
@@ -766,9 +744,10 @@ export class MongoBucketBatch extends BaseObserver {
766
744
  const result = await this.flush();
767
745
  await this.withTransaction(async () => {
768
746
  for (let table of sourceTables) {
769
- await this.db.source_tables.deleteOne({ _id: mongoTableId(table.id) });
747
+ await this.db.commonSourceTables(this.group_id).deleteOne({ _id: mongoTableId(table.id) });
770
748
  }
771
749
  });
750
+ await this.cleanupDroppedSourceTables(sourceTables);
772
751
  return result;
773
752
  }
774
753
  async truncate(sourceTables) {
@@ -795,41 +774,30 @@ export class MongoBucketBatch extends BaseObserver {
795
774
  let lastBatchCount = BATCH_LIMIT;
796
775
  while (lastBatchCount == BATCH_LIMIT) {
797
776
  await this.withReplicationTransaction(`Truncate ${sourceTable.qualifiedName}`, async (session, opSeq) => {
798
- const current_data_filter = {
799
- _id: idPrefixFilter({ g: this.group_id, t: mongoTableId(sourceTable.id) }, ['k']),
800
- // Skip soft-deleted data
801
- // Works for both v1 and v3 current_data schemas
802
- pending_delete: { $exists: false }
803
- };
804
- const cursor = this.db.common_current_data.find(current_data_filter, {
805
- projection: {
806
- _id: 1,
807
- buckets: 1,
808
- lookups: 1
809
- },
810
- limit: BATCH_LIMIT,
811
- session: session
812
- });
813
- const batch = await cursor.toArray();
814
- const persistedBatch = new PersistedBatch(this.db, this.group_id, 0, { logger: this.logger });
777
+ using evalSpan = this.tracer.span('evaluate');
778
+ const sourceTableId = mongoTableId(sourceTable.id);
779
+ const batch = await this.sourceRecordStore.loadTruncateBatch(session, sourceTableId, BATCH_LIMIT);
780
+ const persistedBatch = this.createPersistedBatch(0);
815
781
  for (let value of batch) {
816
782
  persistedBatch.saveBucketData({
817
783
  op_seq: opSeq,
818
784
  before_buckets: value.buckets,
819
785
  evaluated: [],
820
786
  table: sourceTable,
821
- sourceKey: value._id.k
787
+ sourceKey: value.replicaId
822
788
  });
823
789
  persistedBatch.saveParameterData({
824
790
  op_seq: opSeq,
825
791
  existing_lookups: value.lookups,
826
792
  evaluated: [],
827
793
  sourceTable: sourceTable,
828
- sourceKey: value._id.k
794
+ sourceKey: value.replicaId
829
795
  });
830
796
  // Since this is not from streaming replication, we can do a hard delete
831
- persistedBatch.hardDeleteCurrentData(value._id);
797
+ persistedBatch.hardDeleteCurrentData(sourceTableId, value.replicaId);
832
798
  }
799
+ evalSpan.end();
800
+ using _ = this.tracer.span('storage', 'persist_flush');
833
801
  await persistedBatch.flush(session);
834
802
  lastBatchCount = batch.length;
835
803
  last_op = opSeq.last();
@@ -846,7 +814,7 @@ export class MongoBucketBatch extends BaseObserver {
846
814
  };
847
815
  copy.snapshotStatus = snapshotStatus;
848
816
  await this.withTransaction(async () => {
849
- await this.db.source_tables.updateOne({ _id: mongoTableId(table.id) }, {
817
+ await this.db.commonSourceTables(this.group_id).updateOne({ _id: mongoTableId(table.id) }, {
850
818
  $set: {
851
819
  snapshot_status: {
852
820
  last_key: snapshotStatus.lastKey == null ? null : new bson.Binary(snapshotStatus.lastKey),
@@ -884,7 +852,7 @@ export class MongoBucketBatch extends BaseObserver {
884
852
  const session = this.session;
885
853
  const ids = tables.map((table) => mongoTableId(table.id));
886
854
  await this.withTransaction(async () => {
887
- await this.db.source_tables.updateMany({ _id: { $in: ids } }, {
855
+ await this.db.commonSourceTables(this.group_id).updateMany({ _id: { $in: ids } }, {
888
856
  $set: {
889
857
  snapshot_done: true
890
858
  },
@@ -933,7 +901,4 @@ export class MongoBucketBatch extends BaseObserver {
933
901
  return this.sync_rules.eventDescriptors.filter((evt) => [...evt.getSourceTables()].some((sourceTable) => sourceTable.matches(table)));
934
902
  }
935
903
  }
936
- export function currentBucketKey(b) {
937
- return `${b.bucket}/${b.table}/${b.id}`;
938
- }
939
904
  //# sourceMappingURL=MongoBucketBatch.js.map