@powersync/service-core-tests 0.0.0-dev-20260225160713 → 0.0.0-dev-20260511080634

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 (55) hide show
  1. package/CHANGELOG.md +95 -3
  2. package/dist/test-utils/MetricsHelper.js +1 -1
  3. package/dist/test-utils/MetricsHelper.js.map +1 -1
  4. package/dist/test-utils/StorageDataHelpers.d.ts +8 -0
  5. package/dist/test-utils/StorageDataHelpers.js +33 -0
  6. package/dist/test-utils/StorageDataHelpers.js.map +1 -0
  7. package/dist/test-utils/general-utils.d.ts +22 -3
  8. package/dist/test-utils/general-utils.js +56 -3
  9. package/dist/test-utils/general-utils.js.map +1 -1
  10. package/dist/test-utils/stream_utils.js +2 -2
  11. package/dist/test-utils/stream_utils.js.map +1 -1
  12. package/dist/test-utils/test-utils-index.d.ts +1 -0
  13. package/dist/test-utils/test-utils-index.js +1 -0
  14. package/dist/test-utils/test-utils-index.js.map +1 -1
  15. package/dist/tests/register-compacting-tests.d.ts +1 -1
  16. package/dist/tests/register-compacting-tests.js +514 -587
  17. package/dist/tests/register-compacting-tests.js.map +1 -1
  18. package/dist/tests/register-data-storage-checkpoint-tests.d.ts +1 -1
  19. package/dist/tests/register-data-storage-checkpoint-tests.js +200 -301
  20. package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
  21. package/dist/tests/register-data-storage-data-tests.d.ts +2 -2
  22. package/dist/tests/register-data-storage-data-tests.js +1220 -1041
  23. package/dist/tests/register-data-storage-data-tests.js.map +1 -1
  24. package/dist/tests/register-data-storage-parameter-tests.d.ts +1 -1
  25. package/dist/tests/register-data-storage-parameter-tests.js +681 -632
  26. package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
  27. package/dist/tests/register-migration-tests.js +63 -139
  28. package/dist/tests/register-migration-tests.js.map +1 -1
  29. package/dist/tests/register-parameter-compacting-tests.d.ts +1 -1
  30. package/dist/tests/register-parameter-compacting-tests.js +121 -201
  31. package/dist/tests/register-parameter-compacting-tests.js.map +1 -1
  32. package/dist/tests/register-sync-tests.d.ts +2 -1
  33. package/dist/tests/register-sync-tests.js +974 -1136
  34. package/dist/tests/register-sync-tests.js.map +1 -1
  35. package/dist/tests/tests-index.d.ts +4 -4
  36. package/dist/tests/tests-index.js +4 -4
  37. package/dist/tests/tests-index.js.map +1 -1
  38. package/dist/tests/util.d.ts +5 -4
  39. package/dist/tests/util.js +27 -12
  40. package/dist/tests/util.js.map +1 -1
  41. package/package.json +6 -6
  42. package/src/test-utils/MetricsHelper.ts +1 -1
  43. package/src/test-utils/StorageDataHelpers.ts +44 -0
  44. package/src/test-utils/general-utils.ts +81 -4
  45. package/src/test-utils/stream_utils.ts +2 -2
  46. package/src/test-utils/test-utils-index.ts +1 -0
  47. package/src/tests/register-compacting-tests.ts +376 -322
  48. package/src/tests/register-data-storage-checkpoint-tests.ts +85 -53
  49. package/src/tests/register-data-storage-data-tests.ts +1076 -563
  50. package/src/tests/register-data-storage-parameter-tests.ts +631 -327
  51. package/src/tests/register-parameter-compacting-tests.ts +92 -96
  52. package/src/tests/register-sync-tests.ts +463 -379
  53. package/src/tests/tests-index.ts +4 -4
  54. package/src/tests/util.ts +46 -17
  55. package/tsconfig.tsbuildinfo +1 -1
@@ -1,376 +1,276 @@
1
- var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
- if (value !== null && value !== void 0) {
3
- if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
- var dispose, inner;
5
- if (async) {
6
- if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
- dispose = value[Symbol.asyncDispose];
8
- }
9
- if (dispose === void 0) {
10
- if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
- dispose = value[Symbol.dispose];
12
- if (async) inner = dispose;
13
- }
14
- if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
- if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
- env.stack.push({ value: value, dispose: dispose, async: async });
17
- }
18
- else if (async) {
19
- env.stack.push({ async: true });
20
- }
21
- return value;
22
- };
23
- var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
- return function (env) {
25
- function fail(e) {
26
- env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
- env.hasError = true;
28
- }
29
- var r, s = 0;
30
- function next() {
31
- while (r = env.stack.pop()) {
32
- try {
33
- if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
- if (r.dispose) {
35
- var result = r.dispose.call(r.value);
36
- if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
- }
38
- else s |= 1;
39
- }
40
- catch (e) {
41
- fail(e);
42
- }
43
- }
44
- if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
- if (env.hasError) throw env.error;
46
- }
47
- return next();
48
- };
49
- })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
- var e = new Error(message);
51
- return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
- });
53
- import { storage, updateSyncRulesFromYaml } from '@powersync/service-core';
1
+ import { addChecksums, storage, updateSyncRulesFromYaml } from '@powersync/service-core';
54
2
  import { expect, test } from 'vitest';
55
3
  import * as test_utils from '../test-utils/test-utils-index.js';
56
- import { bucketRequest, bucketRequestMap, bucketRequests } from './util.js';
57
- const TEST_TABLE = test_utils.makeTestTable('test', ['id']);
58
- export function registerCompactTests(generateStorageFactory) {
4
+ import { bucketRequest } from '../test-utils/test-utils-index.js';
5
+ import { bucketRequestMap, bucketRequests } from './util.js';
6
+ export function registerCompactTests(config) {
7
+ const generateStorageFactory = config.factory;
59
8
  test('compacting (1)', async () => {
60
- const env_1 = { stack: [], error: void 0, hasError: false };
61
- try {
62
- const factory = __addDisposableResource(env_1, await generateStorageFactory(), true);
63
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
9
+ await using factory = await generateStorageFactory();
10
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
64
11
  bucket_definitions:
65
12
  global:
66
13
  data: [select * from test]
67
14
  `));
68
- const bucketStorage = factory.getInstance(syncRules);
69
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
70
- await batch.save({
71
- sourceTable: TEST_TABLE,
72
- tag: storage.SaveOperationTag.INSERT,
73
- after: {
74
- id: 't1'
75
- },
76
- afterReplicaId: test_utils.rid('t1')
77
- });
78
- await batch.save({
79
- sourceTable: TEST_TABLE,
80
- tag: storage.SaveOperationTag.INSERT,
81
- after: {
82
- id: 't2'
83
- },
84
- afterReplicaId: test_utils.rid('t2')
85
- });
86
- await batch.save({
87
- sourceTable: TEST_TABLE,
88
- tag: storage.SaveOperationTag.UPDATE,
89
- after: {
90
- id: 't2'
91
- },
92
- afterReplicaId: test_utils.rid('t2')
93
- });
94
- await batch.commit('1/1');
95
- });
96
- const checkpoint = result.flushed_op;
97
- const batchBefore = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, bucketRequestMap(syncRules, [['global[]', 0n]])));
98
- const dataBefore = batchBefore.chunkData.data;
99
- const checksumBefore = await bucketStorage.getChecksums(checkpoint, bucketRequests(syncRules, ['global[]']));
100
- expect(dataBefore).toMatchObject([
101
- {
102
- checksum: 2634521662,
103
- object_id: 't1',
104
- op: 'PUT',
105
- op_id: '1'
106
- },
107
- {
108
- checksum: 4243212114,
109
- object_id: 't2',
110
- op: 'PUT',
111
- op_id: '2'
112
- },
113
- {
114
- checksum: 4243212114,
115
- object_id: 't2',
116
- op: 'PUT',
117
- op_id: '3'
118
- }
119
- ]);
120
- expect(batchBefore.targetOp).toEqual(null);
121
- await bucketStorage.compact({
122
- clearBatchLimit: 2,
123
- moveBatchLimit: 1,
124
- moveBatchQueryLimit: 1,
125
- minBucketChanges: 1,
126
- minChangeRatio: 0
127
- });
128
- const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, bucketRequestMap(syncRules, [['global[]', 0n]])));
129
- const dataAfter = batchAfter.chunkData.data;
130
- const checksumAfter = await bucketStorage.getChecksums(checkpoint, bucketRequests(syncRules, ['global[]']));
131
- bucketStorage.clearChecksumCache();
132
- const checksumAfter2 = await bucketStorage.getChecksums(checkpoint, bucketRequests(syncRules, ['global[]']));
133
- expect(batchAfter.targetOp).toEqual(3n);
134
- expect(dataAfter).toMatchObject([
135
- {
136
- checksum: 2634521662,
137
- object_id: 't1',
138
- op: 'PUT',
139
- op_id: '1'
140
- },
141
- {
142
- checksum: 4243212114,
143
- op: 'MOVE',
144
- op_id: '2'
145
- },
146
- {
147
- checksum: 4243212114,
148
- object_id: 't2',
149
- op: 'PUT',
150
- op_id: '3'
151
- }
152
- ]);
153
- expect(checksumAfter.get(bucketRequest(syncRules, 'global[]'))).toEqual(checksumBefore.get(bucketRequest(syncRules, 'global[]')));
154
- expect(checksumAfter2.get(bucketRequest(syncRules, 'global[]'))).toEqual(checksumBefore.get(bucketRequest(syncRules, 'global[]')));
155
- test_utils.validateCompactedBucket(dataBefore, dataAfter);
156
- }
157
- catch (e_1) {
158
- env_1.error = e_1;
159
- env_1.hasError = true;
160
- }
161
- finally {
162
- const result_1 = __disposeResources(env_1);
163
- if (result_1)
164
- await result_1;
165
- }
15
+ const bucketStorage = factory.getInstance(syncRules);
16
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
17
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
18
+ await writer.markAllSnapshotDone('1/1');
19
+ await writer.save({
20
+ sourceTable: testTable,
21
+ tag: storage.SaveOperationTag.INSERT,
22
+ after: {
23
+ id: 't1'
24
+ },
25
+ afterReplicaId: test_utils.rid('t1')
26
+ });
27
+ await writer.save({
28
+ sourceTable: testTable,
29
+ tag: storage.SaveOperationTag.INSERT,
30
+ after: {
31
+ id: 't2'
32
+ },
33
+ afterReplicaId: test_utils.rid('t2')
34
+ });
35
+ await writer.save({
36
+ sourceTable: testTable,
37
+ tag: storage.SaveOperationTag.UPDATE,
38
+ after: {
39
+ id: 't2'
40
+ },
41
+ afterReplicaId: test_utils.rid('t2')
42
+ });
43
+ await writer.commit('1/1');
44
+ await writer.flush();
45
+ const checkpoint = writer.last_flushed_op;
46
+ const request = bucketRequest(syncRules, 'global[]');
47
+ const batchBefore = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
48
+ const dataBefore = batchBefore.chunkData.data;
49
+ const checksumBefore = await bucketStorage.getChecksums(checkpoint, [request]);
50
+ expect(dataBefore).toMatchObject([
51
+ {
52
+ object_id: 't1',
53
+ op: 'PUT',
54
+ op_id: '1'
55
+ },
56
+ {
57
+ object_id: 't2',
58
+ op: 'PUT',
59
+ op_id: '2'
60
+ },
61
+ {
62
+ object_id: 't2',
63
+ op: 'PUT',
64
+ op_id: '3'
65
+ }
66
+ ]);
67
+ expect(batchBefore.targetOp).toEqual(null);
68
+ await bucketStorage.compact({
69
+ clearBatchLimit: 2,
70
+ moveBatchLimit: 1,
71
+ moveBatchQueryLimit: 1,
72
+ minBucketChanges: 1,
73
+ minChangeRatio: 0
74
+ });
75
+ const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
76
+ const dataAfter = batchAfter.chunkData.data;
77
+ const checksumAfter = await bucketStorage.getChecksums(checkpoint, [request]);
78
+ bucketStorage.clearChecksumCache();
79
+ const checksumAfter2 = await bucketStorage.getChecksums(checkpoint, [request]);
80
+ expect(batchAfter.targetOp).toEqual(3n);
81
+ expect(dataAfter).toMatchObject([
82
+ dataBefore[0],
83
+ {
84
+ checksum: dataBefore[1].checksum,
85
+ op: 'MOVE',
86
+ op_id: '2'
87
+ },
88
+ {
89
+ checksum: dataBefore[2].checksum,
90
+ object_id: 't2',
91
+ op: 'PUT',
92
+ op_id: '3'
93
+ }
94
+ ]);
95
+ expect(checksumAfter.get(request.bucket)).toEqual(checksumBefore.get(request.bucket));
96
+ expect(checksumAfter2.get(request.bucket)).toEqual(checksumBefore.get(request.bucket));
97
+ test_utils.validateCompactedBucket(dataBefore, dataAfter);
166
98
  });
167
99
  test('compacting (2)', async () => {
168
- const env_2 = { stack: [], error: void 0, hasError: false };
169
- try {
170
- const factory = __addDisposableResource(env_2, await generateStorageFactory(), true);
171
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
100
+ await using factory = await generateStorageFactory();
101
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
172
102
  bucket_definitions:
173
103
  global:
174
104
  data: [select * from test]
175
105
  `));
176
- const bucketStorage = factory.getInstance(syncRules);
177
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
178
- await batch.save({
179
- sourceTable: TEST_TABLE,
180
- tag: storage.SaveOperationTag.INSERT,
181
- after: {
182
- id: 't1'
183
- },
184
- afterReplicaId: test_utils.rid('t1')
185
- });
186
- await batch.save({
187
- sourceTable: TEST_TABLE,
188
- tag: storage.SaveOperationTag.INSERT,
189
- after: {
190
- id: 't2'
191
- },
192
- afterReplicaId: test_utils.rid('t2')
193
- });
194
- await batch.save({
195
- sourceTable: TEST_TABLE,
196
- tag: storage.SaveOperationTag.DELETE,
197
- before: {
198
- id: 't1'
199
- },
200
- beforeReplicaId: test_utils.rid('t1')
201
- });
202
- await batch.save({
203
- sourceTable: TEST_TABLE,
204
- tag: storage.SaveOperationTag.UPDATE,
205
- after: {
206
- id: 't2'
207
- },
208
- afterReplicaId: test_utils.rid('t2')
209
- });
210
- await batch.commit('1/1');
211
- });
212
- const checkpoint = result.flushed_op;
213
- const batchBefore = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, bucketRequestMap(syncRules, [['global[]', 0n]])));
214
- const dataBefore = batchBefore.chunkData.data;
215
- const checksumBefore = await bucketStorage.getChecksums(checkpoint, bucketRequests(syncRules, ['global[]']));
216
- expect(dataBefore).toMatchObject([
217
- {
218
- checksum: 2634521662,
219
- object_id: 't1',
220
- op: 'PUT',
221
- op_id: '1'
222
- },
223
- {
224
- checksum: 4243212114,
225
- object_id: 't2',
226
- op: 'PUT',
227
- op_id: '2'
228
- },
229
- {
230
- checksum: 4228978084,
231
- object_id: 't1',
232
- op: 'REMOVE',
233
- op_id: '3'
234
- },
235
- {
236
- checksum: 4243212114,
237
- object_id: 't2',
238
- op: 'PUT',
239
- op_id: '4'
240
- }
241
- ]);
242
- await bucketStorage.compact({
243
- clearBatchLimit: 2,
244
- moveBatchLimit: 1,
245
- moveBatchQueryLimit: 1,
246
- minBucketChanges: 1,
247
- minChangeRatio: 0
248
- });
249
- const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, bucketRequestMap(syncRules, [['global[]', 0n]])));
250
- const dataAfter = batchAfter.chunkData.data;
251
- bucketStorage.clearChecksumCache();
252
- const checksumAfter = await bucketStorage.getChecksums(checkpoint, bucketRequests(syncRules, ['global[]']));
253
- expect(batchAfter.targetOp).toEqual(4n);
254
- expect(dataAfter).toMatchObject([
255
- {
256
- checksum: -1778190028,
257
- op: 'CLEAR',
258
- op_id: '3'
259
- },
260
- {
261
- checksum: 4243212114,
262
- object_id: 't2',
263
- op: 'PUT',
264
- op_id: '4'
265
- }
266
- ]);
267
- expect(checksumAfter.get(bucketRequest(syncRules, 'global[]'))).toEqual({
268
- ...checksumBefore.get(bucketRequest(syncRules, 'global[]')),
269
- count: 2
270
- });
271
- test_utils.validateCompactedBucket(dataBefore, dataAfter);
272
- }
273
- catch (e_2) {
274
- env_2.error = e_2;
275
- env_2.hasError = true;
276
- }
277
- finally {
278
- const result_2 = __disposeResources(env_2);
279
- if (result_2)
280
- await result_2;
281
- }
106
+ const bucketStorage = factory.getInstance(syncRules);
107
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
108
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
109
+ await writer.markAllSnapshotDone('1/1');
110
+ await writer.save({
111
+ sourceTable: testTable,
112
+ tag: storage.SaveOperationTag.INSERT,
113
+ after: {
114
+ id: 't1'
115
+ },
116
+ afterReplicaId: test_utils.rid('t1')
117
+ });
118
+ await writer.save({
119
+ sourceTable: testTable,
120
+ tag: storage.SaveOperationTag.INSERT,
121
+ after: {
122
+ id: 't2'
123
+ },
124
+ afterReplicaId: test_utils.rid('t2')
125
+ });
126
+ await writer.save({
127
+ sourceTable: testTable,
128
+ tag: storage.SaveOperationTag.DELETE,
129
+ before: {
130
+ id: 't1'
131
+ },
132
+ beforeReplicaId: test_utils.rid('t1')
133
+ });
134
+ await writer.save({
135
+ sourceTable: testTable,
136
+ tag: storage.SaveOperationTag.UPDATE,
137
+ after: {
138
+ id: 't2'
139
+ },
140
+ afterReplicaId: test_utils.rid('t2')
141
+ });
142
+ await writer.commit('1/1');
143
+ await writer.flush();
144
+ const checkpoint = writer.last_flushed_op;
145
+ const request = bucketRequest(syncRules, 'global[]');
146
+ const batchBefore = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
147
+ const dataBefore = batchBefore.chunkData.data;
148
+ const checksumBefore = await bucketStorage.getChecksums(checkpoint, [request]);
149
+ // op_id sequence depends on the storage implementation
150
+ expect(dataBefore).toMatchObject([
151
+ {
152
+ object_id: 't1',
153
+ op: 'PUT'
154
+ },
155
+ {
156
+ object_id: 't2',
157
+ op: 'PUT'
158
+ },
159
+ {
160
+ object_id: 't1',
161
+ op: 'REMOVE'
162
+ },
163
+ {
164
+ object_id: 't2',
165
+ op: 'PUT'
166
+ }
167
+ ]);
168
+ await bucketStorage.compact({
169
+ clearBatchLimit: 2,
170
+ moveBatchLimit: 1,
171
+ moveBatchQueryLimit: 1,
172
+ minBucketChanges: 1,
173
+ minChangeRatio: 0
174
+ });
175
+ const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint, [request]));
176
+ const dataAfter = batchAfter.chunkData.data;
177
+ bucketStorage.clearChecksumCache();
178
+ const checksumAfter = await bucketStorage.getChecksums(checkpoint, [request]);
179
+ expect(batchAfter.targetOp).toBeLessThanOrEqual(checkpoint);
180
+ expect(dataAfter).toMatchObject([
181
+ {
182
+ checksum: addChecksums(addChecksums(dataBefore[0].checksum, dataBefore[1].checksum), dataBefore[2].checksum),
183
+ op: 'CLEAR'
184
+ },
185
+ {
186
+ checksum: dataBefore[3].checksum,
187
+ object_id: 't2',
188
+ op: 'PUT'
189
+ }
190
+ ]);
191
+ expect(checksumAfter.get(request.bucket)).toEqual({
192
+ ...checksumBefore.get(request.bucket),
193
+ count: 2
194
+ });
195
+ test_utils.validateCompactedBucket(dataBefore, dataAfter);
282
196
  });
283
197
  test('compacting (3)', async () => {
284
- const env_3 = { stack: [], error: void 0, hasError: false };
285
- try {
286
- const factory = __addDisposableResource(env_3, await generateStorageFactory(), true);
287
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
198
+ await using factory = await generateStorageFactory();
199
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
288
200
  bucket_definitions:
289
201
  global:
290
202
  data: [select * from test]
291
203
  `));
292
- const bucketStorage = factory.getInstance(syncRules);
293
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
294
- await batch.save({
295
- sourceTable: TEST_TABLE,
296
- tag: storage.SaveOperationTag.INSERT,
297
- after: {
298
- id: 't1'
299
- },
300
- afterReplicaId: 't1'
301
- });
302
- await batch.save({
303
- sourceTable: TEST_TABLE,
304
- tag: storage.SaveOperationTag.INSERT,
305
- after: {
306
- id: 't2'
307
- },
308
- afterReplicaId: 't2'
309
- });
310
- await batch.save({
311
- sourceTable: TEST_TABLE,
312
- tag: storage.SaveOperationTag.DELETE,
313
- before: {
314
- id: 't1'
315
- },
316
- beforeReplicaId: 't1'
317
- });
318
- await batch.commit('1/1');
319
- });
320
- const checkpoint1 = result.flushed_op;
321
- const checksumBefore = await bucketStorage.getChecksums(checkpoint1, bucketRequests(syncRules, ['global[]']));
322
- const result2 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
323
- await batch.save({
324
- sourceTable: TEST_TABLE,
325
- tag: storage.SaveOperationTag.DELETE,
326
- before: {
327
- id: 't2'
328
- },
329
- beforeReplicaId: 't2'
330
- });
331
- await batch.commit('2/1');
332
- });
333
- const checkpoint2 = result2.flushed_op;
334
- await bucketStorage.compact({
335
- clearBatchLimit: 2,
336
- moveBatchLimit: 1,
337
- moveBatchQueryLimit: 1,
338
- minBucketChanges: 1,
339
- minChangeRatio: 0
340
- });
341
- const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint2, bucketRequestMap(syncRules, [['global[]', 0n]])));
342
- const dataAfter = batchAfter.chunkData.data;
343
- await bucketStorage.clearChecksumCache();
344
- const checksumAfter = await bucketStorage.getChecksums(checkpoint2, bucketRequests(syncRules, ['global[]']));
345
- expect(batchAfter.targetOp).toEqual(4n);
346
- expect(dataAfter).toMatchObject([
347
- {
348
- checksum: 1874612650,
349
- op: 'CLEAR',
350
- op_id: '4'
351
- }
352
- ]);
353
- expect(checksumAfter.get(bucketRequest(syncRules, 'global[]'))).toEqual({
354
- bucket: bucketRequest(syncRules, 'global[]'),
355
- count: 1,
356
- checksum: 1874612650
357
- });
358
- }
359
- catch (e_3) {
360
- env_3.error = e_3;
361
- env_3.hasError = true;
362
- }
363
- finally {
364
- const result_3 = __disposeResources(env_3);
365
- if (result_3)
366
- await result_3;
367
- }
204
+ const bucketStorage = factory.getInstance(syncRules);
205
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
206
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
207
+ await writer.markAllSnapshotDone('1/1');
208
+ await writer.save({
209
+ sourceTable: testTable,
210
+ tag: storage.SaveOperationTag.INSERT,
211
+ after: {
212
+ id: 't1'
213
+ },
214
+ afterReplicaId: 't1'
215
+ });
216
+ await writer.save({
217
+ sourceTable: testTable,
218
+ tag: storage.SaveOperationTag.INSERT,
219
+ after: {
220
+ id: 't2'
221
+ },
222
+ afterReplicaId: 't2'
223
+ });
224
+ await writer.save({
225
+ sourceTable: testTable,
226
+ tag: storage.SaveOperationTag.DELETE,
227
+ before: {
228
+ id: 't1'
229
+ },
230
+ beforeReplicaId: 't1'
231
+ });
232
+ await writer.commit('1/1');
233
+ await writer.flush();
234
+ const checkpoint1 = writer.last_flushed_op;
235
+ const request = bucketRequest(syncRules, 'global[]');
236
+ await using writer2 = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
237
+ const testTable2 = await test_utils.resolveTestTable(writer2, 'test', ['id'], config);
238
+ await writer2.save({
239
+ sourceTable: testTable2,
240
+ tag: storage.SaveOperationTag.DELETE,
241
+ before: {
242
+ id: 't2'
243
+ },
244
+ beforeReplicaId: 't2'
245
+ });
246
+ await writer2.commit('2/1');
247
+ await writer2.flush();
248
+ const checkpoint2 = writer2.last_flushed_op;
249
+ await bucketStorage.compact({
250
+ clearBatchLimit: 2,
251
+ moveBatchLimit: 1,
252
+ moveBatchQueryLimit: 1,
253
+ minBucketChanges: 1,
254
+ minChangeRatio: 0
255
+ });
256
+ const batchAfter = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint2, [request]));
257
+ const dataAfter = batchAfter.chunkData.data;
258
+ await bucketStorage.clearChecksumCache();
259
+ const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
260
+ expect(dataAfter).toMatchObject([
261
+ {
262
+ op: 'CLEAR'
263
+ }
264
+ ]);
265
+ expect(checksumAfter.get(request.bucket)).toEqual({
266
+ bucket: request.bucket,
267
+ count: 1,
268
+ checksum: dataAfter[0].checksum
269
+ });
368
270
  });
369
271
  test('compacting (4)', async () => {
370
- const env_4 = { stack: [], error: void 0, hasError: false };
371
- try {
372
- const factory = __addDisposableResource(env_4, await generateStorageFactory(), true);
373
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(` bucket_definitions:
272
+ await using factory = await generateStorageFactory();
273
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(` bucket_definitions:
374
274
  grouped:
375
275
  # The parameter query here is not important
376
276
  # We specifically don't want to create bucket_parameter records here
@@ -378,257 +278,284 @@ bucket_definitions:
378
278
  parameters: select 'b' as b
379
279
  data:
380
280
  - select * from test where b = bucket.b`));
381
- const bucketStorage = factory.getInstance(syncRules);
382
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
383
- /**
384
- * Repeatedly create operations which fall into different buckets.
385
- * The bucket operations are purposely interleaved as the op_id increases.
386
- * A large amount of operations are created here.
387
- * The configured window of compacting operations is 100. This means the initial window will
388
- * contain operations from multiple buckets.
389
- */
390
- for (let count = 0; count < 100; count++) {
391
- await batch.save({
392
- sourceTable: TEST_TABLE,
393
- tag: storage.SaveOperationTag.INSERT,
394
- after: {
395
- id: 't1',
396
- b: 'b1',
397
- value: 'start'
398
- },
399
- afterReplicaId: test_utils.rid('t1')
400
- });
401
- await batch.save({
402
- sourceTable: TEST_TABLE,
403
- tag: storage.SaveOperationTag.UPDATE,
404
- after: {
405
- id: 't1',
406
- b: 'b1',
407
- value: 'intermediate'
408
- },
409
- afterReplicaId: test_utils.rid('t1')
410
- });
411
- await batch.save({
412
- sourceTable: TEST_TABLE,
413
- tag: storage.SaveOperationTag.INSERT,
414
- after: {
415
- id: 't2',
416
- b: 'b2',
417
- value: 'start'
418
- },
419
- afterReplicaId: test_utils.rid('t2')
420
- });
421
- await batch.save({
422
- sourceTable: TEST_TABLE,
423
- tag: storage.SaveOperationTag.UPDATE,
424
- after: {
425
- id: 't1',
426
- b: 'b1',
427
- value: 'final'
428
- },
429
- afterReplicaId: test_utils.rid('t1')
430
- });
431
- await batch.save({
432
- sourceTable: TEST_TABLE,
433
- tag: storage.SaveOperationTag.UPDATE,
434
- after: {
435
- id: 't2',
436
- b: 'b2',
437
- value: 'final'
438
- },
439
- afterReplicaId: test_utils.rid('t2')
440
- });
441
- await batch.commit('1/1');
442
- }
281
+ const bucketStorage = factory.getInstance(syncRules);
282
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
283
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
284
+ await writer.markAllSnapshotDone('1/1');
285
+ /**
286
+ * Repeatedly create operations which fall into different buckets.
287
+ * The bucket operations are purposely interleaved as the op_id increases.
288
+ * A large amount of operations are created here.
289
+ * The configured window of compacting operations is 100. This means the initial window will
290
+ * contain operations from multiple buckets.
291
+ */
292
+ for (let count = 0; count < 100; count++) {
293
+ await writer.save({
294
+ sourceTable: testTable,
295
+ tag: storage.SaveOperationTag.INSERT,
296
+ after: {
297
+ id: 't1',
298
+ b: 'b1',
299
+ value: 'start'
300
+ },
301
+ afterReplicaId: test_utils.rid('t1')
443
302
  });
444
- const checkpoint = result.flushed_op;
445
- await bucketStorage.compact({
446
- clearBatchLimit: 100,
447
- moveBatchLimit: 100,
448
- moveBatchQueryLimit: 100, // Larger limit for a larger window of operations
449
- minBucketChanges: 1,
450
- minChangeRatio: 0
303
+ await writer.save({
304
+ sourceTable: testTable,
305
+ tag: storage.SaveOperationTag.UPDATE,
306
+ after: {
307
+ id: 't1',
308
+ b: 'b1',
309
+ value: 'intermediate'
310
+ },
311
+ afterReplicaId: test_utils.rid('t1')
451
312
  });
452
- const batchAfter = await test_utils.fromAsync(bucketStorage.getBucketDataBatch(checkpoint, bucketRequestMap(syncRules, [
453
- ['grouped["b1"]', 0n],
454
- ['grouped["b2"]', 0n]
455
- ])));
456
- const dataAfter = batchAfter.flatMap((b) => b.chunkData.data);
457
- // The op_ids will vary between MongoDB and Postgres storage
458
- expect(dataAfter).toMatchObject(expect.arrayContaining([
459
- { op_id: '497', op: 'CLEAR', checksum: -937074151 },
460
- {
461
- op_id: '499',
462
- op: 'PUT',
463
- object_type: 'test',
464
- object_id: 't1',
465
- checksum: 52221819,
466
- subkey: '6544e3899293153fa7b38331/117ab485-4b42-58a2-ab32-0053a22c3423',
467
- data: '{"id":"t1","b":"b1","value":"final"}'
313
+ await writer.save({
314
+ sourceTable: testTable,
315
+ tag: storage.SaveOperationTag.INSERT,
316
+ after: {
317
+ id: 't2',
318
+ b: 'b2',
319
+ value: 'start'
468
320
  },
469
- { op_id: '498', op: 'CLEAR', checksum: -234380197 },
470
- {
471
- op_id: '500',
472
- op: 'PUT',
473
- object_type: 'test',
474
- object_id: 't2',
475
- checksum: 2126669493,
476
- subkey: '6544e3899293153fa7b38331/ec27c691-b47a-5d92-927a-9944feb89eee',
477
- data: '{"id":"t2","b":"b2","value":"final"}'
478
- }
479
- ]));
480
- }
481
- catch (e_4) {
482
- env_4.error = e_4;
483
- env_4.hasError = true;
484
- }
485
- finally {
486
- const result_4 = __disposeResources(env_4);
487
- if (result_4)
488
- await result_4;
321
+ afterReplicaId: test_utils.rid('t2')
322
+ });
323
+ await writer.save({
324
+ sourceTable: testTable,
325
+ tag: storage.SaveOperationTag.UPDATE,
326
+ after: {
327
+ id: 't1',
328
+ b: 'b1',
329
+ value: 'final'
330
+ },
331
+ afterReplicaId: test_utils.rid('t1')
332
+ });
333
+ await writer.save({
334
+ sourceTable: testTable,
335
+ tag: storage.SaveOperationTag.UPDATE,
336
+ after: {
337
+ id: 't2',
338
+ b: 'b2',
339
+ value: 'final'
340
+ },
341
+ afterReplicaId: test_utils.rid('t2')
342
+ });
343
+ await writer.commit('1/1');
489
344
  }
345
+ await writer.flush();
346
+ const checkpoint = writer.last_flushed_op;
347
+ await bucketStorage.compact({
348
+ clearBatchLimit: 100,
349
+ moveBatchLimit: 100,
350
+ moveBatchQueryLimit: 100, // Larger limit for a larger window of operations
351
+ minBucketChanges: 1,
352
+ minChangeRatio: 0
353
+ });
354
+ const batchAfter = await test_utils.fromAsync(bucketStorage.getBucketDataBatch(checkpoint, bucketRequestMap(syncRules, [
355
+ ['grouped["b1"]', 0n],
356
+ ['grouped["b2"]', 0n]
357
+ ])));
358
+ const dataAfter = batchAfter.flatMap((b) => b.chunkData.data);
359
+ // The op_ids will vary between MongoDB and Postgres storage
360
+ expect(dataAfter).toMatchObject(expect.arrayContaining([
361
+ { op_id: '497', op: 'CLEAR', checksum: -937074151 },
362
+ {
363
+ op_id: '499',
364
+ op: 'PUT',
365
+ object_type: 'test',
366
+ object_id: 't1',
367
+ checksum: 52221819,
368
+ subkey: '6544e3899293153fa7b38331/117ab485-4b42-58a2-ab32-0053a22c3423',
369
+ data: '{"id":"t1","b":"b1","value":"final"}'
370
+ },
371
+ { op_id: '498', op: 'CLEAR', checksum: -234380197 },
372
+ {
373
+ op_id: '500',
374
+ op: 'PUT',
375
+ object_type: 'test',
376
+ object_id: 't2',
377
+ checksum: 2126669493,
378
+ subkey: '6544e3899293153fa7b38331/ec27c691-b47a-5d92-927a-9944feb89eee',
379
+ data: '{"id":"t2","b":"b2","value":"final"}'
380
+ }
381
+ ]));
490
382
  });
491
383
  test('partial checksums after compacting', async () => {
492
- const env_5 = { stack: [], error: void 0, hasError: false };
493
- try {
494
- const factory = __addDisposableResource(env_5, await generateStorageFactory(), true);
495
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
384
+ await using factory = await generateStorageFactory();
385
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
496
386
  bucket_definitions:
497
387
  global:
498
388
  data: [select * from test]
499
389
  `));
500
- const bucketStorage = factory.getInstance(syncRules);
501
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
502
- await batch.save({
503
- sourceTable: TEST_TABLE,
504
- tag: storage.SaveOperationTag.INSERT,
505
- after: {
506
- id: 't1'
507
- },
508
- afterReplicaId: 't1'
509
- });
510
- await batch.save({
511
- sourceTable: TEST_TABLE,
512
- tag: storage.SaveOperationTag.INSERT,
513
- after: {
514
- id: 't2'
515
- },
516
- afterReplicaId: 't2'
517
- });
518
- await batch.save({
519
- sourceTable: TEST_TABLE,
520
- tag: storage.SaveOperationTag.DELETE,
521
- before: {
522
- id: 't1'
523
- },
524
- beforeReplicaId: 't1'
525
- });
526
- await batch.commit('1/1');
527
- });
528
- await bucketStorage.compact({
529
- clearBatchLimit: 2,
530
- moveBatchLimit: 1,
531
- moveBatchQueryLimit: 1,
532
- minBucketChanges: 1,
533
- minChangeRatio: 0
534
- });
535
- const result2 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
536
- await batch.save({
537
- sourceTable: TEST_TABLE,
538
- tag: storage.SaveOperationTag.DELETE,
539
- before: {
540
- id: 't2'
541
- },
542
- beforeReplicaId: 't2'
543
- });
544
- await batch.commit('2/1');
545
- });
546
- const checkpoint2 = result2.flushed_op;
547
- await bucketStorage.clearChecksumCache();
548
- const checksumAfter = await bucketStorage.getChecksums(checkpoint2, bucketRequests(syncRules, ['global[]']));
549
- expect(checksumAfter.get(bucketRequest(syncRules, 'global[]'))).toEqual({
550
- bucket: bucketRequest(syncRules, 'global[]'),
551
- count: 4,
552
- checksum: 1874612650
553
- });
554
- }
555
- catch (e_5) {
556
- env_5.error = e_5;
557
- env_5.hasError = true;
558
- }
559
- finally {
560
- const result_5 = __disposeResources(env_5);
561
- if (result_5)
562
- await result_5;
563
- }
390
+ const bucketStorage = factory.getInstance(syncRules);
391
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
392
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
393
+ await writer.markAllSnapshotDone('1/1');
394
+ await writer.save({
395
+ sourceTable: testTable,
396
+ tag: storage.SaveOperationTag.INSERT,
397
+ after: {
398
+ id: 't1'
399
+ },
400
+ afterReplicaId: 't1'
401
+ });
402
+ await writer.save({
403
+ sourceTable: testTable,
404
+ tag: storage.SaveOperationTag.INSERT,
405
+ after: {
406
+ id: 't2'
407
+ },
408
+ afterReplicaId: 't2'
409
+ });
410
+ await writer.save({
411
+ sourceTable: testTable,
412
+ tag: storage.SaveOperationTag.DELETE,
413
+ before: {
414
+ id: 't1'
415
+ },
416
+ beforeReplicaId: 't1'
417
+ });
418
+ await writer.commit('1/1');
419
+ await writer.flush();
420
+ await bucketStorage.compact({
421
+ clearBatchLimit: 2,
422
+ moveBatchLimit: 1,
423
+ moveBatchQueryLimit: 1,
424
+ minBucketChanges: 1,
425
+ minChangeRatio: 0
426
+ });
427
+ await using writer2 = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
428
+ const testTable2 = await test_utils.resolveTestTable(writer2, 'test', ['id'], config);
429
+ await writer2.save({
430
+ sourceTable: testTable2,
431
+ tag: storage.SaveOperationTag.DELETE,
432
+ before: {
433
+ id: 't2'
434
+ },
435
+ beforeReplicaId: 't2'
436
+ });
437
+ await writer2.commit('2/1');
438
+ await writer2.flush();
439
+ const checkpoint2 = writer2.last_flushed_op;
440
+ const request = bucketRequest(syncRules, 'global[]');
441
+ await bucketStorage.clearChecksumCache();
442
+ const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
443
+ const globalChecksum = checksumAfter.get(request.bucket);
444
+ expect(globalChecksum).toMatchObject({
445
+ bucket: request.bucket,
446
+ count: 4
447
+ });
448
+ // storage-specific checksum - just check that it does not change
449
+ expect(globalChecksum).toMatchSnapshot();
564
450
  });
565
451
  test('partial checksums after compacting (2)', async () => {
566
- const env_6 = { stack: [], error: void 0, hasError: false };
567
- try {
568
- const factory = __addDisposableResource(env_6, await generateStorageFactory(), true);
569
- const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
452
+ await using factory = await generateStorageFactory();
453
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
570
454
  bucket_definitions:
571
455
  global:
572
456
  data: [select * from test]
573
457
  `));
574
- const bucketStorage = factory.getInstance(syncRules);
575
- const result = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
576
- await batch.save({
577
- sourceTable: TEST_TABLE,
578
- tag: storage.SaveOperationTag.INSERT,
579
- after: {
580
- id: 't1'
581
- },
582
- afterReplicaId: 't1'
583
- });
584
- await batch.save({
585
- sourceTable: TEST_TABLE,
586
- tag: storage.SaveOperationTag.UPDATE,
587
- after: {
588
- id: 't1'
589
- },
590
- afterReplicaId: 't1'
591
- });
592
- await batch.commit('1/1');
593
- });
594
- // Get checksums here just to populate the cache
595
- await bucketStorage.getChecksums(result.flushed_op, bucketRequests(syncRules, ['global[]']));
596
- const result2 = await bucketStorage.startBatch(test_utils.BATCH_OPTIONS, async (batch) => {
597
- await batch.save({
598
- sourceTable: TEST_TABLE,
599
- tag: storage.SaveOperationTag.DELETE,
600
- before: {
601
- id: 't1'
602
- },
603
- beforeReplicaId: 't1'
604
- });
605
- await batch.commit('2/1');
606
- });
607
- await bucketStorage.compact({
608
- clearBatchLimit: 20,
609
- moveBatchLimit: 10,
610
- moveBatchQueryLimit: 10,
611
- minBucketChanges: 1,
612
- minChangeRatio: 0
613
- });
614
- const checkpoint2 = result2.flushed_op;
615
- // Check that the checksum was correctly updated with the clear operation after having a cached checksum
616
- const checksumAfter = await bucketStorage.getChecksums(checkpoint2, bucketRequests(syncRules, ['global[]']));
617
- expect(checksumAfter.get(bucketRequest(syncRules, 'global[]'))).toMatchObject({
618
- bucket: bucketRequest(syncRules, 'global[]'),
619
- count: 1,
620
- checksum: -1481659821
621
- });
622
- }
623
- catch (e_6) {
624
- env_6.error = e_6;
625
- env_6.hasError = true;
626
- }
627
- finally {
628
- const result_6 = __disposeResources(env_6);
629
- if (result_6)
630
- await result_6;
631
- }
458
+ const bucketStorage = factory.getInstance(syncRules);
459
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
460
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
461
+ await writer.markAllSnapshotDone('1/1');
462
+ await writer.save({
463
+ sourceTable: testTable,
464
+ tag: storage.SaveOperationTag.INSERT,
465
+ after: {
466
+ id: 't1'
467
+ },
468
+ afterReplicaId: 't1'
469
+ });
470
+ await writer.save({
471
+ sourceTable: testTable,
472
+ tag: storage.SaveOperationTag.UPDATE,
473
+ after: {
474
+ id: 't1'
475
+ },
476
+ afterReplicaId: 't1'
477
+ });
478
+ await writer.commit('1/1');
479
+ await writer.flush();
480
+ // Get checksums here just to populate the cache
481
+ await bucketStorage.getChecksums(writer.last_flushed_op, bucketRequests(syncRules, ['global[]']));
482
+ await using writer2 = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
483
+ const testTable2 = await test_utils.resolveTestTable(writer2, 'test', ['id'], config);
484
+ await writer2.save({
485
+ sourceTable: testTable2,
486
+ tag: storage.SaveOperationTag.DELETE,
487
+ before: {
488
+ id: 't1'
489
+ },
490
+ beforeReplicaId: 't1'
491
+ });
492
+ await writer2.commit('2/1');
493
+ await writer2.flush();
494
+ await bucketStorage.compact({
495
+ clearBatchLimit: 20,
496
+ moveBatchLimit: 10,
497
+ moveBatchQueryLimit: 10,
498
+ minBucketChanges: 1,
499
+ minChangeRatio: 0
500
+ });
501
+ const checkpoint2 = writer2.last_flushed_op;
502
+ const request = bucketRequest(syncRules, 'global[]');
503
+ // Check that the checksum was correctly updated with the clear operation after having a cached checksum
504
+ const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
505
+ const globalChecksum = checksumAfter.get(request.bucket);
506
+ expect(globalChecksum).toMatchObject({
507
+ bucket: request.bucket,
508
+ count: 1
509
+ });
510
+ // storage-specific checksum - just check that it does not change
511
+ expect(globalChecksum).toMatchSnapshot();
512
+ });
513
+ test('defaults maxOpId to current checkpoint', async () => {
514
+ await using factory = await generateStorageFactory();
515
+ const syncRules = await factory.updateSyncRules(updateSyncRulesFromYaml(`
516
+ bucket_definitions:
517
+ global:
518
+ data: [select * from test]
519
+ `));
520
+ const bucketStorage = factory.getInstance(syncRules);
521
+ await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
522
+ const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
523
+ await writer.markAllSnapshotDone('1/1');
524
+ await writer.save({
525
+ sourceTable: testTable,
526
+ tag: storage.SaveOperationTag.INSERT,
527
+ after: { id: 't1' },
528
+ afterReplicaId: test_utils.rid('t1')
529
+ });
530
+ await writer.commit('1/1');
531
+ await writer.flush();
532
+ const checkpoint1 = writer.last_flushed_op;
533
+ await using writer2 = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
534
+ const testTable2 = await test_utils.resolveTestTable(writer2, 'test', ['id'], config);
535
+ // This is flushed but not committed (does not advance the checkpoint)
536
+ await writer2.save({
537
+ sourceTable: testTable2,
538
+ tag: storage.SaveOperationTag.UPDATE,
539
+ after: { id: 't1' },
540
+ afterReplicaId: test_utils.rid('t1')
541
+ });
542
+ await writer2.flush();
543
+ const checkpoint2 = writer2.last_flushed_op;
544
+ const checkpointBeforeCompact = await bucketStorage.getCheckpoint();
545
+ expect(checkpointBeforeCompact.checkpoint).toEqual(checkpoint1);
546
+ // With default options, Postgres compaction should use the active checkpoint.
547
+ await bucketStorage.compact({
548
+ moveBatchLimit: 1,
549
+ moveBatchQueryLimit: 1,
550
+ minBucketChanges: 1,
551
+ minChangeRatio: 0
552
+ });
553
+ const batchAfterDefaultCompact = await test_utils.oneFromAsync(bucketStorage.getBucketDataBatch(checkpoint2, bucketRequestMap(syncRules, [['global[]', 0n]])));
554
+ // Operation 1 should remain a PUT because op_id=2 is above the default maxOpId checkpoint.
555
+ expect(batchAfterDefaultCompact.chunkData.data).toMatchObject([
556
+ { op_id: '1', op: 'PUT', object_id: 't1' },
557
+ { op_id: '2', op: 'PUT', object_id: 't1' }
558
+ ]);
632
559
  });
633
560
  }
634
561
  //# sourceMappingURL=register-compacting-tests.js.map