@powersync/service-module-postgres 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/types/types.d.ts +3 -0
- package/package.json +11 -11
- package/test/src/checkpoints.test.ts +4 -4
- package/test/src/chunked_snapshots.test.ts +8 -4
- package/test/src/large_batch.test.ts +16 -22
- package/test/src/resuming_snapshots.test.ts +14 -7
- package/test/src/route_api_adapter.test.ts +3 -1
- package/test/src/schema_changes.test.ts +20 -18
- package/test/src/slow_tests.test.ts +12 -6
- package/test/src/util.ts +35 -9
- package/test/src/validation.test.ts +2 -1
- package/test/src/wal_stream.test.ts +26 -24
- package/test/src/wal_stream_utils.ts +56 -10
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# @powersync/service-module-postgres
|
|
2
2
|
|
|
3
|
+
## 0.18.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 8bd83e8: Introduce storage versions.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [15aea77]
|
|
12
|
+
- Updated dependencies [0998251]
|
|
13
|
+
- Updated dependencies [65f3c89]
|
|
14
|
+
- Updated dependencies [1c45667]
|
|
15
|
+
- Updated dependencies [8785a3f]
|
|
16
|
+
- Updated dependencies [8a4c34e]
|
|
17
|
+
- Updated dependencies [b440093]
|
|
18
|
+
- Updated dependencies [d7ff4ad]
|
|
19
|
+
- Updated dependencies [c683322]
|
|
20
|
+
- Updated dependencies [8bd83e8]
|
|
21
|
+
- Updated dependencies [83989b2]
|
|
22
|
+
- Updated dependencies [79a9729]
|
|
23
|
+
- Updated dependencies [5edd95f]
|
|
24
|
+
- @powersync/lib-service-postgres@0.4.22
|
|
25
|
+
- @powersync/service-jpgwire@0.21.13
|
|
26
|
+
- @powersync/service-core@1.20.0
|
|
27
|
+
- @powersync/service-types@0.15.0
|
|
28
|
+
- @powersync/service-sync-rules@0.32.0
|
|
29
|
+
- @powersync/lib-services-framework@0.8.3
|
|
30
|
+
|
|
3
31
|
## 0.17.2
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
package/dist/types/types.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare const PostgresConnectionConfig: t.Intersection<t.Codec<{
|
|
|
28
28
|
reject_ip_ranges?: string[] | undefined;
|
|
29
29
|
slot_name_prefix?: string | undefined;
|
|
30
30
|
max_pool_size?: number | undefined;
|
|
31
|
+
connect_timeout?: number | undefined;
|
|
31
32
|
}, {
|
|
32
33
|
type: string;
|
|
33
34
|
id?: string | undefined;
|
|
@@ -51,6 +52,7 @@ export declare const PostgresConnectionConfig: t.Intersection<t.Codec<{
|
|
|
51
52
|
reject_ip_ranges?: string[] | undefined;
|
|
52
53
|
slot_name_prefix?: string | undefined;
|
|
53
54
|
max_pool_size?: number | undefined;
|
|
55
|
+
connect_timeout?: number | undefined;
|
|
54
56
|
}, string, t.CodecProps>, t.ObjectCodec<{}>>;
|
|
55
57
|
/**
|
|
56
58
|
* Config input specified when starting services
|
|
@@ -80,5 +82,6 @@ export declare function normalizeConnectionConfig(options: PostgresConnectionCon
|
|
|
80
82
|
lookup: import("net").LookupFunction | undefined;
|
|
81
83
|
client_certificate: string | undefined;
|
|
82
84
|
client_private_key: string | undefined;
|
|
85
|
+
connect_timeout_ms: number | undefined;
|
|
83
86
|
max_pool_size: number;
|
|
84
87
|
};
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.
|
|
8
|
+
"version": "0.18.0",
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"license": "FSL-1.1-ALv2",
|
|
11
11
|
"type": "module",
|
|
@@ -25,20 +25,20 @@
|
|
|
25
25
|
"semver": "^7.5.4",
|
|
26
26
|
"ts-codec": "^1.3.0",
|
|
27
27
|
"uuid": "^11.1.0",
|
|
28
|
-
"@powersync/lib-service-postgres": "0.4.
|
|
29
|
-
"@powersync/lib-services-framework": "0.8.
|
|
30
|
-
"@powersync/service-core": "1.
|
|
31
|
-
"@powersync/service-jpgwire": "0.21.
|
|
28
|
+
"@powersync/lib-service-postgres": "0.4.22",
|
|
29
|
+
"@powersync/lib-services-framework": "0.8.3",
|
|
30
|
+
"@powersync/service-core": "1.20.0",
|
|
31
|
+
"@powersync/service-jpgwire": "0.21.13",
|
|
32
32
|
"@powersync/service-jsonbig": "0.17.12",
|
|
33
|
-
"@powersync/service-sync-rules": "0.
|
|
34
|
-
"@powersync/service-types": "0.
|
|
33
|
+
"@powersync/service-sync-rules": "0.32.0",
|
|
34
|
+
"@powersync/service-types": "0.15.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/semver": "^7.5.4",
|
|
38
|
-
"@powersync/service-core-tests": "0.
|
|
39
|
-
"@powersync/service-module-mongodb-storage": "0.
|
|
40
|
-
"@powersync/lib-service-postgres": "0.4.
|
|
41
|
-
"@powersync/service-module-postgres-storage": "0.
|
|
38
|
+
"@powersync/service-core-tests": "0.14.0",
|
|
39
|
+
"@powersync/service-module-mongodb-storage": "0.14.0",
|
|
40
|
+
"@powersync/lib-service-postgres": "0.4.22",
|
|
41
|
+
"@powersync/service-module-postgres-storage": "0.12.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"build": "tsc -b",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js';
|
|
2
|
-
import { checkpointUserId, createWriteCheckpoint
|
|
2
|
+
import { checkpointUserId, createWriteCheckpoint } from '@powersync/service-core';
|
|
3
3
|
import { describe, test } from 'vitest';
|
|
4
|
-
import { describeWithStorage } from './util.js';
|
|
4
|
+
import { describeWithStorage, StorageVersionTestContext } from './util.js';
|
|
5
5
|
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
6
6
|
|
|
7
7
|
import timers from 'node:timers/promises';
|
|
@@ -15,9 +15,9 @@ describe('checkpoint tests', () => {
|
|
|
15
15
|
describeWithStorage({}, checkpointTests);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
const checkpointTests = (factory:
|
|
18
|
+
const checkpointTests = ({ factory, storageVersion }: StorageVersionTestContext) => {
|
|
19
19
|
test('write checkpoints', { timeout: 50_000 }, async () => {
|
|
20
|
-
await using context = await WalStreamTestContext.open(factory);
|
|
20
|
+
await using context = await WalStreamTestContext.open(factory, { storageVersion });
|
|
21
21
|
|
|
22
22
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
23
23
|
const { pool } = context;
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import { reduceBucket
|
|
1
|
+
import { reduceBucket } from '@powersync/service-core';
|
|
2
2
|
import { METRICS_HELPER } from '@powersync/service-core-tests';
|
|
3
3
|
import { SqliteJsonValue } from '@powersync/service-sync-rules';
|
|
4
4
|
import * as crypto from 'node:crypto';
|
|
5
5
|
import * as timers from 'timers/promises';
|
|
6
6
|
import { describe, expect, test } from 'vitest';
|
|
7
|
-
import { describeWithStorage } from './util.js';
|
|
7
|
+
import { describeWithStorage, StorageVersionTestContext } from './util.js';
|
|
8
8
|
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
9
9
|
|
|
10
10
|
describe('chunked snapshots', () => {
|
|
11
11
|
describeWithStorage({ timeout: 120_000 }, defineBatchTests);
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
function defineBatchTests(factory:
|
|
14
|
+
function defineBatchTests({ factory, storageVersion }: StorageVersionTestContext) {
|
|
15
|
+
const openContext = (options?: Parameters<typeof WalStreamTestContext.open>[1]) => {
|
|
16
|
+
return WalStreamTestContext.open(factory, { ...options, storageVersion });
|
|
17
|
+
};
|
|
18
|
+
|
|
15
19
|
// We need to test every supported type, since chunking could be quite sensitive to
|
|
16
20
|
// how each specific type is handled.
|
|
17
21
|
test('chunked snapshot edge case (int2)', async () => {
|
|
@@ -89,7 +93,7 @@ function defineBatchTests(factory: TestStorageFactory) {
|
|
|
89
93
|
// 5. Logical replication picks up the UPDATE above, but it is missing the TOAST column.
|
|
90
94
|
// 6. We end up with a row that has a missing TOAST column.
|
|
91
95
|
|
|
92
|
-
await using context = await
|
|
96
|
+
await using context = await openContext({
|
|
93
97
|
// We need to use a smaller chunk size here, so that we can run a query in between chunks
|
|
94
98
|
walStreamOptions: { snapshotChunkLength: 100 }
|
|
95
99
|
});
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { storage } from '@powersync/service-core';
|
|
2
1
|
import { describe, expect, test } from 'vitest';
|
|
3
2
|
import { populateData } from '../../dist/utils/populate_test_data.js';
|
|
4
3
|
import { env } from './env.js';
|
|
5
|
-
import { describeWithStorage, TEST_CONNECTION_OPTIONS } from './util.js';
|
|
4
|
+
import { describeWithStorage, StorageVersionTestContext, TEST_CONNECTION_OPTIONS } from './util.js';
|
|
6
5
|
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
7
6
|
|
|
8
7
|
describe.skipIf(!(env.CI || env.SLOW_TESTS))('batch replication', function () {
|
|
9
|
-
describeWithStorage({ timeout: 240_000 },
|
|
10
|
-
defineBatchTests(factory);
|
|
11
|
-
});
|
|
8
|
+
describeWithStorage({ timeout: 240_000 }, defineBatchTests);
|
|
12
9
|
});
|
|
13
10
|
|
|
14
11
|
const BASIC_SYNC_RULES = `bucket_definitions:
|
|
@@ -16,9 +13,13 @@ const BASIC_SYNC_RULES = `bucket_definitions:
|
|
|
16
13
|
data:
|
|
17
14
|
- SELECT id, description, other FROM "test_data"`;
|
|
18
15
|
|
|
19
|
-
function defineBatchTests(factory:
|
|
16
|
+
function defineBatchTests({ factory, storageVersion }: StorageVersionTestContext) {
|
|
17
|
+
const openContext = (options?: Parameters<typeof WalStreamTestContext.open>[1]) => {
|
|
18
|
+
return WalStreamTestContext.open(factory, { ...options, storageVersion });
|
|
19
|
+
};
|
|
20
|
+
|
|
20
21
|
test('update large record', async () => {
|
|
21
|
-
await using context = await
|
|
22
|
+
await using context = await openContext();
|
|
22
23
|
// This test generates a large transaction in MongoDB, despite the replicated data
|
|
23
24
|
// not being that large.
|
|
24
25
|
// If we don't limit transaction size, we could run into this error:
|
|
@@ -41,17 +42,16 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
41
42
|
|
|
42
43
|
context.startStreaming();
|
|
43
44
|
|
|
44
|
-
const
|
|
45
|
+
const checksum = await context.getChecksums(['global[]'], { timeout: 50_000 });
|
|
45
46
|
const duration = Date.now() - start;
|
|
46
47
|
const used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
47
|
-
const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
|
|
48
48
|
expect(checksum.get('global[]')!.count).toEqual(operation_count);
|
|
49
49
|
const perSecond = Math.round((operation_count / duration) * 1000);
|
|
50
50
|
console.log(`${operation_count} ops in ${duration}ms ${perSecond} ops/s. ${used}MB heap`);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
test('initial replication performance', async () => {
|
|
54
|
-
await using context = await
|
|
54
|
+
await using context = await openContext();
|
|
55
55
|
// Manual test to check initial replication performance and memory usage
|
|
56
56
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
57
57
|
const { pool } = context;
|
|
@@ -89,9 +89,8 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
89
89
|
await context.replicateSnapshot();
|
|
90
90
|
context.startStreaming();
|
|
91
91
|
|
|
92
|
-
const
|
|
92
|
+
const checksum = await context.getChecksums(['global[]'], { timeout: 100_000 });
|
|
93
93
|
const duration = Date.now() - start;
|
|
94
|
-
const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
|
|
95
94
|
expect(checksum.get('global[]')!.count).toEqual(operation_count);
|
|
96
95
|
const perSecond = Math.round((operation_count / duration) * 1000);
|
|
97
96
|
console.log(`${operation_count} ops in ${duration}ms ${perSecond} ops/s.`);
|
|
@@ -102,7 +101,7 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
102
101
|
});
|
|
103
102
|
|
|
104
103
|
test('large number of operations', async () => {
|
|
105
|
-
await using context = await
|
|
104
|
+
await using context = await openContext();
|
|
106
105
|
// This just tests performance of a large number of operations inside a transaction.
|
|
107
106
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
108
107
|
const { pool } = context;
|
|
@@ -141,10 +140,9 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
141
140
|
|
|
142
141
|
context.startStreaming();
|
|
143
142
|
|
|
144
|
-
const
|
|
143
|
+
const checksum = await context.getChecksums(['global[]']);
|
|
145
144
|
const duration = Date.now() - start;
|
|
146
145
|
const used = Math.round(process.memoryUsage().heapUsed / 1024 / 1024);
|
|
147
|
-
const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
|
|
148
146
|
expect(checksum.get('global[]')!.count).toEqual(operationCount);
|
|
149
147
|
const perSecond = Math.round((operationCount / duration) * 1000);
|
|
150
148
|
// This number depends on the test machine, so we keep the test significantly
|
|
@@ -158,10 +156,8 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
158
156
|
const truncateStart = Date.now();
|
|
159
157
|
await pool.query(`TRUNCATE test_data`);
|
|
160
158
|
|
|
161
|
-
const
|
|
159
|
+
const checksum2 = await context.getChecksums(['global[]'], { timeout: 20_000 });
|
|
162
160
|
const truncateDuration = Date.now() - truncateStart;
|
|
163
|
-
|
|
164
|
-
const checksum2 = await context.storage!.getChecksums(checkpoint2, ['global[]']);
|
|
165
161
|
const truncateCount = checksum2.get('global[]')!.count - checksum.get('global[]')!.count;
|
|
166
162
|
expect(truncateCount).toEqual(numTransactions * perTransaction);
|
|
167
163
|
const truncatePerSecond = Math.round((truncateCount / truncateDuration) * 1000);
|
|
@@ -183,7 +179,7 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
183
179
|
// 4. Another document to make sure the internal batching overflows
|
|
184
180
|
// to a second batch.
|
|
185
181
|
|
|
186
|
-
await using context = await
|
|
182
|
+
await using context = await openContext();
|
|
187
183
|
await context.updateSyncRules(`bucket_definitions:
|
|
188
184
|
global:
|
|
189
185
|
data:
|
|
@@ -227,9 +223,7 @@ function defineBatchTests(factory: storage.TestStorageFactory) {
|
|
|
227
223
|
await context.replicateSnapshot();
|
|
228
224
|
|
|
229
225
|
context.startStreaming();
|
|
230
|
-
|
|
231
|
-
const checkpoint = await context.getCheckpoint({ timeout: 50_000 });
|
|
232
|
-
const checksum = await context.storage!.getChecksums(checkpoint, ['global[]']);
|
|
226
|
+
const checksum = await context.getChecksums(['global[]'], { timeout: 50_000 });
|
|
233
227
|
expect(checksum.get('global[]')!.count).toEqual((numDocs + 2) * 4);
|
|
234
228
|
});
|
|
235
229
|
|
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
import { describe, expect, test } from 'vitest';
|
|
2
2
|
import { env } from './env.js';
|
|
3
|
-
import { describeWithStorage } from './util.js';
|
|
3
|
+
import { describeWithStorage, StorageVersionTestContext } from './util.js';
|
|
4
4
|
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
5
|
-
import { TestStorageFactory } from '@powersync/service-core';
|
|
6
5
|
import { METRICS_HELPER } from '@powersync/service-core-tests';
|
|
7
6
|
import { ReplicationMetric } from '@powersync/service-types';
|
|
8
7
|
import * as timers from 'node:timers/promises';
|
|
9
8
|
import { ReplicationAbortedError } from '@powersync/lib-services-framework';
|
|
10
9
|
|
|
11
10
|
describe.skipIf(!(env.CI || env.SLOW_TESTS))('batch replication', function () {
|
|
12
|
-
describeWithStorage({ timeout: 240_000 }, function (factory) {
|
|
11
|
+
describeWithStorage({ timeout: 240_000 }, function ({ factory, storageVersion }) {
|
|
13
12
|
test('resuming initial replication (1)', async () => {
|
|
14
13
|
// Stop early - likely to not include deleted row in first replication attempt.
|
|
15
|
-
await testResumingReplication(factory, 2000);
|
|
14
|
+
await testResumingReplication(factory, storageVersion, 2000);
|
|
16
15
|
});
|
|
17
16
|
test('resuming initial replication (2)', async () => {
|
|
18
17
|
// Stop late - likely to include deleted row in first replication attempt.
|
|
19
|
-
await testResumingReplication(factory, 8000);
|
|
18
|
+
await testResumingReplication(factory, storageVersion, 8000);
|
|
20
19
|
});
|
|
21
20
|
});
|
|
22
21
|
});
|
|
23
22
|
|
|
24
|
-
async function testResumingReplication(
|
|
23
|
+
async function testResumingReplication(
|
|
24
|
+
factory: StorageVersionTestContext['factory'],
|
|
25
|
+
storageVersion: number,
|
|
26
|
+
stopAfter: number
|
|
27
|
+
) {
|
|
25
28
|
// This tests interrupting and then resuming initial replication.
|
|
26
29
|
// We interrupt replication after test_data1 has fully replicated, and
|
|
27
30
|
// test_data2 has partially replicated.
|
|
@@ -33,7 +36,10 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
|
|
|
33
36
|
// have been / have not been replicated at that point is not deterministic.
|
|
34
37
|
// We do allow for some variation in the test results to account for this.
|
|
35
38
|
|
|
36
|
-
await using context = await WalStreamTestContext.open(factory, {
|
|
39
|
+
await using context = await WalStreamTestContext.open(factory, {
|
|
40
|
+
storageVersion,
|
|
41
|
+
walStreamOptions: { snapshotChunkLength: 1000 }
|
|
42
|
+
});
|
|
37
43
|
|
|
38
44
|
await context.updateSyncRules(`bucket_definitions:
|
|
39
45
|
global:
|
|
@@ -84,6 +90,7 @@ async function testResumingReplication(factory: TestStorageFactory, stopAfter: n
|
|
|
84
90
|
// Bypass the usual "clear db on factory open" step.
|
|
85
91
|
await using context2 = await WalStreamTestContext.open(factory, {
|
|
86
92
|
doNotClear: true,
|
|
93
|
+
storageVersion,
|
|
87
94
|
walStreamOptions: { snapshotChunkLength: 1000 }
|
|
88
95
|
});
|
|
89
96
|
|
|
@@ -20,7 +20,9 @@ describe('PostgresRouteAPIAdapter tests', () => {
|
|
|
20
20
|
`);
|
|
21
21
|
|
|
22
22
|
const schema = await api.getConnectionSchema();
|
|
23
|
-
|
|
23
|
+
// Ignore any other potential schemas in the test database, for example the 'powersync' schema.
|
|
24
|
+
const filtered = schema.filter((s) => s.name == 'public');
|
|
25
|
+
expect(filtered).toStrictEqual([
|
|
24
26
|
{
|
|
25
27
|
name: 'public',
|
|
26
28
|
tables: [
|
|
@@ -2,8 +2,7 @@ import { compareIds, putOp, reduceBucket, removeOp, test_utils } from '@powersyn
|
|
|
2
2
|
import * as timers from 'timers/promises';
|
|
3
3
|
import { describe, expect, test } from 'vitest';
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import { describeWithStorage } from './util.js';
|
|
5
|
+
import { describeWithStorage, StorageVersionTestContext } from './util.js';
|
|
7
6
|
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
8
7
|
|
|
9
8
|
describe('schema changes', { timeout: 20_000 }, function () {
|
|
@@ -24,9 +23,12 @@ const PUT_T3 = test_utils.putOp('test_data', { id: 't3', description: 'test3' })
|
|
|
24
23
|
const REMOVE_T1 = test_utils.removeOp('test_data', 't1');
|
|
25
24
|
const REMOVE_T2 = test_utils.removeOp('test_data', 't2');
|
|
26
25
|
|
|
27
|
-
function defineTests(factory:
|
|
26
|
+
function defineTests({ factory, storageVersion }: StorageVersionTestContext) {
|
|
27
|
+
const openContext = (options?: Parameters<typeof WalStreamTestContext.open>[1]) => {
|
|
28
|
+
return WalStreamTestContext.open(factory, { ...options, storageVersion });
|
|
29
|
+
};
|
|
28
30
|
test('re-create table', async () => {
|
|
29
|
-
await using context = await
|
|
31
|
+
await using context = await openContext();
|
|
30
32
|
|
|
31
33
|
// Drop a table and re-create it.
|
|
32
34
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
@@ -70,7 +72,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
70
72
|
});
|
|
71
73
|
|
|
72
74
|
test('add table', async () => {
|
|
73
|
-
await using context = await
|
|
75
|
+
await using context = await openContext();
|
|
74
76
|
// Add table after initial replication
|
|
75
77
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
76
78
|
const { pool } = context;
|
|
@@ -98,7 +100,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
98
100
|
});
|
|
99
101
|
|
|
100
102
|
test('rename table (1)', async () => {
|
|
101
|
-
await using context = await
|
|
103
|
+
await using context = await openContext();
|
|
102
104
|
const { pool } = context;
|
|
103
105
|
|
|
104
106
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
@@ -136,7 +138,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
136
138
|
});
|
|
137
139
|
|
|
138
140
|
test('rename table (2)', async () => {
|
|
139
|
-
await using context = await
|
|
141
|
+
await using context = await openContext();
|
|
140
142
|
// Rename table in sync rules -> in sync rules
|
|
141
143
|
const { pool } = context;
|
|
142
144
|
|
|
@@ -189,7 +191,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
189
191
|
});
|
|
190
192
|
|
|
191
193
|
test('rename table (3)', async () => {
|
|
192
|
-
await using context = await
|
|
194
|
+
await using context = await openContext();
|
|
193
195
|
// Rename table in sync rules -> not in sync rules
|
|
194
196
|
|
|
195
197
|
const { pool } = context;
|
|
@@ -224,7 +226,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
224
226
|
});
|
|
225
227
|
|
|
226
228
|
test('change replica id', async () => {
|
|
227
|
-
await using context = await
|
|
229
|
+
await using context = await openContext();
|
|
228
230
|
// Change replica id from default to full
|
|
229
231
|
// Causes a re-import of the table.
|
|
230
232
|
|
|
@@ -268,7 +270,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
268
270
|
});
|
|
269
271
|
|
|
270
272
|
test('change full replica id by adding column', async () => {
|
|
271
|
-
await using context = await
|
|
273
|
+
await using context = await openContext();
|
|
272
274
|
// Change replica id from full by adding column
|
|
273
275
|
// Causes a re-import of the table.
|
|
274
276
|
// Other changes such as renaming column would have the same effect
|
|
@@ -311,7 +313,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
311
313
|
});
|
|
312
314
|
|
|
313
315
|
test('change default replica id by changing column type', async () => {
|
|
314
|
-
await using context = await
|
|
316
|
+
await using context = await openContext();
|
|
315
317
|
// Change default replica id by changing column type
|
|
316
318
|
// Causes a re-import of the table.
|
|
317
319
|
const { pool } = context;
|
|
@@ -348,7 +350,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
348
350
|
});
|
|
349
351
|
|
|
350
352
|
test('change index id by changing column type', async () => {
|
|
351
|
-
await using context = await
|
|
353
|
+
await using context = await openContext();
|
|
352
354
|
// Change index replica id by changing column type
|
|
353
355
|
// Causes a re-import of the table.
|
|
354
356
|
// Secondary functionality tested here is that replica id column order stays
|
|
@@ -404,7 +406,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
404
406
|
});
|
|
405
407
|
|
|
406
408
|
test('add to publication', async () => {
|
|
407
|
-
await using context = await
|
|
409
|
+
await using context = await openContext();
|
|
408
410
|
// Add table to publication after initial replication
|
|
409
411
|
const { pool } = context;
|
|
410
412
|
|
|
@@ -447,7 +449,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
447
449
|
});
|
|
448
450
|
|
|
449
451
|
test('add to publication (not in sync rules)', async () => {
|
|
450
|
-
await using context = await
|
|
452
|
+
await using context = await openContext();
|
|
451
453
|
// Add table to publication after initial replication
|
|
452
454
|
// Since the table is not in sync rules, it should not be replicated.
|
|
453
455
|
const { pool } = context;
|
|
@@ -474,7 +476,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
474
476
|
});
|
|
475
477
|
|
|
476
478
|
test('replica identity nothing', async () => {
|
|
477
|
-
await using context = await
|
|
479
|
+
await using context = await openContext();
|
|
478
480
|
// Technically not a schema change, but fits here.
|
|
479
481
|
// Replica ID works a little differently here - the table doesn't have
|
|
480
482
|
// one defined, but we generate a unique one for each replicated row.
|
|
@@ -521,7 +523,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
521
523
|
});
|
|
522
524
|
|
|
523
525
|
test('replica identity default without PK', async () => {
|
|
524
|
-
await using context = await
|
|
526
|
+
await using context = await openContext();
|
|
525
527
|
// Same as no replica identity
|
|
526
528
|
const { pool } = context;
|
|
527
529
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
@@ -573,7 +575,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
573
575
|
// await new Promise((resolve) => setTimeout(resolve, 100));
|
|
574
576
|
// await this.snapshotTable(batch, db, result.table);
|
|
575
577
|
test('table snapshot consistency', async () => {
|
|
576
|
-
await using context = await
|
|
578
|
+
await using context = await openContext();
|
|
577
579
|
const { pool } = context;
|
|
578
580
|
|
|
579
581
|
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
@@ -640,7 +642,7 @@ function defineTests(factory: storage.TestStorageFactory) {
|
|
|
640
642
|
});
|
|
641
643
|
|
|
642
644
|
test('custom types', async () => {
|
|
643
|
-
await using context = await
|
|
645
|
+
await using context = await openContext();
|
|
644
646
|
|
|
645
647
|
await context.updateSyncRules(`
|
|
646
648
|
streams:
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
connectPgPool,
|
|
8
8
|
describeWithStorage,
|
|
9
9
|
getClientCheckpoint,
|
|
10
|
+
StorageVersionTestContext,
|
|
10
11
|
TEST_CONNECTION_OPTIONS
|
|
11
12
|
} from './util.js';
|
|
12
13
|
|
|
@@ -14,7 +15,11 @@ import * as pgwire from '@powersync/service-jpgwire';
|
|
|
14
15
|
import { SqliteRow } from '@powersync/service-sync-rules';
|
|
15
16
|
|
|
16
17
|
import { PgManager } from '@module/replication/PgManager.js';
|
|
17
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
createCoreReplicationMetrics,
|
|
20
|
+
initializeCoreReplicationMetrics,
|
|
21
|
+
updateSyncRulesFromYaml
|
|
22
|
+
} from '@powersync/service-core';
|
|
18
23
|
import { METRICS_HELPER, test_utils } from '@powersync/service-core-tests';
|
|
19
24
|
import * as mongo_storage from '@powersync/service-module-mongodb-storage';
|
|
20
25
|
import * as postgres_storage from '@powersync/service-module-postgres-storage';
|
|
@@ -22,12 +27,12 @@ import * as timers from 'node:timers/promises';
|
|
|
22
27
|
import { CustomTypeRegistry } from '@module/types/registry.js';
|
|
23
28
|
|
|
24
29
|
describe.skipIf(!(env.CI || env.SLOW_TESTS))('slow tests', function () {
|
|
25
|
-
describeWithStorage({ timeout: 120_000 }, function (factory) {
|
|
26
|
-
defineSlowTests(factory);
|
|
30
|
+
describeWithStorage({ timeout: 120_000 }, function ({ factory, storageVersion }) {
|
|
31
|
+
defineSlowTests({ factory, storageVersion });
|
|
27
32
|
});
|
|
28
33
|
});
|
|
29
34
|
|
|
30
|
-
function defineSlowTests(factory:
|
|
35
|
+
function defineSlowTests({ factory, storageVersion }: StorageVersionTestContext) {
|
|
31
36
|
let walStream: WalStream | undefined;
|
|
32
37
|
let connections: PgManager | undefined;
|
|
33
38
|
let abortController: AbortController | undefined;
|
|
@@ -81,7 +86,7 @@ bucket_definitions:
|
|
|
81
86
|
data:
|
|
82
87
|
- SELECT * FROM "test_data"
|
|
83
88
|
`;
|
|
84
|
-
const syncRules = await f.updateSyncRules({
|
|
89
|
+
const syncRules = await f.updateSyncRules(updateSyncRulesFromYaml(syncRuleContent, { storageVersion }));
|
|
85
90
|
const storage = f.getInstance(syncRules);
|
|
86
91
|
abortController = new AbortController();
|
|
87
92
|
const options: WalStreamOptions = {
|
|
@@ -306,7 +311,8 @@ bucket_definitions:
|
|
|
306
311
|
data:
|
|
307
312
|
- SELECT id, description FROM "test_data"
|
|
308
313
|
`;
|
|
309
|
-
|
|
314
|
+
|
|
315
|
+
const syncRules = await f.updateSyncRules(updateSyncRulesFromYaml(syncRuleContent, { storageVersion }));
|
|
310
316
|
const storage = f.getInstance(syncRules);
|
|
311
317
|
|
|
312
318
|
// 1. Setup some base data that will be replicated in initial replication
|
package/test/src/util.ts
CHANGED
|
@@ -2,12 +2,18 @@ import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js'
|
|
|
2
2
|
import * as types from '@module/types/types.js';
|
|
3
3
|
import * as lib_postgres from '@powersync/lib-service-postgres';
|
|
4
4
|
import { logger } from '@powersync/lib-services-framework';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
BucketStorageFactory,
|
|
7
|
+
CURRENT_STORAGE_VERSION,
|
|
8
|
+
InternalOpId,
|
|
9
|
+
LEGACY_STORAGE_VERSION,
|
|
10
|
+
TestStorageFactory
|
|
11
|
+
} from '@powersync/service-core';
|
|
6
12
|
import * as pgwire from '@powersync/service-jpgwire';
|
|
7
13
|
import * as mongo_storage from '@powersync/service-module-mongodb-storage';
|
|
8
14
|
import * as postgres_storage from '@powersync/service-module-postgres-storage';
|
|
9
|
-
import { env } from './env.js';
|
|
10
15
|
import { describe, TestOptions } from 'vitest';
|
|
16
|
+
import { env } from './env.js';
|
|
11
17
|
|
|
12
18
|
export const TEST_URI = env.PG_TEST_URL;
|
|
13
19
|
|
|
@@ -20,14 +26,34 @@ export const INITIALIZED_POSTGRES_STORAGE_FACTORY = postgres_storage.test_utils.
|
|
|
20
26
|
url: env.PG_STORAGE_TEST_URL
|
|
21
27
|
});
|
|
22
28
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
29
|
+
const TEST_STORAGE_VERSIONS = [LEGACY_STORAGE_VERSION, CURRENT_STORAGE_VERSION];
|
|
30
|
+
|
|
31
|
+
export interface StorageVersionTestContext {
|
|
32
|
+
factory: TestStorageFactory;
|
|
33
|
+
storageVersion: number;
|
|
34
|
+
}
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
export function describeWithStorage(options: TestOptions, fn: (context: StorageVersionTestContext) => void) {
|
|
37
|
+
const describeFactory = (storageName: string, factory: TestStorageFactory) => {
|
|
38
|
+
describe(`${storageName} storage`, options, function () {
|
|
39
|
+
for (const storageVersion of TEST_STORAGE_VERSIONS) {
|
|
40
|
+
describe(`storage v${storageVersion}`, function () {
|
|
41
|
+
fn({
|
|
42
|
+
factory,
|
|
43
|
+
storageVersion
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (env.TEST_MONGO_STORAGE) {
|
|
51
|
+
describeFactory('mongodb', INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (env.TEST_POSTGRES_STORAGE) {
|
|
55
|
+
describeFactory('postgres', INITIALIZED_POSTGRES_STORAGE_FACTORY);
|
|
56
|
+
}
|
|
31
57
|
}
|
|
32
58
|
|
|
33
59
|
export const TEST_CONNECTION_OPTIONS = types.normalizeConnectionConfig({
|
|
@@ -3,6 +3,7 @@ import { expect, test } from 'vitest';
|
|
|
3
3
|
|
|
4
4
|
import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js';
|
|
5
5
|
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
6
|
+
import { updateSyncRulesFromYaml } from '@powersync/service-core';
|
|
6
7
|
|
|
7
8
|
test('validate tables', async () => {
|
|
8
9
|
await using context = await WalStreamTestContext.open(INITIALIZED_MONGO_STORAGE_FACTORY);
|
|
@@ -19,7 +20,7 @@ bucket_definitions:
|
|
|
19
20
|
- SELECT * FROM "other%"
|
|
20
21
|
`;
|
|
21
22
|
|
|
22
|
-
const syncRules = await context.factory.updateSyncRules(
|
|
23
|
+
const syncRules = await context.factory.updateSyncRules(updateSyncRulesFromYaml(syncRuleContent));
|
|
23
24
|
|
|
24
25
|
const tablePatterns = syncRules.parsed({ defaultSchema: 'public' }).sync_rules.config.getSourceTables();
|
|
25
26
|
const tableInfo = await getDebugTablesInfo({
|