@powersync/service-module-mongodb-storage 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/storage/implementation/MongoBucketBatch.d.ts +1 -0
- package/dist/storage/implementation/MongoBucketBatch.js +9 -0
- package/dist/storage/implementation/MongoBucketBatch.js.map +1 -1
- package/dist/storage/implementation/MongoCompactor.d.ts +6 -0
- package/dist/storage/implementation/MongoCompactor.js +40 -12
- package/dist/storage/implementation/MongoCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoParameterCompactor.js +13 -1
- package/dist/storage/implementation/MongoParameterCompactor.js.map +1 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.d.ts +5 -1
- package/dist/storage/implementation/MongoSyncBucketStorage.js +28 -26
- package/dist/storage/implementation/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/implementation/models.d.ts +2 -2
- package/package.json +6 -6
- package/src/storage/implementation/MongoBucketBatch.ts +10 -0
- package/src/storage/implementation/MongoCompactor.ts +40 -12
- package/src/storage/implementation/MongoParameterCompactor.ts +14 -1
- package/src/storage/implementation/MongoSyncBucketStorage.ts +17 -14
- package/src/storage/implementation/models.ts +2 -2
- package/test/src/__snapshots__/storage_sync.test.ts.snap +24 -24
- package/test/src/storage_compacting.test.ts +97 -28
- package/test/src/storage_sync.test.ts +43 -44
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,41 +1,48 @@
|
|
|
1
1
|
import { storage, SyncRulesBucketStorage, updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
2
2
|
import { bucketRequest, register, test_utils } from '@powersync/service-core-tests';
|
|
3
3
|
import { describe, expect, test } from 'vitest';
|
|
4
|
+
import { MongoCompactor } from '../../src/storage/implementation/MongoCompactor.js';
|
|
4
5
|
import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js';
|
|
5
6
|
|
|
6
7
|
describe('Mongo Sync Bucket Storage Compact', () => {
|
|
7
8
|
register.registerCompactTests(INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
8
|
-
const TEST_TABLE = test_utils.makeTestTable('test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
9
9
|
|
|
10
10
|
describe('with blank bucket_state', () => {
|
|
11
11
|
// This can happen when migrating from older service versions, that did not populate bucket_state yet.
|
|
12
|
-
const populate = async (bucketStorage: SyncRulesBucketStorage) => {
|
|
13
|
-
await bucketStorage.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
12
|
+
const populate = async (bucketStorage: SyncRulesBucketStorage, sourceTableIndex: number) => {
|
|
13
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
14
|
+
|
|
15
|
+
const sourceTable = await test_utils.resolveTestTable(
|
|
16
|
+
writer,
|
|
17
|
+
'test',
|
|
18
|
+
['id'],
|
|
19
|
+
INITIALIZED_MONGO_STORAGE_FACTORY,
|
|
20
|
+
sourceTableIndex
|
|
21
|
+
);
|
|
22
|
+
await writer.markAllSnapshotDone('1/1');
|
|
23
|
+
|
|
24
|
+
await writer.save({
|
|
25
|
+
sourceTable,
|
|
26
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
27
|
+
after: {
|
|
28
|
+
id: 't1',
|
|
29
|
+
owner_id: 'u1'
|
|
30
|
+
},
|
|
31
|
+
afterReplicaId: test_utils.rid('t1')
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await writer.save({
|
|
35
|
+
sourceTable,
|
|
36
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
37
|
+
after: {
|
|
38
|
+
id: 't2',
|
|
39
|
+
owner_id: 'u2'
|
|
40
|
+
},
|
|
41
|
+
afterReplicaId: test_utils.rid('t2')
|
|
37
42
|
});
|
|
38
43
|
|
|
44
|
+
await writer.commit('1/1');
|
|
45
|
+
|
|
39
46
|
return bucketStorage.getCheckpoint();
|
|
40
47
|
};
|
|
41
48
|
|
|
@@ -50,7 +57,7 @@ bucket_definitions:
|
|
|
50
57
|
`)
|
|
51
58
|
);
|
|
52
59
|
const bucketStorage = factory.getInstance(syncRules);
|
|
53
|
-
const { checkpoint } = await populate(bucketStorage);
|
|
60
|
+
const { checkpoint } = await populate(bucketStorage, 1);
|
|
54
61
|
|
|
55
62
|
return { bucketStorage, checkpoint, factory, syncRules };
|
|
56
63
|
};
|
|
@@ -102,7 +109,7 @@ bucket_definitions:
|
|
|
102
109
|
);
|
|
103
110
|
const bucketStorage = factory.getInstance(syncRules);
|
|
104
111
|
|
|
105
|
-
await populate(bucketStorage);
|
|
112
|
+
await populate(bucketStorage, 2);
|
|
106
113
|
const { checkpoint } = await bucketStorage.getCheckpoint();
|
|
107
114
|
|
|
108
115
|
// Default is to small small numbers - should be a no-op
|
|
@@ -140,6 +147,68 @@ bucket_definitions:
|
|
|
140
147
|
count: 1
|
|
141
148
|
});
|
|
142
149
|
});
|
|
150
|
+
|
|
151
|
+
test('dirty bucket discovery handles bigint bucket_state bytes', async () => {
|
|
152
|
+
await using factory = await INITIALIZED_MONGO_STORAGE_FACTORY.factory();
|
|
153
|
+
const syncRules = await factory.updateSyncRules(
|
|
154
|
+
updateSyncRulesFromYaml(`
|
|
155
|
+
bucket_definitions:
|
|
156
|
+
global:
|
|
157
|
+
data: [select * from test]
|
|
158
|
+
`)
|
|
159
|
+
);
|
|
160
|
+
const bucketStorage = factory.getInstance(syncRules);
|
|
161
|
+
|
|
162
|
+
// This simulates bucket_state created using bigint bytes.
|
|
163
|
+
// This typically happens when buckets get very large (> 2GiB). We don't want to create that much
|
|
164
|
+
// data in the tests, so we directly insert the bucket_state here.
|
|
165
|
+
await factory.db.bucket_state.insertOne({
|
|
166
|
+
_id: {
|
|
167
|
+
g: bucketStorage.group_id,
|
|
168
|
+
b: 'global[]'
|
|
169
|
+
},
|
|
170
|
+
last_op: 5n,
|
|
171
|
+
compacted_state: {
|
|
172
|
+
op_id: 3n,
|
|
173
|
+
count: 3,
|
|
174
|
+
checksum: 0n,
|
|
175
|
+
bytes: 7n
|
|
176
|
+
},
|
|
177
|
+
estimate_since_compact: {
|
|
178
|
+
count: 2,
|
|
179
|
+
bytes: 5n
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// This test uses a couple of internal APIs of the compactor - there is no simple way
|
|
184
|
+
// to test this using the current public APIs.
|
|
185
|
+
const compactor = new MongoCompactor(bucketStorage, (bucketStorage as any).db, {
|
|
186
|
+
maxOpId: 5n
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const dirtyBuckets = (compactor as any).dirtyBucketBatches({
|
|
190
|
+
minBucketChanges: 1,
|
|
191
|
+
minChangeRatio: 0.39
|
|
192
|
+
});
|
|
193
|
+
const firstBatch = await dirtyBuckets.next();
|
|
194
|
+
|
|
195
|
+
expect(firstBatch.done).toBe(false);
|
|
196
|
+
expect(firstBatch.value).toHaveLength(1);
|
|
197
|
+
expect(firstBatch.value[0].bucket).toBe('global[]');
|
|
198
|
+
expect(firstBatch.value[0].estimatedCount).toBe(5);
|
|
199
|
+
expect(typeof firstBatch.value[0].estimatedCount).toBe('number');
|
|
200
|
+
expect(firstBatch.value[0].dirtyRatio).toBeCloseTo(5 / 12);
|
|
201
|
+
|
|
202
|
+
const checksumBuckets = await (compactor as any).dirtyBucketBatchForChecksums({
|
|
203
|
+
minBucketChanges: 1
|
|
204
|
+
});
|
|
205
|
+
expect(checksumBuckets).toEqual([
|
|
206
|
+
{
|
|
207
|
+
bucket: 'global[]',
|
|
208
|
+
estimatedCount: 5
|
|
209
|
+
}
|
|
210
|
+
]);
|
|
211
|
+
});
|
|
143
212
|
});
|
|
144
213
|
});
|
|
145
214
|
|
|
@@ -8,8 +8,6 @@ function registerSyncStorageTests(storageConfig: storage.TestStorageConfig, stor
|
|
|
8
8
|
storageVersion,
|
|
9
9
|
tableIdStrings: storageConfig.tableIdStrings
|
|
10
10
|
});
|
|
11
|
-
const TEST_TABLE = test_utils.makeTestTable('test', ['id'], storageConfig);
|
|
12
|
-
|
|
13
11
|
// The split of returned results can vary depending on storage drivers
|
|
14
12
|
test('large batch (2)', async () => {
|
|
15
13
|
// Test syncing a batch of data that is small in count,
|
|
@@ -28,56 +26,57 @@ function registerSyncStorageTests(storageConfig: storage.TestStorageConfig, stor
|
|
|
28
26
|
)
|
|
29
27
|
);
|
|
30
28
|
const bucketStorage = factory.getInstance(syncRules);
|
|
31
|
-
const globalBucket = bucketRequest(syncRules, 'global[]');
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
31
|
+
|
|
32
|
+
const sourceTable = await test_utils.resolveTestTable(writer, 'test', ['id'], INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
35
33
|
|
|
36
|
-
|
|
34
|
+
const largeDescription = '0123456789'.repeat(2_000_00);
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
await writer.save({
|
|
37
|
+
sourceTable,
|
|
38
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
39
|
+
after: {
|
|
40
|
+
id: 'test1',
|
|
41
|
+
description: 'test1'
|
|
42
|
+
},
|
|
43
|
+
afterReplicaId: test_utils.rid('test1')
|
|
44
|
+
});
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
await writer.save({
|
|
47
|
+
sourceTable,
|
|
48
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
49
|
+
after: {
|
|
50
|
+
id: 'large1',
|
|
51
|
+
description: largeDescription
|
|
52
|
+
},
|
|
53
|
+
afterReplicaId: test_utils.rid('large1')
|
|
54
|
+
});
|
|
57
55
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
// Large enough to split the returned batch
|
|
57
|
+
await writer.save({
|
|
58
|
+
sourceTable,
|
|
59
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
60
|
+
after: {
|
|
61
|
+
id: 'large2',
|
|
62
|
+
description: largeDescription
|
|
63
|
+
},
|
|
64
|
+
afterReplicaId: test_utils.rid('large2')
|
|
65
|
+
});
|
|
68
66
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
67
|
+
await writer.save({
|
|
68
|
+
sourceTable,
|
|
69
|
+
tag: storage.SaveOperationTag.INSERT,
|
|
70
|
+
after: {
|
|
71
|
+
id: 'test3',
|
|
72
|
+
description: 'test3'
|
|
73
|
+
},
|
|
74
|
+
afterReplicaId: test_utils.rid('test3')
|
|
78
75
|
});
|
|
79
76
|
|
|
80
|
-
const
|
|
77
|
+
const flushResult = await writer.flush();
|
|
78
|
+
|
|
79
|
+
const checkpoint = flushResult!.flushed_op;
|
|
81
80
|
|
|
82
81
|
const options: storage.BucketDataBatchOptions = {};
|
|
83
82
|
const batch1 = await test_utils.fromAsync(
|