Skip to content

Commit 48a8678

Browse files
authored
Test Postgres 18 (#386)
* Add postgres 18 to the test matrix. * Update test for postgres 18. * Use postgres 18 for storage tests. * Improve error output when tests cannot connect to the storage database. * Add test for dropped replication slot.
1 parent 88982d9 commit 48a8678

File tree

3 files changed

+72
-6
lines changed

3 files changed

+72
-6
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ jobs:
8989
strategy:
9090
fail-fast: false
9191
matrix:
92-
postgres-version: [11, 12, 13, 14, 15, 16, 17]
92+
postgres-version: [11, 12, 13, 14, 15, 16, 17, 18]
9393

9494
steps:
9595
- uses: actions/checkout@v5
@@ -210,7 +210,7 @@ jobs:
210210
-e POSTGRES_PASSWORD=postgres \
211211
-e POSTGRES_DB=powersync_storage_test \
212212
-p 5431:5432 \
213-
-d postgres:16
213+
-d postgres:18
214214
215215
- name: Enable Corepack
216216
run: corepack enable
@@ -270,7 +270,7 @@ jobs:
270270
-e POSTGRES_PASSWORD=postgres \
271271
-e POSTGRES_DB=powersync_storage_test \
272272
-p 5431:5432 \
273-
-d postgres:16
273+
-d postgres:18
274274
275275
- name: Enable Corepack
276276
run: corepack enable

libs/lib-postgres/src/db/connection/DatabaseClient.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,12 @@ export class DatabaseClient extends AbstractPostgresConnection<DatabaseClientLis
255255
}
256256

257257
async [Symbol.asyncDispose]() {
258-
await this.initialized;
258+
try {
259+
await this.initialized;
260+
} catch (e) {
261+
// Error was already reported when initializing - ignore here.
262+
// If if throw this, we typically get a SuppressedError, which is difficult to debug.
263+
}
259264
this.closed = true;
260265

261266
for (const c of this.connections) {

modules/module-postgres/test/src/wal_stream.test.ts

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,13 +315,74 @@ bucket_definitions:
315315
await pool.query(`UPDATE test_data SET description = 'updated'`);
316316
await pool.query('CREATE PUBLICATION powersync FOR ALL TABLES');
317317

318+
const serverVersion = await context.connectionManager.getServerVersion();
319+
318320
await context.loadActiveSyncRules();
319-
await expect(async () => {
321+
322+
if (serverVersion!.compareMain('18.0.0') >= 0) {
320323
await context.replicateSnapshot();
321-
}).rejects.toThrowError(MissingReplicationSlotError);
324+
// No error expected in Postres 18
325+
// TODO: introduce new test scenario for Postgres 18 that _does_ invalidate the replication slot.
326+
} else {
327+
// Postgres < 18 invalidates the replication slot when the publication is re-created.
328+
// The error is handled on a higher level, which triggers
329+
// creating a new replication slot.
330+
await expect(async () => {
331+
await context.replicateSnapshot();
332+
}).rejects.toThrowError(MissingReplicationSlotError);
333+
}
334+
}
335+
});
336+
337+
test('dropped replication slot', async () => {
338+
{
339+
await using context = await WalStreamTestContext.open(factory);
340+
const { pool } = context;
341+
await context.updateSyncRules(`
342+
bucket_definitions:
343+
global:
344+
data:
345+
- SELECT id, description FROM "test_data"`);
346+
347+
await pool.query(
348+
`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text, num int8)`
349+
);
350+
await pool.query(
351+
`INSERT INTO test_data(id, description) VALUES('8133cd37-903b-4937-a022-7c8294015a3a', 'test1') returning id as test_id`
352+
);
353+
await context.replicateSnapshot();
354+
await context.startStreaming();
355+
356+
const data = await context.getBucketData('global[]');
357+
358+
expect(data).toMatchObject([
359+
putOp('test_data', {
360+
id: '8133cd37-903b-4937-a022-7c8294015a3a',
361+
description: 'test1'
362+
})
363+
]);
364+
365+
expect(await context.storage!.getStatus()).toMatchObject({ active: true, snapshot_done: true });
366+
}
367+
368+
{
369+
await using context = await WalStreamTestContext.open(factory, { doNotClear: true });
370+
const { pool } = context;
371+
const storage = await context.factory.getActiveStorage();
372+
373+
// Here we explicitly drop the replication slot, which should always be handled.
374+
await pool.query({
375+
statement: `SELECT pg_drop_replication_slot($1)`,
376+
params: [{ type: 'varchar', value: storage?.slot_name! }]
377+
});
378+
379+
await context.loadActiveSyncRules();
322380

323381
// The error is handled on a higher level, which triggers
324382
// creating a new replication slot.
383+
await expect(async () => {
384+
await context.replicateSnapshot();
385+
}).rejects.toThrowError(MissingReplicationSlotError);
325386
}
326387
});
327388

0 commit comments

Comments
 (0)