@powersync/service-core-tests 0.16.0 → 0.17.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 +34 -0
- package/dist/test-utils/AbstractStreamTestContext.d.ts +6 -5
- package/dist/test-utils/AbstractStreamTestContext.js +26 -22
- package/dist/test-utils/AbstractStreamTestContext.js.map +1 -1
- package/dist/test-utils/StorageDataHelpers.d.ts +5 -5
- package/dist/test-utils/StorageDataHelpers.js.map +1 -1
- package/dist/test-utils/bucket-validation.d.ts +3 -3
- package/dist/test-utils/bucket-validation.js +3 -3
- package/dist/test-utils/general-utils.d.ts +13 -2
- package/dist/test-utils/general-utils.js +24 -3
- package/dist/test-utils/general-utils.js.map +1 -1
- package/dist/tests/register-compacting-tests.js +28 -21
- package/dist/tests/register-compacting-tests.js.map +1 -1
- package/dist/tests/register-data-storage-checkpoint-tests.js +8 -2
- package/dist/tests/register-data-storage-checkpoint-tests.js.map +1 -1
- package/dist/tests/register-data-storage-data-tests.js +148 -101
- package/dist/tests/register-data-storage-data-tests.js.map +1 -1
- package/dist/tests/register-data-storage-parameter-tests.js +44 -42
- package/dist/tests/register-data-storage-parameter-tests.js.map +1 -1
- package/dist/tests/register-sync-tests.js +141 -8
- package/dist/tests/register-sync-tests.js.map +1 -1
- package/dist/tests/util.d.ts +2 -2
- package/dist/tests/util.js.map +1 -1
- package/package.json +6 -7
- package/src/test-utils/AbstractStreamTestContext.ts +29 -24
- package/src/test-utils/StorageDataHelpers.ts +7 -6
- package/src/test-utils/bucket-validation.ts +3 -3
- package/src/test-utils/general-utils.ts +35 -7
- package/src/tests/register-compacting-tests.ts +45 -22
- package/src/tests/register-data-storage-checkpoint-tests.ts +12 -2
- package/src/tests/register-data-storage-data-tests.ts +210 -107
- package/src/tests/register-data-storage-parameter-tests.ts +58 -42
- package/src/tests/register-sync-tests.ts +159 -8
- package/src/tests/util.ts +2 -2
- package/tsconfig.json +1 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,6 +2,7 @@ import { ReplicationAbortedError } from '@powersync/lib-services-framework';
|
|
|
2
2
|
import {
|
|
3
3
|
BucketStorageFactory,
|
|
4
4
|
InternalOpId,
|
|
5
|
+
ReplicationCheckpoint,
|
|
5
6
|
settledPromise,
|
|
6
7
|
storage,
|
|
7
8
|
SyncRulesBucketStorage,
|
|
@@ -14,7 +15,8 @@ import { fromAsync } from './stream_utils.js';
|
|
|
14
15
|
|
|
15
16
|
export abstract class AbstractStreamTestContext implements AsyncDisposable {
|
|
16
17
|
protected abortController = new AbortController();
|
|
17
|
-
protected syncRulesContent?: storage.
|
|
18
|
+
protected syncRulesContent?: storage.PersistedSyncConfigContent;
|
|
19
|
+
protected replicationStream?: storage.PersistedReplicationStream;
|
|
18
20
|
public storage?: SyncRulesBucketStorage;
|
|
19
21
|
protected settledReplicationPromise?: Promise<PromiseSettledResult<void>>;
|
|
20
22
|
|
|
@@ -44,39 +46,42 @@ export abstract class AbstractStreamTestContext implements AsyncDisposable {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
async updateSyncRules(content: string) {
|
|
47
|
-
const
|
|
49
|
+
const stream = await this.factory.updateSyncRules(
|
|
48
50
|
updateSyncRulesFromYaml(content, { validate: true, storageVersion: this.storageVersion })
|
|
49
51
|
);
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
52
|
+
this.replicationStream = stream;
|
|
53
|
+
this.syncRulesContent = stream.syncConfigContent[0];
|
|
54
|
+
this.storage = this.factory.getInstance(stream);
|
|
52
55
|
return this.storage!;
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
async loadNextSyncRules() {
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
throw new Error(`Next sync
|
|
59
|
+
const syncConfig = await this.factory.getDeployingSyncConfig();
|
|
60
|
+
if (syncConfig == null) {
|
|
61
|
+
throw new Error(`Next sync config not available`);
|
|
59
62
|
}
|
|
60
63
|
|
|
61
|
-
this.syncRulesContent =
|
|
62
|
-
this.
|
|
64
|
+
this.syncRulesContent = syncConfig.content;
|
|
65
|
+
this.replicationStream = syncConfig.replicationStream;
|
|
66
|
+
this.storage = syncConfig.storage;
|
|
63
67
|
return this.storage!;
|
|
64
68
|
}
|
|
65
69
|
|
|
66
70
|
async loadActiveSyncRules() {
|
|
67
|
-
const
|
|
68
|
-
if (
|
|
69
|
-
throw new Error(`Active sync
|
|
71
|
+
const syncConfig = await this.factory.getActiveSyncConfig();
|
|
72
|
+
if (syncConfig == null) {
|
|
73
|
+
throw new Error(`Active sync config not available`);
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
this.syncRulesContent =
|
|
73
|
-
this.
|
|
76
|
+
this.syncRulesContent = syncConfig.content;
|
|
77
|
+
this.replicationStream = syncConfig.replicationStream;
|
|
78
|
+
this.storage = syncConfig.storage;
|
|
74
79
|
return this.storage!;
|
|
75
80
|
}
|
|
76
81
|
|
|
77
|
-
private
|
|
82
|
+
private getSyncConfigContent(): storage.PersistedSyncConfigContent {
|
|
78
83
|
if (this.syncRulesContent == null) {
|
|
79
|
-
throw new Error('Sync
|
|
84
|
+
throw new Error('Sync config not configured - call updateSyncRules() first');
|
|
80
85
|
}
|
|
81
86
|
return this.syncRulesContent;
|
|
82
87
|
}
|
|
@@ -112,7 +117,7 @@ export abstract class AbstractStreamTestContext implements AsyncDisposable {
|
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
|
|
115
|
-
abstract getClientCheckpoint(options?: { timeout?: number }): Promise<
|
|
120
|
+
abstract getClientCheckpoint(options?: { timeout?: number }): Promise<ReplicationCheckpoint>;
|
|
116
121
|
|
|
117
122
|
async getCheckpoint(options?: { timeout?: number }) {
|
|
118
123
|
let checkpoint = await Promise.race([
|
|
@@ -128,7 +133,7 @@ export abstract class AbstractStreamTestContext implements AsyncDisposable {
|
|
|
128
133
|
}
|
|
129
134
|
|
|
130
135
|
async getBucketsDataBatch(buckets: Record<string, InternalOpId>, options?: { timeout?: number }) {
|
|
131
|
-
const helpers = new StorageDataHelpers(this.storage!, this.
|
|
136
|
+
const helpers = new StorageDataHelpers(this.storage!, this.getSyncConfigContent());
|
|
132
137
|
const checkpoint = await this.getCheckpoint(options);
|
|
133
138
|
return helpers.getBucketsDataBatch(buckets, checkpoint);
|
|
134
139
|
}
|
|
@@ -137,15 +142,15 @@ export abstract class AbstractStreamTestContext implements AsyncDisposable {
|
|
|
137
142
|
* This waits for a client checkpoint.
|
|
138
143
|
*/
|
|
139
144
|
async getBucketData(bucket: string, start?: InternalOpId | string | undefined, options?: { timeout?: number }) {
|
|
140
|
-
const helpers = new StorageDataHelpers(this.storage!, this.
|
|
145
|
+
const helpers = new StorageDataHelpers(this.storage!, this.getSyncConfigContent());
|
|
141
146
|
const checkpoint = await this.getCheckpoint(options);
|
|
142
147
|
return helpers.getBucketData(bucket, checkpoint, start);
|
|
143
148
|
}
|
|
144
149
|
|
|
145
150
|
async getChecksums(buckets: string[], options?: { timeout?: number }) {
|
|
146
151
|
const checkpoint = await this.getCheckpoint(options);
|
|
147
|
-
const
|
|
148
|
-
const versionedBuckets = buckets.map((bucket) => bucketRequest(
|
|
152
|
+
const syncConfigContent = this.getSyncConfigContent();
|
|
153
|
+
const versionedBuckets = buckets.map((bucket) => bucketRequest(syncConfigContent, bucket, 0n));
|
|
149
154
|
const checksums = await this.storage!.getChecksums(checkpoint, versionedBuckets);
|
|
150
155
|
|
|
151
156
|
const unversioned = new Map();
|
|
@@ -169,9 +174,9 @@ export abstract class AbstractStreamTestContext implements AsyncDisposable {
|
|
|
169
174
|
if (typeof start == 'string') {
|
|
170
175
|
start = BigInt(start);
|
|
171
176
|
}
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
const map = [bucketRequest(
|
|
177
|
+
const syncConfigContent = this.getSyncConfigContent();
|
|
178
|
+
const checkpoint = await this.storage!.getCheckpoint();
|
|
179
|
+
const map = [bucketRequest(syncConfigContent, bucket, start)];
|
|
175
180
|
const batch = this.storage!.getBucketDataBatch(checkpoint, map);
|
|
176
181
|
const batches = await fromAsync(batch);
|
|
177
182
|
return batches[0]?.chunkData.data ?? [];
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InternalOpId,
|
|
3
3
|
OplogEntry,
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
ParsedSyncConfigSet,
|
|
5
|
+
PersistedSyncConfigContent,
|
|
6
|
+
ReplicationCheckpoint,
|
|
6
7
|
SyncRulesBucketStorage
|
|
7
8
|
} from '@powersync/service-core';
|
|
8
9
|
import { bucketRequest } from './general-utils.js';
|
|
@@ -10,14 +11,14 @@ import { fromAsync } from './stream_utils.js';
|
|
|
10
11
|
|
|
11
12
|
export class StorageDataHelpers {
|
|
12
13
|
storage: SyncRulesBucketStorage;
|
|
13
|
-
syncRules:
|
|
14
|
+
syncRules: PersistedSyncConfigContent | ParsedSyncConfigSet;
|
|
14
15
|
|
|
15
|
-
constructor(storage: SyncRulesBucketStorage, syncRules:
|
|
16
|
+
constructor(storage: SyncRulesBucketStorage, syncRules: PersistedSyncConfigContent | ParsedSyncConfigSet) {
|
|
16
17
|
this.storage = storage;
|
|
17
18
|
this.syncRules = syncRules;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
async getBucketData(bucket: string, checkpoint:
|
|
21
|
+
async getBucketData(bucket: string, checkpoint: ReplicationCheckpoint, start?: InternalOpId | string | undefined) {
|
|
21
22
|
start ??= 0n;
|
|
22
23
|
if (typeof start == 'string') {
|
|
23
24
|
start = BigInt(start);
|
|
@@ -37,7 +38,7 @@ export class StorageDataHelpers {
|
|
|
37
38
|
return data;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
async getBucketsDataBatch(buckets: Record<string, InternalOpId>, checkpoint:
|
|
41
|
+
async getBucketsDataBatch(buckets: Record<string, InternalOpId>, checkpoint: ReplicationCheckpoint) {
|
|
41
42
|
const map = Object.entries(buckets).map(([bucket, start]) => bucketRequest(this.syncRules, bucket, start));
|
|
42
43
|
return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map));
|
|
43
44
|
}
|
|
@@ -9,7 +9,7 @@ import { expect } from 'vitest';
|
|
|
9
9
|
* All other operations are replaced with a single CLEAR operation,
|
|
10
10
|
* summing their checksums, and using a 0 as an op_id.
|
|
11
11
|
*
|
|
12
|
-
* This is the function $r(B)$, as described in /docs/bucket-properties.md.
|
|
12
|
+
* This is the function $r(B)$, as described in /docs/storage/bucket-properties.md.
|
|
13
13
|
*/
|
|
14
14
|
export function reduceBucket(operations: utils.OplogEntry[]) {
|
|
15
15
|
let rowState = new Map<string, utils.OplogEntry>();
|
|
@@ -63,7 +63,7 @@ import { reduceBucket } from '@/util/utils.js';
|
|
|
63
63
|
import { expect } from 'vitest';
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Validate this property, as described in /docs/bucket-properties.md:
|
|
66
|
+
* Validate this property, as described in /docs/storage/bucket-properties.md:
|
|
67
67
|
*
|
|
68
68
|
* $r(B_{[..id_n]}) = r(r(B_{[..id_i]}) \cup B_{[id_{i+1}..id_n]}) \;\forall\; i \in [1..n]$
|
|
69
69
|
*
|
|
@@ -87,7 +87,7 @@ export function validateBucket(bucket: utils.OplogEntry[]) {
|
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* Validate these properties for a bucket $B$ and its compacted version $B'$,:
|
|
90
|
-
* as described in /docs/bucket-properties.md:
|
|
90
|
+
* as described in /docs/storage/bucket-properties.md:
|
|
91
91
|
*
|
|
92
92
|
* 1. $r(B) = r(B')$
|
|
93
93
|
* 2. $r(B_{[..c]}) = r(r(B_{[..c_i]}) \cup B'_{[c_i+1..c]}) \;\forall\; c_i \in B$
|
|
@@ -4,7 +4,7 @@ import * as bson from 'bson';
|
|
|
4
4
|
|
|
5
5
|
export const ZERO_LSN = '0/0';
|
|
6
6
|
|
|
7
|
-
export const PARSE_OPTIONS: storage.
|
|
7
|
+
export const PARSE_OPTIONS: storage.ParseSyncConfigOptions = {
|
|
8
8
|
defaultSchema: 'public'
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -14,6 +14,31 @@ export const BATCH_OPTIONS: storage.CreateWriterOptions = {
|
|
|
14
14
|
storeCurrentData: true
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
export function testCheckpoint(checkpoint: InternalOpId, lsn: string | null = null): storage.ReplicationCheckpoint {
|
|
18
|
+
return {
|
|
19
|
+
checkpoint,
|
|
20
|
+
lsn,
|
|
21
|
+
async getParameterSets() {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Deploy a sync config and return both the replication stream (for {@link storage.BucketStorageFactory.getInstance})
|
|
29
|
+
* and the deployed config content (for parsing / bucket requests).
|
|
30
|
+
*
|
|
31
|
+
* Replication streams and sync config content are separate concerns since one stream can hold multiple configs.
|
|
32
|
+
*/
|
|
33
|
+
export async function deploySyncRules(
|
|
34
|
+
factory: storage.BucketStorageFactory,
|
|
35
|
+
options: storage.UpdateSyncRulesOptions
|
|
36
|
+
): Promise<{ stream: storage.PersistedReplicationStream; content: storage.PersistedSyncConfigContent }> {
|
|
37
|
+
const stream = await factory.updateSyncRules(options);
|
|
38
|
+
const content = stream.syncConfigContent[0];
|
|
39
|
+
return { stream, content };
|
|
40
|
+
}
|
|
41
|
+
|
|
17
42
|
/**
|
|
18
43
|
* With newer storage versions, we need actual test tables, resolved via the writer.
|
|
19
44
|
*/
|
|
@@ -78,9 +103,9 @@ export function getBatchData(
|
|
|
78
103
|
}
|
|
79
104
|
|
|
80
105
|
function isParsedSyncRules(
|
|
81
|
-
syncRules: storage.
|
|
82
|
-
): syncRules is storage.
|
|
83
|
-
return (syncRules as storage.
|
|
106
|
+
syncRules: storage.PersistedSyncConfigContent | storage.ParsedSyncConfigSet
|
|
107
|
+
): syncRules is storage.ParsedSyncConfigSet {
|
|
108
|
+
return (syncRules as storage.ParsedSyncConfigSet).syncConfigs !== undefined;
|
|
84
109
|
}
|
|
85
110
|
|
|
86
111
|
/**
|
|
@@ -88,7 +113,7 @@ function isParsedSyncRules(
|
|
|
88
113
|
* This converts a bucket name like "global[]" into the actual bucket name, for use in tests.
|
|
89
114
|
*/
|
|
90
115
|
export function bucketRequest(
|
|
91
|
-
syncRules: storage.
|
|
116
|
+
syncRules: storage.PersistedSyncConfigContent | storage.ParsedSyncConfigSet,
|
|
92
117
|
bucket: string,
|
|
93
118
|
start?: InternalOpId | string | number
|
|
94
119
|
): BucketDataRequest {
|
|
@@ -97,10 +122,13 @@ export function bucketRequest(
|
|
|
97
122
|
const parameterStart = bucket.indexOf('[');
|
|
98
123
|
const definitionName = bucket.substring(0, parameterStart);
|
|
99
124
|
const parameters = bucket.substring(parameterStart);
|
|
100
|
-
const
|
|
125
|
+
const availableSources = parsed.syncConfigs.flatMap((config) => config.config.bucketDataSources);
|
|
126
|
+
const source = availableSources.find((b) => b.uniqueName === definitionName);
|
|
101
127
|
|
|
102
128
|
if (source == null) {
|
|
103
|
-
throw new Error(
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Failed to find global bucket ${bucket}. Available: ${availableSources.map((s) => s.uniqueName).join(',')}`
|
|
131
|
+
);
|
|
104
132
|
}
|
|
105
133
|
const bucketName = hydrationState.getBucketSourceScope(source).bucketPrefix + parameters;
|
|
106
134
|
return {
|
|
@@ -17,6 +17,7 @@ bucket_definitions:
|
|
|
17
17
|
`)
|
|
18
18
|
);
|
|
19
19
|
const bucketStorage = factory.getInstance(syncRules);
|
|
20
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
20
21
|
|
|
21
22
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
22
23
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -53,11 +54,13 @@ bucket_definitions:
|
|
|
53
54
|
|
|
54
55
|
const checkpoint = writer.last_flushed_op!;
|
|
55
56
|
|
|
56
|
-
const request = bucketRequest(
|
|
57
|
+
const request = bucketRequest(syncRulesContent, 'global[]');
|
|
57
58
|
|
|
58
|
-
const batchBefore = await test_utils.oneFromAsync(
|
|
59
|
+
const batchBefore = await test_utils.oneFromAsync(
|
|
60
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
61
|
+
);
|
|
59
62
|
const dataBefore = batchBefore.chunkData.data;
|
|
60
|
-
const checksumBefore = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
63
|
+
const checksumBefore = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request]);
|
|
61
64
|
|
|
62
65
|
expect(dataBefore).toMatchObject([
|
|
63
66
|
{
|
|
@@ -86,11 +89,13 @@ bucket_definitions:
|
|
|
86
89
|
minChangeRatio: 0
|
|
87
90
|
});
|
|
88
91
|
|
|
89
|
-
const batchAfter = await test_utils.oneFromAsync(
|
|
92
|
+
const batchAfter = await test_utils.oneFromAsync(
|
|
93
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
94
|
+
);
|
|
90
95
|
const dataAfter = batchAfter.chunkData.data;
|
|
91
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
96
|
+
const checksumAfter = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request]);
|
|
92
97
|
bucketStorage.clearChecksumCache();
|
|
93
|
-
const checksumAfter2 = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
98
|
+
const checksumAfter2 = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request]);
|
|
94
99
|
|
|
95
100
|
expect(batchAfter.targetOp).toEqual(3n);
|
|
96
101
|
expect(dataAfter).toMatchObject([
|
|
@@ -124,6 +129,7 @@ bucket_definitions:
|
|
|
124
129
|
`)
|
|
125
130
|
);
|
|
126
131
|
const bucketStorage = factory.getInstance(syncRules);
|
|
132
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
127
133
|
|
|
128
134
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
129
135
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -168,11 +174,13 @@ bucket_definitions:
|
|
|
168
174
|
await writer.flush();
|
|
169
175
|
|
|
170
176
|
const checkpoint = writer.last_flushed_op!;
|
|
171
|
-
const request = bucketRequest(
|
|
177
|
+
const request = bucketRequest(syncRulesContent, 'global[]');
|
|
172
178
|
|
|
173
|
-
const batchBefore = await test_utils.oneFromAsync(
|
|
179
|
+
const batchBefore = await test_utils.oneFromAsync(
|
|
180
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
181
|
+
);
|
|
174
182
|
const dataBefore = batchBefore.chunkData.data;
|
|
175
|
-
const checksumBefore = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
183
|
+
const checksumBefore = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request]);
|
|
176
184
|
|
|
177
185
|
// op_id sequence depends on the storage implementation
|
|
178
186
|
expect(dataBefore).toMatchObject([
|
|
@@ -202,10 +210,12 @@ bucket_definitions:
|
|
|
202
210
|
minChangeRatio: 0
|
|
203
211
|
});
|
|
204
212
|
|
|
205
|
-
const batchAfter = await test_utils.oneFromAsync(
|
|
213
|
+
const batchAfter = await test_utils.oneFromAsync(
|
|
214
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint), [request])
|
|
215
|
+
);
|
|
206
216
|
const dataAfter = batchAfter.chunkData.data;
|
|
207
217
|
bucketStorage.clearChecksumCache();
|
|
208
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint, [request]);
|
|
218
|
+
const checksumAfter = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint), [request]);
|
|
209
219
|
|
|
210
220
|
expect(batchAfter.targetOp).toBeLessThanOrEqual(checkpoint);
|
|
211
221
|
expect(dataAfter).toMatchObject([
|
|
@@ -240,6 +250,7 @@ bucket_definitions:
|
|
|
240
250
|
`)
|
|
241
251
|
);
|
|
242
252
|
const bucketStorage = factory.getInstance(syncRules);
|
|
253
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
243
254
|
|
|
244
255
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
245
256
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -275,7 +286,7 @@ bucket_definitions:
|
|
|
275
286
|
await writer.flush();
|
|
276
287
|
|
|
277
288
|
const checkpoint1 = writer.last_flushed_op!;
|
|
278
|
-
const request = bucketRequest(
|
|
289
|
+
const request = bucketRequest(syncRulesContent, 'global[]');
|
|
279
290
|
await using writer2 = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
280
291
|
const testTable2 = await test_utils.resolveTestTable(writer2, 'test', ['id'], config);
|
|
281
292
|
await writer2.save({
|
|
@@ -298,10 +309,12 @@ bucket_definitions:
|
|
|
298
309
|
minChangeRatio: 0
|
|
299
310
|
});
|
|
300
311
|
|
|
301
|
-
const batchAfter = await test_utils.oneFromAsync(
|
|
312
|
+
const batchAfter = await test_utils.oneFromAsync(
|
|
313
|
+
bucketStorage.getBucketDataBatch(test_utils.testCheckpoint(checkpoint2), [request])
|
|
314
|
+
);
|
|
302
315
|
const dataAfter = batchAfter.chunkData.data;
|
|
303
316
|
await bucketStorage.clearChecksumCache();
|
|
304
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
|
|
317
|
+
const checksumAfter = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint2), [request]);
|
|
305
318
|
|
|
306
319
|
expect(dataAfter).toMatchObject([
|
|
307
320
|
{
|
|
@@ -328,6 +341,7 @@ bucket_definitions:
|
|
|
328
341
|
- select * from test where b = bucket.b`)
|
|
329
342
|
);
|
|
330
343
|
const bucketStorage = factory.getInstance(syncRules);
|
|
344
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
331
345
|
|
|
332
346
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
333
347
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -412,8 +426,8 @@ bucket_definitions:
|
|
|
412
426
|
|
|
413
427
|
const batchAfter = await test_utils.fromAsync(
|
|
414
428
|
bucketStorage.getBucketDataBatch(
|
|
415
|
-
checkpoint,
|
|
416
|
-
bucketRequestMap(
|
|
429
|
+
test_utils.testCheckpoint(checkpoint),
|
|
430
|
+
bucketRequestMap(syncRulesContent, [
|
|
417
431
|
['grouped["b1"]', 0n],
|
|
418
432
|
['grouped["b2"]', 0n]
|
|
419
433
|
])
|
|
@@ -458,6 +472,7 @@ bucket_definitions:
|
|
|
458
472
|
`)
|
|
459
473
|
);
|
|
460
474
|
const bucketStorage = factory.getInstance(syncRules);
|
|
475
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
461
476
|
|
|
462
477
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
463
478
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -513,9 +528,9 @@ bucket_definitions:
|
|
|
513
528
|
await writer2.commit('2/1');
|
|
514
529
|
await writer2.flush();
|
|
515
530
|
const checkpoint2 = writer2.last_flushed_op!;
|
|
516
|
-
const request = bucketRequest(
|
|
531
|
+
const request = bucketRequest(syncRulesContent, 'global[]');
|
|
517
532
|
await bucketStorage.clearChecksumCache();
|
|
518
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
|
|
533
|
+
const checksumAfter = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint2), [request]);
|
|
519
534
|
const globalChecksum = checksumAfter.get(request.bucket);
|
|
520
535
|
expect(globalChecksum).toMatchObject({
|
|
521
536
|
bucket: request.bucket,
|
|
@@ -536,6 +551,7 @@ bucket_definitions:
|
|
|
536
551
|
`)
|
|
537
552
|
);
|
|
538
553
|
const bucketStorage = factory.getInstance(syncRules);
|
|
554
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
539
555
|
|
|
540
556
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
541
557
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -562,7 +578,10 @@ bucket_definitions:
|
|
|
562
578
|
await writer.flush();
|
|
563
579
|
|
|
564
580
|
// Get checksums here just to populate the cache
|
|
565
|
-
await bucketStorage.getChecksums(
|
|
581
|
+
await bucketStorage.getChecksums(
|
|
582
|
+
test_utils.testCheckpoint(writer.last_flushed_op!),
|
|
583
|
+
bucketRequests(syncRulesContent, ['global[]'])
|
|
584
|
+
);
|
|
566
585
|
await using writer2 = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
567
586
|
const testTable2 = await test_utils.resolveTestTable(writer2, 'test', ['id'], config);
|
|
568
587
|
await writer2.save({
|
|
@@ -585,9 +604,9 @@ bucket_definitions:
|
|
|
585
604
|
});
|
|
586
605
|
|
|
587
606
|
const checkpoint2 = writer2.last_flushed_op!;
|
|
588
|
-
const request = bucketRequest(
|
|
607
|
+
const request = bucketRequest(syncRulesContent, 'global[]');
|
|
589
608
|
// Check that the checksum was correctly updated with the clear operation after having a cached checksum
|
|
590
|
-
const checksumAfter = await bucketStorage.getChecksums(checkpoint2, [request]);
|
|
609
|
+
const checksumAfter = await bucketStorage.getChecksums(test_utils.testCheckpoint(checkpoint2), [request]);
|
|
591
610
|
const globalChecksum = checksumAfter.get(request.bucket);
|
|
592
611
|
expect(globalChecksum).toMatchObject({
|
|
593
612
|
bucket: request.bucket,
|
|
@@ -607,6 +626,7 @@ bucket_definitions:
|
|
|
607
626
|
`)
|
|
608
627
|
);
|
|
609
628
|
const bucketStorage = factory.getInstance(syncRules);
|
|
629
|
+
const syncRulesContent = syncRules.syncConfigContent[0];
|
|
610
630
|
|
|
611
631
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
612
632
|
const testTable = await test_utils.resolveTestTable(writer, 'test', ['id'], config);
|
|
@@ -646,7 +666,10 @@ bucket_definitions:
|
|
|
646
666
|
});
|
|
647
667
|
|
|
648
668
|
const batchAfterDefaultCompact = await test_utils.oneFromAsync(
|
|
649
|
-
bucketStorage.getBucketDataBatch(
|
|
669
|
+
bucketStorage.getBucketDataBatch(
|
|
670
|
+
test_utils.testCheckpoint(checkpoint2),
|
|
671
|
+
bucketRequestMap(syncRulesContent, [['global[]', 0n]])
|
|
672
|
+
)
|
|
650
673
|
);
|
|
651
674
|
|
|
652
675
|
// Operation 1 should remain a PUT because op_id=2 is above the default maxOpId checkpoint.
|
|
@@ -42,7 +42,7 @@ bucket_definitions:
|
|
|
42
42
|
await using writer = await bucketStorage.createWriter(test_utils.BATCH_OPTIONS);
|
|
43
43
|
await writer.markAllSnapshotDone('1/1');
|
|
44
44
|
|
|
45
|
-
const writeCheckpoint = await
|
|
45
|
+
const writeCheckpoint = await createManagedWriteCheckpoint(bucketStorage, {
|
|
46
46
|
heads: { '1': '5/0' },
|
|
47
47
|
user_id: 'user1'
|
|
48
48
|
});
|
|
@@ -102,7 +102,7 @@ bucket_definitions:
|
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
const writeCheckpoint = await
|
|
105
|
+
const writeCheckpoint = await createManagedWriteCheckpoint(bucketStorage, {
|
|
106
106
|
heads: { '1': '6/0' },
|
|
107
107
|
user_id: 'user1'
|
|
108
108
|
});
|
|
@@ -302,3 +302,13 @@ bucket_definitions:
|
|
|
302
302
|
});
|
|
303
303
|
});
|
|
304
304
|
}
|
|
305
|
+
|
|
306
|
+
async function createManagedWriteCheckpoint(
|
|
307
|
+
bucketStorage: storage.SyncRulesBucketStorage,
|
|
308
|
+
checkpoint: storage.ManagedWriteCheckpointOptions
|
|
309
|
+
) {
|
|
310
|
+
const checkpoints = await bucketStorage.createManagedWriteCheckpoints([checkpoint]);
|
|
311
|
+
const writeCheckpoint = checkpoints.get(checkpoint.user_id);
|
|
312
|
+
expect(writeCheckpoint).not.toBeUndefined();
|
|
313
|
+
return writeCheckpoint!;
|
|
314
|
+
}
|