@powersync/service-module-mongodb-storage 0.15.4 → 0.16.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.
Files changed (193) hide show
  1. package/CHANGELOG.md +35 -0
  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 +2 -2
  5. package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
  6. package/dist/storage/MongoBucketStorage.d.ts +2 -2
  7. package/dist/storage/MongoBucketStorage.js +47 -34
  8. package/dist/storage/MongoBucketStorage.js.map +1 -1
  9. package/dist/storage/implementation/BucketDefinitionMapping.d.ts +17 -0
  10. package/dist/storage/implementation/BucketDefinitionMapping.js +58 -0
  11. package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
  12. package/dist/storage/implementation/MongoBucketBatch.d.ts +16 -14
  13. package/dist/storage/implementation/MongoBucketBatch.js +80 -115
  14. package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
  15. package/dist/storage/implementation/MongoBucketBatchShared.d.ts +5 -0
  16. package/dist/storage/implementation/MongoBucketBatchShared.js +8 -0
  17. package/dist/storage/implementation/MongoBucketBatchShared.js.map +1 -0
  18. package/dist/storage/implementation/MongoChecksums.d.ts +28 -17
  19. package/dist/storage/implementation/MongoChecksums.js +13 -72
  20. package/dist/storage/implementation/MongoChecksums.js.map +1 -1
  21. package/dist/storage/implementation/MongoCompactor.d.ts +98 -58
  22. package/dist/storage/implementation/MongoCompactor.js +229 -296
  23. package/dist/storage/implementation/MongoCompactor.js.map +1 -1
  24. package/dist/storage/implementation/MongoParameterCompactor.d.ts +11 -6
  25. package/dist/storage/implementation/MongoParameterCompactor.js +11 -8
  26. package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
  27. package/dist/storage/implementation/MongoPersistedSyncRules.d.ts +14 -0
  28. package/dist/storage/implementation/MongoPersistedSyncRules.js +64 -0
  29. package/dist/storage/implementation/MongoPersistedSyncRules.js.map +1 -0
  30. package/dist/storage/implementation/MongoPersistedSyncRulesContent.d.ts +3 -0
  31. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js +9 -0
  32. package/dist/storage/implementation/MongoPersistedSyncRulesContent.js.map +1 -1
  33. package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +47 -29
  34. package/dist/storage/implementation/MongoSyncBucketStorage.js +94 -387
  35. package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
  36. package/dist/storage/implementation/MongoSyncRulesLock.d.ts +5 -3
  37. package/dist/storage/implementation/MongoSyncRulesLock.js +12 -10
  38. package/dist/storage/implementation/MongoSyncRulesLock.js.map +1 -1
  39. package/dist/storage/implementation/MongoWriteCheckpointAPI.js +1 -1
  40. package/dist/storage/implementation/MongoWriteCheckpointAPI.js.map +1 -1
  41. package/dist/storage/implementation/OperationBatch.js +1 -1
  42. package/dist/storage/implementation/common/BucketDataDoc.d.ts +35 -0
  43. package/dist/storage/implementation/common/BucketDataDoc.js +2 -0
  44. package/dist/storage/implementation/common/BucketDataDoc.js.map +1 -0
  45. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.d.ts +13 -0
  46. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js +2 -0
  47. package/dist/storage/implementation/common/MongoSyncBucketStorageContext.js.map +1 -0
  48. package/dist/storage/implementation/common/PersistedBatch.d.ts +108 -0
  49. package/dist/storage/implementation/common/PersistedBatch.js +237 -0
  50. package/dist/storage/implementation/common/PersistedBatch.js.map +1 -0
  51. package/dist/storage/implementation/common/SingleBucketStore.d.ts +54 -0
  52. package/dist/storage/implementation/common/SingleBucketStore.js +3 -0
  53. package/dist/storage/implementation/common/SingleBucketStore.js.map +1 -0
  54. package/dist/storage/implementation/common/SourceRecordStore.d.ts +36 -0
  55. package/dist/storage/implementation/common/SourceRecordStore.js +2 -0
  56. package/dist/storage/implementation/common/SourceRecordStore.js.map +1 -0
  57. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.d.ts +27 -0
  58. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js +57 -0
  59. package/dist/storage/implementation/common/VersionedPowerSyncMongoBase.js.map +1 -0
  60. package/dist/storage/implementation/createMongoSyncBucketStorage.d.ts +7 -0
  61. package/dist/storage/implementation/createMongoSyncBucketStorage.js +9 -0
  62. package/dist/storage/implementation/createMongoSyncBucketStorage.js.map +1 -0
  63. package/dist/storage/implementation/db.d.ts +32 -35
  64. package/dist/storage/implementation/db.js +77 -99
  65. package/dist/storage/implementation/db.js.map +1 -1
  66. package/dist/storage/implementation/models.d.ts +62 -33
  67. package/dist/storage/implementation/models.js +20 -1
  68. package/dist/storage/implementation/models.js.map +1 -1
  69. package/dist/storage/implementation/v1/MongoBucketBatchV1.d.ts +13 -0
  70. package/dist/storage/implementation/v1/MongoBucketBatchV1.js +22 -0
  71. package/dist/storage/implementation/v1/MongoBucketBatchV1.js.map +1 -0
  72. package/dist/storage/implementation/v1/MongoChecksumsV1.d.ts +12 -0
  73. package/dist/storage/implementation/v1/MongoChecksumsV1.js +56 -0
  74. package/dist/storage/implementation/v1/MongoChecksumsV1.js.map +1 -0
  75. package/dist/storage/implementation/v1/MongoCompactorV1.d.ts +23 -0
  76. package/dist/storage/implementation/v1/MongoCompactorV1.js +52 -0
  77. package/dist/storage/implementation/v1/MongoCompactorV1.js.map +1 -0
  78. package/dist/storage/implementation/v1/MongoParameterCompactorV1.d.ts +9 -0
  79. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js +20 -0
  80. package/dist/storage/implementation/v1/MongoParameterCompactorV1.js.map +1 -0
  81. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.d.ts +41 -0
  82. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js +283 -0
  83. package/dist/storage/implementation/v1/MongoSyncBucketStorageV1.js.map +1 -0
  84. package/dist/storage/implementation/v1/PersistedBatchV1.d.ts +26 -0
  85. package/dist/storage/implementation/v1/PersistedBatchV1.js +183 -0
  86. package/dist/storage/implementation/v1/PersistedBatchV1.js.map +1 -0
  87. package/dist/storage/implementation/v1/SingleBucketStoreV1.d.ts +18 -0
  88. package/dist/storage/implementation/v1/SingleBucketStoreV1.js +57 -0
  89. package/dist/storage/implementation/v1/SingleBucketStoreV1.js.map +1 -0
  90. package/dist/storage/implementation/v1/SourceRecordStoreV1.d.ts +19 -0
  91. package/dist/storage/implementation/v1/SourceRecordStoreV1.js +105 -0
  92. package/dist/storage/implementation/v1/SourceRecordStoreV1.js.map +1 -0
  93. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.d.ts +12 -0
  94. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js +20 -0
  95. package/dist/storage/implementation/v1/VersionedPowerSyncMongoV1.js.map +1 -0
  96. package/dist/storage/implementation/v1/models.d.ts +34 -0
  97. package/dist/storage/implementation/v1/models.js +37 -0
  98. package/dist/storage/implementation/v1/models.js.map +1 -0
  99. package/dist/storage/implementation/v3/MongoBucketBatchV3.d.ts +13 -0
  100. package/dist/storage/implementation/v3/MongoBucketBatchV3.js +34 -0
  101. package/dist/storage/implementation/v3/MongoBucketBatchV3.js.map +1 -0
  102. package/dist/storage/implementation/v3/MongoChecksumsV3.d.ts +15 -0
  103. package/dist/storage/implementation/v3/MongoChecksumsV3.js +84 -0
  104. package/dist/storage/implementation/v3/MongoChecksumsV3.js.map +1 -0
  105. package/dist/storage/implementation/v3/MongoCompactorV3.d.ts +23 -0
  106. package/dist/storage/implementation/v3/MongoCompactorV3.js +68 -0
  107. package/dist/storage/implementation/v3/MongoCompactorV3.js.map +1 -0
  108. package/dist/storage/implementation/v3/MongoParameterCompactorV3.d.ts +9 -0
  109. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js +18 -0
  110. package/dist/storage/implementation/v3/MongoParameterCompactorV3.js.map +1 -0
  111. package/dist/storage/implementation/v3/MongoParameterLookupV3.d.ts +5 -0
  112. package/dist/storage/implementation/v3/MongoParameterLookupV3.js +9 -0
  113. package/dist/storage/implementation/v3/MongoParameterLookupV3.js.map +1 -0
  114. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.d.ts +41 -0
  115. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js +407 -0
  116. package/dist/storage/implementation/v3/MongoSyncBucketStorageV3.js.map +1 -0
  117. package/dist/storage/implementation/v3/PersistedBatchV3.d.ts +29 -0
  118. package/dist/storage/implementation/v3/PersistedBatchV3.js +259 -0
  119. package/dist/storage/implementation/v3/PersistedBatchV3.js.map +1 -0
  120. package/dist/storage/implementation/v3/SingleBucketStoreV3.d.ts +18 -0
  121. package/dist/storage/implementation/v3/SingleBucketStoreV3.js +48 -0
  122. package/dist/storage/implementation/v3/SingleBucketStoreV3.js.map +1 -0
  123. package/dist/storage/implementation/v3/SourceRecordStoreV3.d.ts +22 -0
  124. package/dist/storage/implementation/v3/SourceRecordStoreV3.js +164 -0
  125. package/dist/storage/implementation/v3/SourceRecordStoreV3.js.map +1 -0
  126. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.d.ts +21 -0
  127. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js +71 -0
  128. package/dist/storage/implementation/v3/VersionedPowerSyncMongoV3.js.map +1 -0
  129. package/dist/storage/implementation/v3/models.d.ts +43 -0
  130. package/dist/storage/implementation/v3/models.js +34 -0
  131. package/dist/storage/implementation/v3/models.js.map +1 -0
  132. package/dist/storage/storage-index.d.ts +6 -3
  133. package/dist/storage/storage-index.js +6 -3
  134. package/dist/storage/storage-index.js.map +1 -1
  135. package/dist/utils/util.d.ts +10 -3
  136. package/dist/utils/util.js +24 -3
  137. package/dist/utils/util.js.map +1 -1
  138. package/package.json +9 -9
  139. package/src/migrations/db/migrations/1688556755264-initial-sync-rules.ts +1 -1
  140. package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +6 -6
  141. package/src/storage/MongoBucketStorage.ts +92 -59
  142. package/src/storage/implementation/BucketDefinitionMapping.ts +72 -0
  143. package/src/storage/implementation/MongoBucketBatch.ts +110 -144
  144. package/src/storage/implementation/MongoBucketBatchShared.ts +11 -0
  145. package/src/storage/implementation/MongoChecksums.ts +52 -75
  146. package/src/storage/implementation/MongoCompactor.ts +374 -404
  147. package/src/storage/implementation/MongoParameterCompactor.ts +37 -24
  148. package/src/storage/implementation/MongoPersistedSyncRules.ts +76 -0
  149. package/src/storage/implementation/MongoPersistedSyncRulesContent.ts +17 -0
  150. package/src/storage/implementation/MongoSyncBucketStorage.ts +181 -455
  151. package/src/storage/implementation/MongoSyncRulesLock.ts +11 -13
  152. package/src/storage/implementation/MongoWriteCheckpointAPI.ts +3 -1
  153. package/src/storage/implementation/OperationBatch.ts +1 -1
  154. package/src/storage/implementation/common/BucketDataDoc.ts +37 -0
  155. package/src/storage/implementation/common/MongoSyncBucketStorageContext.ts +15 -0
  156. package/src/storage/implementation/common/PersistedBatch.ts +364 -0
  157. package/src/storage/implementation/common/SingleBucketStore.ts +63 -0
  158. package/src/storage/implementation/common/SourceRecordStore.ts +49 -0
  159. package/src/storage/implementation/common/VersionedPowerSyncMongoBase.ts +80 -0
  160. package/src/storage/implementation/createMongoSyncBucketStorage.ts +25 -0
  161. package/src/storage/implementation/db.ts +105 -129
  162. package/src/storage/implementation/models.ts +82 -36
  163. package/src/storage/implementation/v1/MongoBucketBatchV1.ts +32 -0
  164. package/src/storage/implementation/v1/MongoChecksumsV1.ts +75 -0
  165. package/src/storage/implementation/v1/MongoCompactorV1.ts +93 -0
  166. package/src/storage/implementation/v1/MongoParameterCompactorV1.ts +26 -0
  167. package/src/storage/implementation/v1/MongoSyncBucketStorageV1.ts +448 -0
  168. package/src/storage/implementation/v1/PersistedBatchV1.ts +230 -0
  169. package/src/storage/implementation/v1/SingleBucketStoreV1.ts +74 -0
  170. package/src/storage/implementation/v1/SourceRecordStoreV1.ts +156 -0
  171. package/src/storage/implementation/v1/VersionedPowerSyncMongoV1.ts +28 -0
  172. package/src/storage/implementation/v1/models.ts +84 -0
  173. package/src/storage/implementation/v3/MongoBucketBatchV3.ts +44 -0
  174. package/src/storage/implementation/v3/MongoChecksumsV3.ts +120 -0
  175. package/src/storage/implementation/v3/MongoCompactorV3.ts +107 -0
  176. package/src/storage/implementation/v3/MongoParameterCompactorV3.ts +24 -0
  177. package/src/storage/implementation/v3/MongoParameterLookupV3.ts +12 -0
  178. package/src/storage/implementation/v3/MongoSyncBucketStorageV3.ts +550 -0
  179. package/src/storage/implementation/v3/PersistedBatchV3.ts +318 -0
  180. package/src/storage/implementation/v3/SingleBucketStoreV3.ts +68 -0
  181. package/src/storage/implementation/v3/SourceRecordStoreV3.ts +226 -0
  182. package/src/storage/implementation/v3/VersionedPowerSyncMongoV3.ts +112 -0
  183. package/src/storage/implementation/v3/models.ts +96 -0
  184. package/src/storage/storage-index.ts +6 -3
  185. package/src/utils/util.ts +34 -5
  186. package/test/src/storage_compacting.test.ts +57 -29
  187. package/test/src/storage_sync.test.ts +351 -5
  188. package/test/tsconfig.json +0 -1
  189. package/tsconfig.tsbuildinfo +1 -1
  190. package/dist/storage/implementation/PersistedBatch.d.ts +0 -71
  191. package/dist/storage/implementation/PersistedBatch.js +0 -354
  192. package/dist/storage/implementation/PersistedBatch.js.map +0 -1
  193. package/src/storage/implementation/PersistedBatch.ts +0 -432
@@ -0,0 +1,25 @@
1
+ import { storage } from '@powersync/service-core';
2
+ import { MongoBucketStorage } from '../MongoBucketStorage.js';
3
+ import { MongoPersistedSyncRulesContent } from './MongoPersistedSyncRulesContent.js';
4
+ import { MongoSyncBucketStorage, MongoSyncBucketStorageOptions } from './MongoSyncBucketStorage.js';
5
+ import { MongoSyncBucketStorageV1 } from './v1/MongoSyncBucketStorageV1.js';
6
+ import { MongoSyncBucketStorageV3 } from './v3/MongoSyncBucketStorageV3.js';
7
+
8
+ export { MongoSyncBucketStorageOptions } from './MongoSyncBucketStorage.js';
9
+
10
+ export type { MongoSyncBucketStorage };
11
+
12
+ export function createMongoSyncBucketStorage(
13
+ factory: MongoBucketStorage,
14
+ group_id: number,
15
+ sync_rules: MongoPersistedSyncRulesContent,
16
+ slot_name: string,
17
+ writeCheckpointMode: storage.WriteCheckpointMode | undefined,
18
+ options: MongoSyncBucketStorageOptions
19
+ ): MongoSyncBucketStorage {
20
+ if (sync_rules.getStorageConfig().incrementalReprocessing) {
21
+ return new MongoSyncBucketStorageV3(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options);
22
+ }
23
+
24
+ return new MongoSyncBucketStorageV1(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options);
25
+ }
@@ -2,16 +2,13 @@ import * as lib_mongo from '@powersync/lib-service-mongodb';
2
2
  import { mongo } from '@powersync/lib-service-mongodb';
3
3
  import { POWERSYNC_VERSION, storage } from '@powersync/service-core';
4
4
 
5
- import { DO_NOT_LOG, ServiceAssertionError } from '@powersync/lib-services-framework';
5
+ import { DO_NOT_LOG } from '@powersync/lib-services-framework';
6
6
  import { MongoStorageConfig } from '../../types/types.js';
7
+ import { BaseVersionedPowerSyncMongo } from './common/VersionedPowerSyncMongoBase.js';
7
8
  import {
8
- BucketDataDocument,
9
- BucketParameterDocument,
10
- BucketStateDocument,
11
9
  CheckpointEventDocument,
12
10
  ClientConnectionDocument,
13
- CurrentDataDocument,
14
- CurrentDataDocumentV3,
11
+ CommonSourceTableDocument,
15
12
  CustomWriteCheckpointDocument,
16
13
  IdSequenceDocument,
17
14
  InstanceDocument,
@@ -20,6 +17,15 @@ import {
20
17
  SyncRuleDocument,
21
18
  WriteCheckpointDocument
22
19
  } from './models.js';
20
+ import {
21
+ BucketDataDocumentV1,
22
+ BucketParameterDocument,
23
+ BucketStateDocumentV1,
24
+ CurrentDataDocument
25
+ } from './v1/models.js';
26
+ import { VersionedPowerSyncMongoV1 } from './v1/VersionedPowerSyncMongoV1.js';
27
+ import { BucketDataDocumentV3 } from './v3/models.js';
28
+ import { VersionedPowerSyncMongoV3 } from './v3/VersionedPowerSyncMongoV3.js';
23
29
 
24
30
  export interface PowerSyncMongoOptions {
25
31
  /**
@@ -32,8 +38,7 @@ export class PowerSyncMongo {
32
38
  [DO_NOT_LOG] = true;
33
39
 
34
40
  readonly current_data: mongo.Collection<CurrentDataDocument>;
35
- readonly v3_current_data: mongo.Collection<CurrentDataDocumentV3>;
36
- readonly bucket_data: mongo.Collection<BucketDataDocument>;
41
+ readonly bucket_data: mongo.Collection<BucketDataDocumentV1>;
37
42
  readonly bucket_parameters: mongo.Collection<BucketParameterDocument>;
38
43
  readonly op_id_sequence: mongo.Collection<IdSequenceDocument>;
39
44
  readonly sync_rules: mongo.Collection<SyncRuleDocument>;
@@ -42,7 +47,7 @@ export class PowerSyncMongo {
42
47
  readonly write_checkpoints: mongo.Collection<WriteCheckpointDocument>;
43
48
  readonly instance: mongo.Collection<InstanceDocument>;
44
49
  readonly locks: mongo.Collection<lib_mongo.locks.Lock>;
45
- readonly bucket_state: mongo.Collection<BucketStateDocument>;
50
+ readonly bucket_state: mongo.Collection<BucketStateDocumentV1>;
46
51
  readonly checkpoint_events: mongo.Collection<CheckpointEventDocument>;
47
52
  readonly connection_report_events: mongo.Collection<ClientConnectionDocument>;
48
53
 
@@ -58,7 +63,6 @@ export class PowerSyncMongo {
58
63
  this.db = db;
59
64
 
60
65
  this.current_data = db.collection('current_data');
61
- this.v3_current_data = db.collection('v3_current_data');
62
66
  this.bucket_data = db.collection('bucket_data');
63
67
  this.bucket_parameters = db.collection('bucket_parameters');
64
68
  this.op_id_sequence = db.collection('op_id_sequence');
@@ -73,8 +77,80 @@ export class PowerSyncMongo {
73
77
  this.connection_report_events = this.db.collection('connection_report_events');
74
78
  }
75
79
 
76
- versioned(storageConfig: StorageConfig) {
77
- return new VersionedPowerSyncMongo(this, storageConfig);
80
+ versioned(storageConfig: StorageConfig): VersionedPowerSyncMongo {
81
+ if (storageConfig.incrementalReprocessing) {
82
+ return new VersionedPowerSyncMongoV3(this, storageConfig);
83
+ }
84
+
85
+ return new VersionedPowerSyncMongoV1(this, storageConfig);
86
+ }
87
+
88
+ /**
89
+ * Not safe for user-provided prefix - only for hardcoded values.
90
+ */
91
+ async listBucketDataCollectionsV3(groupId?: number): Promise<mongo.Collection<BucketDataDocumentV3>[]> {
92
+ const prefix = groupId == null ? 'bucket_data_' : `bucket_data_${groupId}_`;
93
+ const collections = await this.db.listCollections({ name: new RegExp(`^${prefix}`) }, { nameOnly: true }).toArray();
94
+
95
+ return collections
96
+ .filter((collection) => collection.name.startsWith(prefix))
97
+ .map((collection) => this.db.collection<BucketDataDocumentV3>(collection.name));
98
+ }
99
+
100
+ /**
101
+ * Not safe for user-provided prefix - only for hardcoded values.
102
+ */
103
+ private async collectionsByPrefix(prefix: string): Promise<mongo.Collection<never>[]> {
104
+ const collections = await this.db.listCollections({ name: new RegExp(`^${prefix}`) }, { nameOnly: true }).toArray();
105
+
106
+ return collections
107
+ .filter((collection) => collection.name.startsWith(prefix))
108
+ .map((collection) => this.db.collection<never>(collection.name));
109
+ }
110
+
111
+ /**
112
+ * List all parameter index collections across all replication streams.
113
+ *
114
+ * Primarily used to clear the db.
115
+ */
116
+ async listAllParameterIndexCollectionsV3(): Promise<mongo.Collection<never>[]> {
117
+ return this.collectionsByPrefix(`parameter_index_`);
118
+ }
119
+
120
+ /**
121
+ * List all parameter index collections across all replication streams.
122
+ *
123
+ * Primarily used to clear the db.
124
+ */
125
+ async listAllSourceRecordCollectionsV3(): Promise<mongo.Collection<never>[]> {
126
+ return this.collectionsByPrefix(`source_records_`);
127
+ }
128
+
129
+ async listAllBucketStateCollectionsV3(): Promise<mongo.Collection<never>[]> {
130
+ return this.collectionsByPrefix(`bucket_state_`);
131
+ }
132
+
133
+ sourceRecordsCollectionName(replicationStreamId: number, sourceTableId: mongo.ObjectId) {
134
+ return `source_records_${replicationStreamId}_${sourceTableId.toHexString()}`;
135
+ }
136
+
137
+ sourceTableCollectionName(replicationStreamId: number) {
138
+ return `source_table_${replicationStreamId}`;
139
+ }
140
+
141
+ async listSourceTableCollections(
142
+ replicationStreamId?: number
143
+ ): Promise<mongo.Collection<CommonSourceTableDocument>[]> {
144
+ const filter =
145
+ replicationStreamId == null
146
+ ? { name: new RegExp('^source_table_') }
147
+ : { name: this.sourceTableCollectionName(replicationStreamId) };
148
+ const prefix = replicationStreamId == null ? 'source_table_' : this.sourceTableCollectionName(replicationStreamId);
149
+ const collections = await this.db.listCollections(filter, { nameOnly: true }).toArray();
150
+
151
+ return collections
152
+ .filter((collection) => collection.name.startsWith(prefix))
153
+ .map((collection) => this.db.collection<CommonSourceTableDocument>(collection.name));
78
154
  }
79
155
 
80
156
  /**
@@ -82,11 +158,25 @@ export class PowerSyncMongo {
82
158
  */
83
159
  async clear() {
84
160
  await this.current_data.deleteMany({});
85
- await this.v3_current_data.deleteMany({});
161
+ for (const collection of await this.listAllSourceRecordCollectionsV3()) {
162
+ await collection.drop();
163
+ }
86
164
  await this.bucket_data.deleteMany({});
165
+ for (const collection of await this.listBucketDataCollectionsV3()) {
166
+ await collection.drop();
167
+ }
87
168
  await this.bucket_parameters.deleteMany({});
169
+ for (const collection of await this.listAllParameterIndexCollectionsV3()) {
170
+ await collection.drop();
171
+ }
172
+ for (const collection of await this.listAllBucketStateCollectionsV3()) {
173
+ await collection.drop();
174
+ }
88
175
  await this.op_id_sequence.deleteMany({});
89
176
  await this.sync_rules.deleteMany({});
177
+ for (const collection of await this.listSourceTableCollections()) {
178
+ await collection.drop();
179
+ }
90
180
  await this.source_tables.deleteMany({});
91
181
  await this.write_checkpoints.deleteMany({});
92
182
  await this.instance.deleteOne({});
@@ -105,7 +195,7 @@ export class PowerSyncMongo {
105
195
  }
106
196
 
107
197
  /**
108
- * Call this after every checkpoint or sync rules status update. Rather call too often than too rarely.
198
+ * Call this after every checkpoint or replication stream status update. Rather call too often than too rarely.
109
199
  *
110
200
  * This is used in a similar way to the Postgres NOTIFY functionality.
111
201
  */
@@ -183,126 +273,12 @@ export class PowerSyncMongo {
183
273
  { name: 'dirty_count' }
184
274
  );
185
275
  }
186
-
187
- async initializeStorageVersion(storageConfig: StorageConfig) {
188
- if (storageConfig.softDeleteCurrentData) {
189
- // Initialize the v3_current_data collection, which is used for the new storage version.
190
- // No-op if this already exists
191
- await this.v3_current_data.createIndex(
192
- {
193
- '_id.g': 1,
194
- pending_delete: 1
195
- },
196
- {
197
- partialFilterExpression: { pending_delete: { $exists: true } },
198
- name: 'pending_delete'
199
- }
200
- );
201
- }
202
- }
203
276
  }
204
277
 
205
278
  /**
206
279
  * This is similar to PowerSyncMongo, but blocks access to certain collections based on the storage version.
207
280
  */
208
- export class VersionedPowerSyncMongo {
209
- readonly client: mongo.MongoClient;
210
- readonly db: mongo.Db;
211
- [DO_NOT_LOG] = true;
212
-
213
- readonly storageConfig: StorageConfig;
214
- #upstream: PowerSyncMongo;
215
-
216
- constructor(upstream: PowerSyncMongo, storageConfig: StorageConfig) {
217
- this.#upstream = upstream;
218
- this.client = upstream.client;
219
- this.db = upstream.db;
220
- this.storageConfig = storageConfig;
221
- }
222
-
223
- /**
224
- * Uses either `current_data` or `v3_current_data` collection based on the storage version.
225
- *
226
- * Use in places where it does not matter which version is used.
227
- */
228
- get common_current_data(): mongo.Collection<CurrentDataDocument> {
229
- if (this.storageConfig.softDeleteCurrentData) {
230
- return this.#upstream.v3_current_data;
231
- } else {
232
- return this.#upstream.current_data;
233
- }
234
- }
235
-
236
- get v1_current_data() {
237
- if (this.storageConfig.softDeleteCurrentData) {
238
- throw new ServiceAssertionError(
239
- 'current_data collection should not be used when softDeleteCurrentData is enabled'
240
- );
241
- }
242
- return this.#upstream.current_data;
243
- }
244
-
245
- get v3_current_data() {
246
- if (!this.storageConfig.softDeleteCurrentData) {
247
- throw new ServiceAssertionError(
248
- 'v3_current_data collection should not be used when softDeleteCurrentData is disabled'
249
- );
250
- }
251
- return this.#upstream.v3_current_data;
252
- }
253
-
254
- get bucket_data() {
255
- return this.#upstream.bucket_data;
256
- }
257
-
258
- get bucket_parameters() {
259
- return this.#upstream.bucket_parameters;
260
- }
261
-
262
- get op_id_sequence() {
263
- return this.#upstream.op_id_sequence;
264
- }
265
-
266
- get sync_rules() {
267
- return this.#upstream.sync_rules;
268
- }
269
-
270
- get source_tables() {
271
- return this.#upstream.source_tables;
272
- }
273
-
274
- get custom_write_checkpoints() {
275
- return this.#upstream.custom_write_checkpoints;
276
- }
277
-
278
- get write_checkpoints() {
279
- return this.#upstream.write_checkpoints;
280
- }
281
-
282
- get instance() {
283
- return this.#upstream.instance;
284
- }
285
-
286
- get locks() {
287
- return this.#upstream.locks;
288
- }
289
-
290
- get bucket_state() {
291
- return this.#upstream.bucket_state;
292
- }
293
-
294
- get checkpoint_events() {
295
- return this.#upstream.checkpoint_events;
296
- }
297
-
298
- get connection_report_events() {
299
- return this.#upstream.connection_report_events;
300
- }
301
-
302
- notifyCheckpoint() {
303
- return this.#upstream.notifyCheckpoint();
304
- }
305
- }
281
+ export type VersionedPowerSyncMongo = BaseVersionedPowerSyncMongo;
306
282
 
307
283
  export function createPowerSyncMongo(config: MongoStorageConfig, options?: lib_mongo.MongoConnectionOptions) {
308
284
  return new PowerSyncMongo(
@@ -3,6 +3,9 @@ import { InternalOpId, SerializedSyncPlan, storage } from '@powersync/service-co
3
3
  import { SqliteJsonValue } from '@powersync/service-sync-rules';
4
4
  import { event_types } from '@powersync/service-types';
5
5
  import * as bson from 'bson';
6
+ import { ParameterIndexId } from './BucketDefinitionMapping.js';
7
+ import type { CurrentDataDocument, SourceTableDocumentV1 } from './v1/models.js';
8
+ import type { CurrentBucketV3, CurrentDataDocumentV3, RecordedLookupV3, SourceTableDocumentV3 } from './v3/models.js';
6
9
 
7
10
  /**
8
11
  * Replica id uniquely identifying a row on the source database.
@@ -22,50 +25,53 @@ export interface SourceKey {
22
25
  k: ReplicaId;
23
26
  }
24
27
 
28
+ export interface SourceTableKey {
29
+ /** source table id */
30
+ t: bson.ObjectId;
31
+ /** source key */
32
+ k: ReplicaId;
33
+ }
34
+
25
35
  export interface BucketDataKey {
26
- /** group_id */
27
- g: number;
28
36
  /** bucket name */
29
37
  b: string;
30
38
  /** op_id */
31
39
  o: bigint;
32
40
  }
33
41
 
34
- export interface CurrentDataDocument {
35
- _id: SourceKey;
36
- data: bson.Binary;
37
- buckets: CurrentBucket[];
38
- lookups: bson.Binary[];
39
- }
40
-
41
- export interface CurrentDataDocumentV3 {
42
- _id: SourceKey;
43
- data: bson.Binary;
44
- buckets: CurrentBucket[];
45
- lookups: bson.Binary[];
46
- /**
47
- * If set, this can be deleted, once there is a consistent checkpoint >= pending_delete.
48
- *
49
- * This must only be set if buckets = [], lookups = [].
50
- */
51
- pending_delete?: bigint;
52
- }
53
-
54
42
  export interface CurrentBucket {
55
43
  bucket: string;
56
44
  table: string;
57
45
  id: string;
58
46
  }
59
47
 
60
- export interface BucketParameterDocument {
48
+ export interface BucketParameterDocumentBase<TKey> {
61
49
  _id: bigint;
62
- key: SourceKey;
50
+ key: TKey;
63
51
  lookup: bson.Binary;
64
52
  bucket_parameters: Record<string, SqliteJsonValue>[];
65
53
  }
66
54
 
67
- export interface BucketDataDocument {
68
- _id: BucketDataKey;
55
+ export interface TaggedBucketParameterDocument extends BucketParameterDocumentBase<SourceKey | SourceTableKey> {
56
+ index: ParameterIndexId;
57
+ }
58
+
59
+ export function bucketParameterDocumentToTagged<TKey extends SourceKey | SourceTableKey>(
60
+ document: BucketParameterDocumentBase<TKey>,
61
+ index: ParameterIndexId
62
+ ): TaggedBucketParameterDocument {
63
+ return {
64
+ ...document,
65
+ index
66
+ };
67
+ }
68
+
69
+ export type OpType = 'PUT' | 'REMOVE' | 'MOVE' | 'CLEAR';
70
+
71
+ /**
72
+ * Common properties for storage V1, storage V3 and in-memory BucketDataDoc.
73
+ */
74
+ export interface BucketDataProperties {
69
75
  op: OpType;
70
76
  source_table?: bson.ObjectId;
71
77
  source_key?: ReplicaId;
@@ -76,11 +82,22 @@ export interface BucketDataDocument {
76
82
  target_op?: bigint | null;
77
83
  }
78
84
 
79
- export type OpType = 'PUT' | 'REMOVE' | 'MOVE' | 'CLEAR';
85
+ export interface BucketDataDocumentBase extends BucketDataProperties {
86
+ _id: { b: string };
87
+ }
88
+
89
+ /**
90
+ * Internal-only tag used for v1 bucket_data rows before they are converted to the v1 on-disk shape.
91
+ */
92
+ export const LEGACY_BUCKET_DATA_DEFINITION_ID = '0';
93
+
94
+ /**
95
+ * Internal-only tag used for v1 bucket_parameters rows before they are converted to the v1 on-disk shape.
96
+ */
97
+ export const LEGACY_BUCKET_PARAMETER_INDEX_ID = '0';
80
98
 
81
99
  export interface SourceTableDocument {
82
100
  _id: bson.ObjectId;
83
- group_id: number;
84
101
  connection_id: number;
85
102
  relation_id: number | string | undefined;
86
103
  schema_name: string;
@@ -100,20 +117,21 @@ export interface SourceTableDocumentSnapshotStatus {
100
117
  /**
101
118
  * Record the state of each bucket.
102
119
  *
103
- * Right now, this is just used to track when buckets are updated, for efficient incremental sync.
104
- * In the future, this could be used to track operation counts, both for diagnostic purposes, and for
105
- * determining when a compact and/or defragment could be beneficial.
120
+ * The primary use case is to track when buckets are updated, for efficient incremental sync.
106
121
  *
107
- * Note: There is currently no migration to populate this collection from existing data - it is only
122
+ * The secondary use case is to track operation counts to determine whether or not a bucket should be compacted.
123
+ *
124
+ * Note: For storage V1, there is no migration to populate this collection from existing data - it is only
108
125
  * populated by new updates.
126
+ *
127
+ * For storage V3, these will always be present.
109
128
  */
110
- export interface BucketStateDocument {
129
+ export interface BucketStateDocumentBase {
111
130
  _id: {
112
- g: number;
113
131
  b: string;
114
132
  };
115
133
  /**
116
- * Important: There is an unique index on {'_id.g': 1, last_op: 1}.
134
+ * Important: There is an unique index on last_op per logical stream.
117
135
  * That means the last_op must match an actual op in the bucket, and not the commit checkpoint.
118
136
  */
119
137
  last_op: bigint;
@@ -215,6 +233,20 @@ export interface SyncRuleDocument {
215
233
  content: string;
216
234
  serialized_plan?: SerializedSyncPlan | null;
217
235
 
236
+ /**
237
+ * Required for V3+ storage.
238
+ */
239
+ rule_mapping?: {
240
+ /**
241
+ * Map of uniqueName -> id, unique per replication stream.
242
+ */
243
+ definitions: Record<string, string>;
244
+ /**
245
+ * Map of (lookupName, queryId) -> id, unique per replication stream.
246
+ */
247
+ parameter_indexes: Record<string, string>;
248
+ };
249
+
218
250
  lock?: {
219
251
  id: string;
220
252
  expires_at: Date;
@@ -231,9 +263,14 @@ export interface StorageConfig extends storage.StorageVersionConfig {
231
263
  * a Long before summing.
232
264
  */
233
265
  longChecksums: boolean;
266
+ /**
267
+ * Enables v3 MongoDB storage behavior used for incremental reprocessing.
268
+ */
269
+ incrementalReprocessing: boolean;
234
270
  }
235
271
 
236
272
  const LONG_CHECKSUMS_STORAGE_VERSION = 2;
273
+ const INCREMENTAL_REPROCESSING_STORAGE_VERSION = storage.STORAGE_VERSION_3;
237
274
 
238
275
  export function getMongoStorageConfig(storageVersion: number): StorageConfig {
239
276
  const baseConfig = storage.STORAGE_VERSION_CONFIG[storageVersion];
@@ -241,7 +278,11 @@ export function getMongoStorageConfig(storageVersion: number): StorageConfig {
241
278
  throw new ServiceError(ErrorCode.PSYNC_S1005, `Unsupported storage version ${storageVersion}`);
242
279
  }
243
280
 
244
- return { ...baseConfig, longChecksums: storageVersion >= LONG_CHECKSUMS_STORAGE_VERSION };
281
+ return {
282
+ ...baseConfig,
283
+ longChecksums: storageVersion >= LONG_CHECKSUMS_STORAGE_VERSION,
284
+ incrementalReprocessing: storageVersion >= INCREMENTAL_REPROCESSING_STORAGE_VERSION
285
+ };
245
286
  }
246
287
 
247
288
  export interface CheckpointEventDocument {
@@ -286,3 +327,8 @@ export interface InstanceDocument {
286
327
  }
287
328
 
288
329
  export interface ClientConnectionDocument extends event_types.ClientConnection {}
330
+
331
+ export type CurrentDataDocumentId = CurrentDataDocument['_id'] | CurrentDataDocumentV3['_id'];
332
+ export type CommonCurrentBucket = CurrentBucket | CurrentBucketV3;
333
+ export type CommonCurrentLookup = bson.Binary | RecordedLookupV3;
334
+ export type CommonSourceTableDocument = SourceTableDocumentV1 | SourceTableDocumentV3;
@@ -0,0 +1,32 @@
1
+ import { SourceTable } from '@powersync/service-core';
2
+ import { MongoBucketBatch, MongoBucketBatchOptions } from '../MongoBucketBatch.js';
3
+ import { PersistedBatch } from '../common/PersistedBatch.js';
4
+ import { SourceRecordStore } from '../common/SourceRecordStore.js';
5
+ import { PersistedBatchV1 } from './PersistedBatchV1.js';
6
+ import { SourceRecordStoreV1 } from './SourceRecordStoreV1.js';
7
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
8
+
9
+ export class MongoBucketBatchV1 extends MongoBucketBatch {
10
+ declare public readonly db: VersionedPowerSyncMongoV1;
11
+
12
+ private readonly store: SourceRecordStore;
13
+
14
+ constructor(options: MongoBucketBatchOptions) {
15
+ super(options);
16
+ this.store = new SourceRecordStoreV1(this.db, this.group_id);
17
+ }
18
+
19
+ protected createPersistedBatch(writtenSize: number): PersistedBatch {
20
+ return new PersistedBatchV1(this.db, this.group_id, this.mapping, writtenSize, {
21
+ logger: this.logger
22
+ });
23
+ }
24
+
25
+ protected get sourceRecordStore(): SourceRecordStore {
26
+ return this.store;
27
+ }
28
+
29
+ protected async cleanupDroppedSourceTables(_tables: SourceTable[]) {
30
+ // No-op for V1: source records live in a shared collection.
31
+ }
32
+ }
@@ -0,0 +1,75 @@
1
+ import {
2
+ bson,
3
+ BucketChecksum,
4
+ FetchPartialBucketChecksum,
5
+ InternalOpId,
6
+ PartialChecksumMap
7
+ } from '@powersync/service-core';
8
+ import { FetchPartialBucketChecksumByBucket, MongoChecksums } from '../MongoChecksums.js';
9
+ import { VersionedPowerSyncMongoV1 } from './VersionedPowerSyncMongoV1.js';
10
+
11
+ export class MongoChecksumsV1 extends MongoChecksums {
12
+ declare protected readonly db: VersionedPowerSyncMongoV1;
13
+
14
+ async computePartialChecksumsDirectByBucket(
15
+ batch: FetchPartialBucketChecksumByBucket[]
16
+ ): Promise<PartialChecksumMap> {
17
+ return this.computePartialChecksumsForCollection(batch, this.db.bucketDataV1, (request) => ({
18
+ _id: {
19
+ $gt: {
20
+ g: this.group_id,
21
+ b: request.bucket,
22
+ o: request.start ?? new bson.MinKey()
23
+ },
24
+ $lte: {
25
+ g: this.group_id,
26
+ b: request.bucket,
27
+ o: request.end
28
+ }
29
+ }
30
+ }));
31
+ }
32
+
33
+ protected async fetchPreStates(
34
+ batch: FetchPartialBucketChecksum[]
35
+ ): Promise<Map<string, { opId: InternalOpId; checksum: BucketChecksum }>> {
36
+ const preFilters = batch
37
+ .filter((request) => request.start == null)
38
+ .map((request) => ({
39
+ _id: {
40
+ g: this.group_id,
41
+ b: request.bucket
42
+ },
43
+ 'compacted_state.op_id': { $exists: true, $lte: request.end }
44
+ }));
45
+
46
+ const preStates = new Map<string, { opId: InternalOpId; checksum: BucketChecksum }>();
47
+ if (preFilters.length == 0) {
48
+ return preStates;
49
+ }
50
+
51
+ const states = await this.db.bucketStateV1
52
+ .find({
53
+ $or: preFilters
54
+ })
55
+ .toArray();
56
+
57
+ for (const state of states) {
58
+ const compactedState = state.compacted_state!;
59
+ preStates.set(state._id.b, {
60
+ opId: compactedState.op_id,
61
+ checksum: {
62
+ bucket: state._id.b,
63
+ checksum: Number(compactedState.checksum),
64
+ count: compactedState.count
65
+ }
66
+ });
67
+ }
68
+
69
+ return preStates;
70
+ }
71
+
72
+ protected async computePartialChecksumsInternal(batch: FetchPartialBucketChecksum[]): Promise<PartialChecksumMap> {
73
+ return this.computePartialChecksumsDirectByBucket(batch);
74
+ }
75
+ }