@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.
- package/CHANGELOG.md +95 -3
- package/dist/test-utils/MetricsHelper.js +1 -1
- package/dist/test-utils/MetricsHelper.js.map +1 -1
- package/dist/test-utils/StorageDataHelpers.d.ts +8 -0
- package/dist/test-utils/StorageDataHelpers.js +33 -0
- package/dist/test-utils/StorageDataHelpers.js.map +1 -0
- package/dist/test-utils/general-utils.d.ts +22 -3
- package/dist/test-utils/general-utils.js +56 -3
- package/dist/test-utils/general-utils.js.map +1 -1
- package/dist/test-utils/stream_utils.js +2 -2
- package/dist/test-utils/stream_utils.js.map +1 -1
- package/dist/test-utils/test-utils-index.d.ts +1 -0
- package/dist/test-utils/test-utils-index.js +1 -0
- package/dist/test-utils/test-utils-index.js.map +1 -1
- package/dist/tests/register-compacting-tests.d.ts +1 -1
- package/dist/tests/register-compacting-tests.js +514 -587
- package/dist/tests/register-compacting-tests.js.map +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.d.ts +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.js +200 -301
- package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
- package/dist/tests/register-data-storage-data-tests.d.ts +2 -2
- package/dist/tests/register-data-storage-data-tests.js +1220 -1041
- package/dist/tests/register-data-storage-data-tests.js.map +1 -1
- package/dist/tests/register-data-storage-parameter-tests.d.ts +1 -1
- package/dist/tests/register-data-storage-parameter-tests.js +681 -632
- package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
- package/dist/tests/register-migration-tests.js +63 -139
- package/dist/tests/register-migration-tests.js.map +1 -1
- package/dist/tests/register-parameter-compacting-tests.d.ts +1 -1
- package/dist/tests/register-parameter-compacting-tests.js +121 -201
- package/dist/tests/register-parameter-compacting-tests.js.map +1 -1
- package/dist/tests/register-sync-tests.d.ts +2 -1
- package/dist/tests/register-sync-tests.js +974 -1136
- package/dist/tests/register-sync-tests.js.map +1 -1
- package/dist/tests/tests-index.d.ts +4 -4
- package/dist/tests/tests-index.js +4 -4
- package/dist/tests/tests-index.js.map +1 -1
- package/dist/tests/util.d.ts +5 -4
- package/dist/tests/util.js +27 -12
- package/dist/tests/util.js.map +1 -1
- package/package.json +6 -6
- package/src/test-utils/MetricsHelper.ts +1 -1
- package/src/test-utils/StorageDataHelpers.ts +44 -0
- package/src/test-utils/general-utils.ts +81 -4
- package/src/test-utils/stream_utils.ts +2 -2
- package/src/test-utils/test-utils-index.ts +1 -0
- package/src/tests/register-compacting-tests.ts +376 -322
- package/src/tests/register-data-storage-checkpoint-tests.ts +85 -53
- package/src/tests/register-data-storage-data-tests.ts +1076 -563
- package/src/tests/register-data-storage-parameter-tests.ts +631 -327
- package/src/tests/register-parameter-compacting-tests.ts +92 -96
- package/src/tests/register-sync-tests.ts +463 -379
- package/src/tests/tests-index.ts +4 -4
- package/src/tests/util.ts +46 -17
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,376 +1,276 @@
|
|
|
1
|
-
|
|
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
|
|
57
|
-
|
|
58
|
-
export function registerCompactTests(
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
169
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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
|
-
|
|
285
|
-
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
371
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
493
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
|
|
567
|
-
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
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
|