@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,550 @@
1
+ import * as lib_mongo from '@powersync/lib-service-mongodb';
2
+ import { mongo } from '@powersync/lib-service-mongodb';
3
+ import {
4
+ CheckpointChanges,
5
+ GetCheckpointChangesOptions,
6
+ InternalOpId,
7
+ internalToExternalOpId,
8
+ ParameterSetLimitExceededError,
9
+ ProtocolOpId,
10
+ storage,
11
+ utils
12
+ } from '@powersync/service-core';
13
+ import { JSONBig } from '@powersync/service-jsonbig';
14
+ import { ParameterLookupRows, ScopedParameterLookup, SqliteJsonRow } from '@powersync/service-sync-rules';
15
+ import * as bson from 'bson';
16
+ import { mapOpEntry, readSingleBatch, setSessionSnapshotTime } from '../../../utils/util.js';
17
+ import { MongoBucketStorage } from '../../MongoBucketStorage.js';
18
+ import {
19
+ MongoSyncBucketStorageCheckpoint,
20
+ MongoSyncBucketStorageContext
21
+ } from '../common/MongoSyncBucketStorageContext.js';
22
+ import { CommonSourceTableDocument } from '../models.js';
23
+ import { MongoBucketBatchOptions } from '../MongoBucketBatch.js';
24
+ import { MongoChecksums } from '../MongoChecksums.js';
25
+ import { MongoCompactOptions, MongoCompactor } from '../MongoCompactor.js';
26
+ import { MongoParameterCompactor } from '../MongoParameterCompactor.js';
27
+ import { MongoPersistedSyncRulesContent } from '../MongoPersistedSyncRulesContent.js';
28
+ import { MongoSyncBucketStorage, MongoSyncBucketStorageOptions } from '../MongoSyncBucketStorage.js';
29
+ import { BucketDataDocumentV3, BucketParameterDocumentV3, loadBucketDataDocumentV3 } from './models.js';
30
+ import { MongoBucketBatchV3 } from './MongoBucketBatchV3.js';
31
+ import { MongoChecksumsV3 } from './MongoChecksumsV3.js';
32
+ import { MongoCompactorV3 } from './MongoCompactorV3.js';
33
+ import { MongoParameterCompactorV3 } from './MongoParameterCompactorV3.js';
34
+ import { deserializeParameterLookupV3, serializeParameterLookupV3 } from './MongoParameterLookupV3.js';
35
+ import { VersionedPowerSyncMongoV3 } from './VersionedPowerSyncMongoV3.js';
36
+
37
+ export class MongoSyncBucketStorageV3 extends MongoSyncBucketStorage {
38
+ // Declare types to be more specific
39
+ declare readonly db: VersionedPowerSyncMongoV3;
40
+ declare readonly checksums: MongoChecksumsV3;
41
+
42
+ constructor(
43
+ factory: MongoBucketStorage,
44
+ group_id: number,
45
+ sync_rules: MongoPersistedSyncRulesContent,
46
+ slot_name: string,
47
+ writeCheckpointMode: storage.WriteCheckpointMode | undefined,
48
+ options: MongoSyncBucketStorageOptions
49
+ ) {
50
+ super(factory, group_id, sync_rules, slot_name, writeCheckpointMode, options);
51
+ }
52
+
53
+ protected async initializeVersionStorage(): Promise<void> {
54
+ const mapping = this.mapping;
55
+ for (let source of mapping.allBucketDefinitionIds()) {
56
+ const collection = this.db.bucketDataV3(this.group_id, source).collectionName;
57
+ await this.db.db
58
+ .createCollection(collection, { clusteredIndex: { name: '_id', unique: true, key: { _id: 1 } } })
59
+ .catch((error) => {
60
+ if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceExists') {
61
+ return;
62
+ }
63
+ throw error;
64
+ });
65
+ }
66
+ for (let indexId of mapping.allParameterIndexIds()) {
67
+ await this.db.parameterIndexV3(this.group_id, indexId).createIndex(
68
+ {
69
+ lookup: 1,
70
+ key: 1,
71
+ _id: -1
72
+ },
73
+ {
74
+ name: 'lookup_op_id'
75
+ }
76
+ );
77
+ }
78
+ }
79
+
80
+ protected createMongoChecksums(options: MongoSyncBucketStorageOptions): MongoChecksums {
81
+ return new MongoChecksumsV3(this.db, this.group_id, {
82
+ ...options.checksumOptions,
83
+ storageConfig: options?.storageConfig,
84
+ mapping: this.sync_rules.mapping
85
+ });
86
+ }
87
+
88
+ createMongoCompactor(options: MongoCompactOptions): MongoCompactor {
89
+ return new MongoCompactorV3(this, this.db, options);
90
+ }
91
+
92
+ protected createMongoParameterCompactor(
93
+ checkpoint: InternalOpId,
94
+ options: storage.CompactOptions
95
+ ): MongoParameterCompactor {
96
+ return new MongoParameterCompactorV3(this.db, this.group_id, checkpoint, options);
97
+ }
98
+
99
+ protected createWriterImpl(batchOptions: MongoBucketBatchOptions): storage.BucketStorageBatch {
100
+ return new MongoBucketBatchV3(batchOptions);
101
+ }
102
+
103
+ protected sourceTableBaseId(): Partial<CommonSourceTableDocument> {
104
+ return {};
105
+ }
106
+
107
+ protected augmentCreatedSourceTableDocument(
108
+ createDoc: CommonSourceTableDocument,
109
+ options: storage.ResolveTableOptions,
110
+ candidateSourceTable: storage.SourceTable
111
+ ): void {
112
+ const bucketDataSourceIds = options.sync_rules.definition.bucketDataSources
113
+ .filter((source) => source.tableSyncsData(candidateSourceTable))
114
+ .map((source) => this.mapping.bucketSourceId(source));
115
+ const parameterLookupSourceIds = options.sync_rules.definition.bucketParameterLookupSources
116
+ .filter((source) => source.tableSyncsParameters(candidateSourceTable))
117
+ .map((source) => this.mapping.parameterLookupId(source));
118
+
119
+ Object.assign(createDoc, {
120
+ bucket_data_source_ids: bucketDataSourceIds,
121
+ parameter_lookup_source_ids: parameterLookupSourceIds
122
+ });
123
+ }
124
+
125
+ protected async initializeResolvedSourceRecords(sourceTableId: bson.ObjectId): Promise<void> {
126
+ await this.db.initializeSourceRecordsCollection(this.group_id, sourceTableId);
127
+ }
128
+
129
+ protected override get versionContext(): MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3> {
130
+ return {
131
+ db: this.db,
132
+ group_id: this.group_id,
133
+ mapping: this.mapping
134
+ };
135
+ }
136
+
137
+ protected getParameterSetsImpl(
138
+ checkpoint: MongoSyncBucketStorageCheckpoint,
139
+ lookups: ScopedParameterLookup[],
140
+ limit: number
141
+ ): Promise<ParameterLookupRows[]> {
142
+ return getParameterSetsV3(this.versionContext, checkpoint, lookups, limit);
143
+ }
144
+
145
+ protected getBucketDataBatchImpl(
146
+ checkpoint: utils.InternalOpId,
147
+ dataBuckets: storage.BucketDataRequest[],
148
+ options?: storage.BucketDataBatchOptions
149
+ ): AsyncIterable<storage.SyncBucketDataChunk> {
150
+ return getBucketDataBatchV3(this.versionContext, checkpoint, dataBuckets, options);
151
+ }
152
+
153
+ protected async clearBucketData(_signal?: AbortSignal): Promise<void> {
154
+ for (const collection of await this.db.listBucketDataCollectionsV3(this.group_id)) {
155
+ await collection.drop();
156
+ }
157
+ }
158
+
159
+ protected async clearParameterIndexes(_signal?: AbortSignal): Promise<void> {
160
+ for (const collection of await this.db.listParameterIndexCollectionsV3(this.group_id)) {
161
+ await collection.collection.drop();
162
+ }
163
+ }
164
+
165
+ protected async clearSourceRecords(_signal?: AbortSignal): Promise<void> {
166
+ for (const collection of await this.db.listSourceRecordCollectionsV3(this.group_id)) {
167
+ await collection.drop();
168
+ }
169
+ }
170
+
171
+ protected async clearBucketState(_signal?: AbortSignal): Promise<void> {
172
+ await this.db
173
+ .bucketStateV3(this.group_id)
174
+ .drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
175
+ .catch((error) => {
176
+ if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceNotFound') {
177
+ return;
178
+ }
179
+ throw error;
180
+ });
181
+ }
182
+
183
+ protected async clearSourceTables(_signal?: AbortSignal): Promise<void> {
184
+ await this.db
185
+ .sourceTablesV3(this.group_id)
186
+ .drop({ maxTimeMS: lib_mongo.db.MONGO_CLEAR_OPERATION_TIMEOUT_MS })
187
+ .catch((error) => {
188
+ if (lib_mongo.isMongoServerError(error) && error.codeName === 'NamespaceNotFound') {
189
+ return;
190
+ }
191
+ throw error;
192
+ });
193
+ }
194
+
195
+ protected getDataBucketChangesImpl(
196
+ options: GetCheckpointChangesOptions
197
+ ): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
198
+ return getDataBucketChangesV3(this.versionContext, options);
199
+ }
200
+
201
+ protected getParameterBucketChangesImpl(
202
+ options: GetCheckpointChangesOptions
203
+ ): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
204
+ return getParameterBucketChangesV3(this.versionContext, options);
205
+ }
206
+ }
207
+
208
+ export async function getParameterSetsV3(
209
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
210
+ checkpoint: MongoSyncBucketStorageCheckpoint,
211
+ lookups: ScopedParameterLookup[],
212
+ limit: number
213
+ ): Promise<ParameterLookupRows[]> {
214
+ return ctx.db.client.withSession({ snapshot: true }, async (session) => {
215
+ setSessionSnapshotTime(session, checkpoint.snapshotTime);
216
+
217
+ const buildLookupPipeline = (
218
+ lookup: ScopedParameterLookup,
219
+ index: number
220
+ ): {
221
+ collection: mongo.Collection<BucketParameterDocumentV3>;
222
+ pipeline: mongo.Document[];
223
+ } => {
224
+ const indexId = lookup.indexId;
225
+ const collection = ctx.db.parameterIndexV3(ctx.group_id, indexId);
226
+ const lookupFilter = serializeParameterLookupV3(lookup);
227
+
228
+ return {
229
+ collection,
230
+ pipeline: [
231
+ {
232
+ $match: {
233
+ lookup: lookupFilter,
234
+ _id: { $lte: checkpoint.checkpoint }
235
+ }
236
+ },
237
+ {
238
+ $sort: {
239
+ key: 1,
240
+ _id: -1
241
+ }
242
+ },
243
+ {
244
+ $group: {
245
+ _id: {
246
+ key: '$key'
247
+ },
248
+ bucket_parameters: {
249
+ $first: '$bucket_parameters'
250
+ }
251
+ }
252
+ },
253
+ {
254
+ $project: {
255
+ _id: 0,
256
+ bucket_parameters: 1,
257
+ index: { $literal: index }
258
+ }
259
+ }
260
+ ]
261
+ };
262
+ };
263
+
264
+ const [firstLookup, ...remainingLookups] = lookups;
265
+ const firstQuery = firstLookup == null ? null : buildLookupPipeline(firstLookup, 0);
266
+ if (firstQuery == null) {
267
+ return [];
268
+ }
269
+
270
+ const pipeline: mongo.Document[] = [
271
+ ...firstQuery.pipeline,
272
+ ...remainingLookups.map((lookup, indexInRemaining) => {
273
+ const query = buildLookupPipeline(lookup, indexInRemaining + 1);
274
+ return {
275
+ $unionWith: {
276
+ coll: query.collection.collectionName,
277
+ pipeline: query.pipeline
278
+ }
279
+ };
280
+ }),
281
+ { $unwind: '$bucket_parameters' },
282
+ { $limit: limit + 1 }
283
+ ];
284
+
285
+ const rows = await firstQuery.collection
286
+ .aggregate<{ index: number; bucket_parameters: SqliteJsonRow }>(pipeline, {
287
+ session,
288
+ readConcern: 'snapshot',
289
+ maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
290
+ })
291
+ .toArray()
292
+ .catch((e) => {
293
+ throw lib_mongo.mapQueryError(e, 'while evaluating parameter queries');
294
+ });
295
+
296
+ if (rows.length > limit) {
297
+ throw new ParameterSetLimitExceededError(limit);
298
+ }
299
+
300
+ const byLookup = Map.groupBy(rows, (row) => lookups[row.index]);
301
+
302
+ const results: ParameterLookupRows[] = [];
303
+ byLookup.forEach((value, lookup) => results.push({ lookup, rows: value.map((r) => r.bucket_parameters) }));
304
+ return results;
305
+ });
306
+ }
307
+
308
+ export async function* getBucketDataBatchV3(
309
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
310
+ checkpoint: utils.InternalOpId,
311
+ dataBuckets: storage.BucketDataRequest[],
312
+ options?: storage.BucketDataBatchOptions
313
+ ): AsyncIterable<storage.SyncBucketDataChunk> {
314
+ if (dataBuckets.length == 0) {
315
+ return;
316
+ }
317
+
318
+ if (checkpoint == null) {
319
+ throw new Error('checkpoint is null');
320
+ }
321
+
322
+ const batchLimit = options?.limit ?? storage.DEFAULT_DOCUMENT_BATCH_LIMIT;
323
+ const chunkSizeLimitBytes = options?.chunkLimitBytes ?? storage.DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES;
324
+ const end = checkpoint;
325
+ let remainingLimit = batchLimit;
326
+
327
+ const requestsByDefinition = new Map<string, storage.BucketDataRequest[]>();
328
+ for (const request of dataBuckets) {
329
+ const definitionId = ctx.mapping.bucketSourceId(request.source);
330
+ const requests = requestsByDefinition.get(definitionId) ?? [];
331
+ requests.push(request);
332
+ requestsByDefinition.set(definitionId, requests);
333
+ }
334
+
335
+ const definitionGroups = Array.from(requestsByDefinition.entries());
336
+ for (let groupIndex = 0; groupIndex < definitionGroups.length && remainingLimit > 0; groupIndex++) {
337
+ const [definitionId, requests] = definitionGroups[groupIndex];
338
+ const hasLaterDefinitionGroups = groupIndex < definitionGroups.length - 1;
339
+ const bucketMap = new Map(requests.map((request) => [request.bucket, request.start]));
340
+ const filters: mongo.Filter<BucketDataDocumentV3>[] = Array.from(bucketMap.entries()).map(([bucket, start]) => ({
341
+ _id: {
342
+ $gt: {
343
+ b: bucket,
344
+ o: start
345
+ },
346
+ $lte: {
347
+ b: bucket,
348
+ o: end as any
349
+ }
350
+ }
351
+ }));
352
+
353
+ const cursor = ctx.db.bucketDataV3(ctx.group_id, definitionId).find(
354
+ {
355
+ $or: filters
356
+ },
357
+ {
358
+ session: undefined,
359
+ sort: { _id: 1 },
360
+ limit: remainingLimit,
361
+ batchSize: remainingLimit + 1,
362
+ raw: true,
363
+ maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
364
+ }
365
+ ) as unknown as mongo.FindCursor<Buffer>;
366
+
367
+ let { data, hasMore: batchHasMore } = await readSingleBatch(cursor).catch((e) => {
368
+ throw lib_mongo.mapQueryError(e, 'while reading bucket data');
369
+ });
370
+ if (data.length == remainingLimit) {
371
+ batchHasMore = true;
372
+ }
373
+ if (data.length == 0) {
374
+ continue;
375
+ }
376
+
377
+ remainingLimit -= data.length;
378
+
379
+ let chunkSizeBytes = 0;
380
+ let currentChunk: utils.SyncBucketData | null = null;
381
+ let targetOp: InternalOpId | null = null;
382
+
383
+ for (let rawData of data) {
384
+ const row = loadBucketDataDocumentV3(
385
+ { replicationStreamId: ctx.group_id, definitionId },
386
+ bson.deserialize(rawData, storage.BSON_DESERIALIZE_INTERNAL_OPTIONS) as BucketDataDocumentV3
387
+ );
388
+ const bucket = row.bucketKey.bucket;
389
+
390
+ if (currentChunk == null || currentChunk.bucket != bucket || chunkSizeBytes >= chunkSizeLimitBytes) {
391
+ let start: ProtocolOpId | undefined = undefined;
392
+ if (currentChunk != null) {
393
+ if (currentChunk.bucket == bucket) {
394
+ currentChunk.has_more = true;
395
+ start = currentChunk.next_after;
396
+ }
397
+
398
+ const yieldChunk = currentChunk;
399
+ currentChunk = null;
400
+ chunkSizeBytes = 0;
401
+ yield { chunkData: yieldChunk, targetOp: targetOp };
402
+ targetOp = null;
403
+ }
404
+
405
+ if (start == null) {
406
+ const startOpId = bucketMap.get(bucket);
407
+ if (startOpId == null) {
408
+ throw new Error(`data for unexpected bucket: ${bucket}`);
409
+ }
410
+ start = internalToExternalOpId(startOpId);
411
+ }
412
+ currentChunk = {
413
+ bucket,
414
+ after: start,
415
+ has_more: false,
416
+ data: [],
417
+ next_after: start
418
+ };
419
+ }
420
+
421
+ const entry = mapOpEntry(row);
422
+ if (row.target_op != null && (targetOp == null || row.target_op > targetOp)) {
423
+ targetOp = row.target_op;
424
+ }
425
+
426
+ currentChunk.data.push(entry);
427
+ currentChunk.next_after = entry.op_id;
428
+ chunkSizeBytes += rawData.byteLength;
429
+ }
430
+
431
+ if (currentChunk != null) {
432
+ const yieldChunk = currentChunk;
433
+ yieldChunk.has_more = batchHasMore || (remainingLimit <= 0 && hasLaterDefinitionGroups);
434
+ yield { chunkData: yieldChunk, targetOp: targetOp };
435
+ }
436
+
437
+ if (batchHasMore || remainingLimit <= 0) {
438
+ return;
439
+ }
440
+ }
441
+ }
442
+
443
+ export async function getDataBucketChangesV3(
444
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
445
+ options: GetCheckpointChangesOptions
446
+ ): Promise<Pick<CheckpointChanges, 'updatedDataBuckets' | 'invalidateDataBuckets'>> {
447
+ const limit = 1000;
448
+ const bucketStateUpdates = await ctx.db
449
+ .bucketStateV3(ctx.group_id)
450
+ .aggregate<{ _id: string; last_op: bigint }>(
451
+ [
452
+ {
453
+ $match: {
454
+ last_op: { $gt: options.lastCheckpoint.checkpoint }
455
+ }
456
+ },
457
+ {
458
+ $group: {
459
+ _id: '$_id.b',
460
+ last_op: { $max: '$last_op' }
461
+ }
462
+ },
463
+ {
464
+ $sort: {
465
+ last_op: 1
466
+ }
467
+ },
468
+ {
469
+ $limit: limit + 1
470
+ }
471
+ ],
472
+ { maxTimeMS: lib_mongo.MONGO_CHECKSUM_TIMEOUT_MS }
473
+ )
474
+ .toArray();
475
+
476
+ const buckets = bucketStateUpdates.map((doc) => doc._id);
477
+ const invalidateDataBuckets = buckets.length > limit;
478
+
479
+ return {
480
+ invalidateDataBuckets,
481
+ updatedDataBuckets: invalidateDataBuckets ? new Set<string>() : new Set(buckets)
482
+ };
483
+ }
484
+
485
+ export async function getParameterBucketChangesV3(
486
+ ctx: MongoSyncBucketStorageContext<VersionedPowerSyncMongoV3>,
487
+ options: GetCheckpointChangesOptions
488
+ ): Promise<Pick<CheckpointChanges, 'updatedParameterLookups' | 'invalidateParameterBuckets'>> {
489
+ const limit = 1000;
490
+ const indexIds = ctx.mapping.allParameterIndexIds();
491
+ const collections = indexIds.map((indexId) => ({
492
+ indexId,
493
+ collection: ctx.db.parameterIndexV3(ctx.group_id, indexId)
494
+ }));
495
+ if (collections.length == 0) {
496
+ return {
497
+ invalidateParameterBuckets: false,
498
+ updatedParameterLookups: new Set<string>()
499
+ };
500
+ }
501
+ const checkpointFilter = {
502
+ _id: { $gt: options.lastCheckpoint.checkpoint, $lte: options.nextCheckpoint.checkpoint }
503
+ };
504
+ const pipelineForCollection = (indexId: string) => [
505
+ {
506
+ $match: checkpointFilter
507
+ },
508
+ {
509
+ $project: {
510
+ _id: 0,
511
+ lookup: 1,
512
+ indexId: { $literal: indexId }
513
+ }
514
+ }
515
+ ];
516
+ const [firstCollection, ...remainingCollections] = collections;
517
+ const parameterUpdates = await firstCollection.collection
518
+ .aggregate<{ lookup: bson.Binary; indexId: string }>(
519
+ [
520
+ ...pipelineForCollection(firstCollection.indexId),
521
+ ...remainingCollections.map((collection) => {
522
+ return {
523
+ $unionWith: {
524
+ coll: collection.collection.collectionName,
525
+ pipeline: pipelineForCollection(collection.indexId)
526
+ }
527
+ };
528
+ }),
529
+ {
530
+ $limit: limit + 1
531
+ }
532
+ ],
533
+ {
534
+ batchSize: limit + 2,
535
+ maxTimeMS: lib_mongo.db.MONGO_OPERATION_TIMEOUT_MS
536
+ }
537
+ )
538
+ .toArray();
539
+
540
+ const invalidateParameterUpdates = parameterUpdates.length > limit;
541
+
542
+ return {
543
+ invalidateParameterBuckets: invalidateParameterUpdates,
544
+ updatedParameterLookups: invalidateParameterUpdates
545
+ ? new Set<string>()
546
+ : new Set<string>(
547
+ parameterUpdates.map((p) => JSONBig.stringify(deserializeParameterLookupV3(p.lookup, p.indexId)))
548
+ )
549
+ };
550
+ }