From c0bc100a92eefddbea730fde5bba1b954899985f Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Fri, 24 Oct 2025 07:36:22 -0600 Subject: [PATCH 1/5] Add postgres 18 to the test matrix. --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 617a2e173..a37a5e956 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -103,7 +103,7 @@ jobs: strategy: fail-fast: false matrix: - postgres-version: [11, 12, 13, 14, 15, 16, 17] + postgres-version: [11, 12, 13, 14, 15, 16, 17, 18] steps: - uses: actions/checkout@v4 From 43422b2b387b0154d96fa4d7541192973124954f Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 29 Oct 2025 14:25:55 -0600 Subject: [PATCH 2/5] Update test for postgres 18. --- .../test/src/wal_stream.test.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/modules/module-postgres/test/src/wal_stream.test.ts b/modules/module-postgres/test/src/wal_stream.test.ts index f888f3333..c7425ffdd 100644 --- a/modules/module-postgres/test/src/wal_stream.test.ts +++ b/modules/module-postgres/test/src/wal_stream.test.ts @@ -315,13 +315,22 @@ bucket_definitions: await pool.query(`UPDATE test_data SET description = 'updated'`); await pool.query('CREATE PUBLICATION powersync FOR ALL TABLES'); + const serverVersion = await context.connectionManager.getServerVersion(); + await context.loadActiveSyncRules(); - await expect(async () => { - await context.replicateSnapshot(); - }).rejects.toThrowError(MissingReplicationSlotError); - // The error is handled on a higher level, which triggers - // creating a new replication slot. + if (serverVersion!.compareMain('18.0.0') >= 0) { + await context.replicateSnapshot(); + // No error expected in Postres 18 + // TODO: introduce new test scenario for Postgres 18 that _does_ invalidate the replication slot. + } else { + // Postgres < 18 invalidates the replication slot when the publication is re-created. + // The error is handled on a higher level, which triggers + // creating a new replication slot. + await expect(async () => { + await context.replicateSnapshot(); + }).rejects.toThrowError(MissingReplicationSlotError); + } } }); From f5a18b96c6d8056f6abf8ddf672916b053ccb024 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 29 Oct 2025 14:26:57 -0600 Subject: [PATCH 3/5] Use postgres 18 for storage tests. --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a37a5e956..a52644134 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -237,7 +237,7 @@ jobs: -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=powersync_storage_test \ -p 5431:5432 \ - -d postgres:16 + -d postgres:18 - name: Setup NodeJS uses: actions/setup-node@v4 @@ -310,7 +310,7 @@ jobs: -e POSTGRES_PASSWORD=postgres \ -e POSTGRES_DB=powersync_storage_test \ -p 5431:5432 \ - -d postgres:16 + -d postgres:18 - name: Setup Node.js uses: actions/setup-node@v4 From a1780b59aa0d085ea22a7082751f3d8ad845e087 Mon Sep 17 00:00:00 2001 From: Ralf Kistner Date: Wed, 29 Oct 2025 16:08:15 -0600 Subject: [PATCH 4/5] Improve error output when tests cannot connect to the storage database. --- libs/lib-postgres/src/db/connection/DatabaseClient.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/lib-postgres/src/db/connection/DatabaseClient.ts b/libs/lib-postgres/src/db/connection/DatabaseClient.ts index 60cd47fa6..22c11f8e5 100644 --- a/libs/lib-postgres/src/db/connection/DatabaseClient.ts +++ b/libs/lib-postgres/src/db/connection/DatabaseClient.ts @@ -255,7 +255,12 @@ export class DatabaseClient extends AbstractPostgresConnection Date: Wed, 29 Oct 2025 16:44:59 -0600 Subject: [PATCH 5/5] Add test for dropped replication slot. --- .../test/src/wal_stream.test.ts | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/modules/module-postgres/test/src/wal_stream.test.ts b/modules/module-postgres/test/src/wal_stream.test.ts index c7425ffdd..f5cb622db 100644 --- a/modules/module-postgres/test/src/wal_stream.test.ts +++ b/modules/module-postgres/test/src/wal_stream.test.ts @@ -334,6 +334,58 @@ bucket_definitions: } }); + test('dropped replication slot', async () => { + { + await using context = await WalStreamTestContext.open(factory); + const { pool } = context; + await context.updateSyncRules(` +bucket_definitions: + global: + data: + - SELECT id, description FROM "test_data"`); + + await pool.query( + `CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text, num int8)` + ); + await pool.query( + `INSERT INTO test_data(id, description) VALUES('8133cd37-903b-4937-a022-7c8294015a3a', 'test1') returning id as test_id` + ); + await context.replicateSnapshot(); + await context.startStreaming(); + + const data = await context.getBucketData('global[]'); + + expect(data).toMatchObject([ + putOp('test_data', { + id: '8133cd37-903b-4937-a022-7c8294015a3a', + description: 'test1' + }) + ]); + + expect(await context.storage!.getStatus()).toMatchObject({ active: true, snapshot_done: true }); + } + + { + await using context = await WalStreamTestContext.open(factory, { doNotClear: true }); + const { pool } = context; + const storage = await context.factory.getActiveStorage(); + + // Here we explicitly drop the replication slot, which should always be handled. + await pool.query({ + statement: `SELECT pg_drop_replication_slot($1)`, + params: [{ type: 'varchar', value: storage?.slot_name! }] + }); + + await context.loadActiveSyncRules(); + + // The error is handled on a higher level, which triggers + // creating a new replication slot. + await expect(async () => { + await context.replicateSnapshot(); + }).rejects.toThrowError(MissingReplicationSlotError); + } + }); + test('old date format', async () => { await using context = await WalStreamTestContext.open(factory); await context.updateSyncRules(BASIC_SYNC_RULES);