@powersync/service-module-postgres 0.0.0-dev-20260203155513 → 0.0.0-dev-20260223082111

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 CHANGED
@@ -1,6 +1,50 @@
1
1
  # @powersync/service-module-postgres
2
2
 
3
- ## 0.0.0-dev-20260203155513
3
+ ## 0.0.0-dev-20260223082111
4
+
5
+ ### Minor Changes
6
+
7
+ - 8bd83e8: Introduce storage versions.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [1c45667]
12
+ - Updated dependencies [8a4c34e]
13
+ - Updated dependencies [238fa85]
14
+ - Updated dependencies [8bd83e8]
15
+ - @powersync/service-sync-rules@0.0.0-dev-20260223082111
16
+ - @powersync/service-core@0.0.0-dev-20260223082111
17
+ - @powersync/lib-services-framework@0.0.0-dev-20260223082111
18
+ - @powersync/service-jpgwire@0.0.0-dev-20260223082111
19
+ - @powersync/lib-service-postgres@0.0.0-dev-20260223082111
20
+
21
+ ## 0.17.2
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [a04252d]
26
+ - @powersync/service-sync-rules@0.31.1
27
+ - @powersync/lib-services-framework@0.8.2
28
+ - @powersync/service-jpgwire@0.21.12
29
+ - @powersync/service-core@1.19.2
30
+ - @powersync/lib-service-postgres@0.4.21
31
+
32
+ ## 0.17.1
33
+
34
+ ### Patch Changes
35
+
36
+ - 479997b: Introduce `BaseSyncConfig` to represent SQL-based sync rules and precompiled sync plans.
37
+ - Updated dependencies [0e99ce0]
38
+ - Updated dependencies [479997b]
39
+ - Updated dependencies [d1c2228]
40
+ - Updated dependencies [1a1a4cc]
41
+ - @powersync/service-sync-rules@0.31.0
42
+ - @powersync/service-core@1.19.1
43
+ - @powersync/lib-services-framework@0.8.1
44
+ - @powersync/service-jpgwire@0.21.11
45
+ - @powersync/lib-service-postgres@0.4.20
46
+
47
+ ## 0.17.0
4
48
 
5
49
  ### Minor Changes
6
50
 
@@ -14,16 +58,15 @@
14
58
  - Updated dependencies [781d0e3]
15
59
  - Updated dependencies [e578245]
16
60
  - Updated dependencies [3040079]
17
- - Updated dependencies [0255483]
18
- - Updated dependencies [3207fd2]
61
+ - Updated dependencies [3b2c512]
19
62
  - Updated dependencies [a02cc58]
20
63
  - Updated dependencies [e108ffd]
21
- - @powersync/service-core@0.0.0-dev-20260203155513
22
- - @powersync/service-sync-rules@0.0.0-dev-20260203155513
23
- - @powersync/service-types@0.0.0-dev-20260203155513
24
- - @powersync/lib-services-framework@0.0.0-dev-20260203155513
25
- - @powersync/service-jpgwire@0.0.0-dev-20260203155513
26
- - @powersync/lib-service-postgres@0.0.0-dev-20260203155513
64
+ - @powersync/service-core@1.19.0
65
+ - @powersync/service-sync-rules@0.30.0
66
+ - @powersync/lib-services-framework@0.8.0
67
+ - @powersync/service-types@0.14.0
68
+ - @powersync/service-jpgwire@0.21.10
69
+ - @powersync/lib-service-postgres@0.4.19
27
70
 
28
71
  ## 0.16.16
29
72
 
@@ -20,7 +20,7 @@ export interface GetDebugTablesInfoOptions {
20
20
  publicationName: string;
21
21
  connectionTag: string;
22
22
  tablePatterns: sync_rules.TablePattern[];
23
- syncRules: sync_rules.SqlSyncRules;
23
+ syncRules: sync_rules.SyncConfig;
24
24
  }
25
25
  export declare function getDebugTablesInfo(options: GetDebugTablesInfoOptions): Promise<PatternResult[]>;
26
26
  export interface GetDebugTableInfoOptions {
@@ -30,7 +30,7 @@ export interface GetDebugTableInfoOptions {
30
30
  connectionTag: string;
31
31
  tablePattern: sync_rules.TablePattern;
32
32
  relationId: number | null;
33
- syncRules: sync_rules.SqlSyncRules;
33
+ syncRules: sync_rules.SyncConfig;
34
34
  }
35
35
  export declare function getDebugTableInfo(options: GetDebugTableInfoOptions): Promise<service_types.TableInfo>;
36
36
  export declare function cleanUpReplicationSlot(slotName: string, db: pgwire.PgClient): Promise<void>;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
- "version": "0.0.0-dev-20260203155513",
8
+ "version": "0.0.0-dev-20260223082111",
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.0.0-dev-20260203155513",
29
- "@powersync/lib-services-framework": "0.0.0-dev-20260203155513",
30
- "@powersync/service-core": "0.0.0-dev-20260203155513",
31
- "@powersync/service-jpgwire": "0.0.0-dev-20260203155513",
28
+ "@powersync/lib-service-postgres": "0.0.0-dev-20260223082111",
29
+ "@powersync/lib-services-framework": "0.0.0-dev-20260223082111",
30
+ "@powersync/service-core": "0.0.0-dev-20260223082111",
31
+ "@powersync/service-jpgwire": "0.0.0-dev-20260223082111",
32
32
  "@powersync/service-jsonbig": "0.17.12",
33
- "@powersync/service-sync-rules": "0.0.0-dev-20260203155513",
34
- "@powersync/service-types": "0.0.0-dev-20260203155513"
33
+ "@powersync/service-sync-rules": "0.0.0-dev-20260223082111",
34
+ "@powersync/service-types": "0.14.0"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/semver": "^7.5.4",
38
- "@powersync/service-core-tests": "0.0.0-dev-20260203155513",
39
- "@powersync/service-module-mongodb-storage": "0.0.0-dev-20260203155513",
40
- "@powersync/lib-service-postgres": "0.0.0-dev-20260203155513",
41
- "@powersync/service-module-postgres-storage": "0.0.0-dev-20260203155513"
38
+ "@powersync/service-core-tests": "0.0.0-dev-20260223082111",
39
+ "@powersync/service-module-mongodb-storage": "0.0.0-dev-20260223082111",
40
+ "@powersync/lib-service-postgres": "0.0.0-dev-20260223082111",
41
+ "@powersync/service-module-postgres-storage": "0.0.0-dev-20260223082111"
42
42
  },
43
43
  "scripts": {
44
44
  "build": "tsc -b",
@@ -196,7 +196,7 @@ export interface GetDebugTablesInfoOptions {
196
196
  publicationName: string;
197
197
  connectionTag: string;
198
198
  tablePatterns: sync_rules.TablePattern[];
199
- syncRules: sync_rules.SqlSyncRules;
199
+ syncRules: sync_rules.SyncConfig;
200
200
  }
201
201
 
202
202
  export async function getDebugTablesInfo(options: GetDebugTablesInfoOptions): Promise<PatternResult[]> {
@@ -296,7 +296,7 @@ export interface GetDebugTableInfoOptions {
296
296
  connectionTag: string;
297
297
  tablePattern: sync_rules.TablePattern;
298
298
  relationId: number | null;
299
- syncRules: sync_rules.SqlSyncRules;
299
+ syncRules: sync_rules.SyncConfig;
300
300
  }
301
301
 
302
302
  export async function getDebugTableInfo(options: GetDebugTableInfoOptions): Promise<service_types.TableInfo> {
@@ -1,7 +1,7 @@
1
1
  import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js';
2
- import { checkpointUserId, createWriteCheckpoint, TestStorageFactory } from '@powersync/service-core';
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: TestStorageFactory) => {
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, TestStorageFactory } from '@powersync/service-core';
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: TestStorageFactory) {
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 WalStreamTestContext.open(factory, {
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 }, function (factory) {
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: storage.TestStorageFactory) {
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 WalStreamTestContext.open(factory);
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 checkpoint = await context.getCheckpoint({ timeout: 100_000 });
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 WalStreamTestContext.open(factory);
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 checkpoint = await context.getCheckpoint({ timeout: 100_000 });
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 WalStreamTestContext.open(factory);
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 checkpoint = await context.getCheckpoint({ timeout: 50_000 });
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 checkpoint2 = await context.getCheckpoint({ timeout: 20_000 });
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 WalStreamTestContext.open(factory);
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(factory: TestStorageFactory, stopAfter: number) {
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, { walStreamOptions: { snapshotChunkLength: 1000 } });
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
- expect(schema).toStrictEqual([
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: [