@powersync/service-module-postgres 0.7.0 → 0.8.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 +25 -0
- package/dist/api/PostgresRouteAPIAdapter.d.ts +2 -1
- package/dist/api/PostgresRouteAPIAdapter.js +7 -2
- package/dist/api/PostgresRouteAPIAdapter.js.map +1 -1
- package/package.json +8 -8
- package/src/api/PostgresRouteAPIAdapter.ts +12 -7
- package/test/src/checkpoints.test.ts +74 -0
- package/test/src/slow_tests.test.ts +102 -114
- package/test/src/util.ts +5 -4
- package/test/src/wal_stream_utils.ts +0 -1
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @powersync/service-module-postgres
|
|
2
2
|
|
|
3
|
+
## 0.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 436eee6: Minor optimizations to new checkpoint calulations.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [436eee6]
|
|
12
|
+
- Updated dependencies [15283d4]
|
|
13
|
+
- Updated dependencies [88d4cb3]
|
|
14
|
+
- Updated dependencies [f55e36a]
|
|
15
|
+
- @powersync/service-core@1.7.0
|
|
16
|
+
- @powersync/service-sync-rules@0.24.0
|
|
17
|
+
- @powersync/lib-services-framework@0.5.2
|
|
18
|
+
- @powersync/lib-service-postgres@0.3.2
|
|
19
|
+
|
|
20
|
+
## 0.7.1
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- ffc8d98: Fix write checkpoint race condition
|
|
25
|
+
- Updated dependencies [ffc8d98]
|
|
26
|
+
- @powersync/service-core@0.18.1
|
|
27
|
+
|
|
3
28
|
## 0.7.0
|
|
4
29
|
|
|
5
30
|
### Minor Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { api, ParseSyncRulesOptions } from '@powersync/service-core';
|
|
1
|
+
import { api, ParseSyncRulesOptions, ReplicationHeadCallback } from '@powersync/service-core';
|
|
2
2
|
import * as pgwire from '@powersync/service-jpgwire';
|
|
3
3
|
import * as sync_rules from '@powersync/service-sync-rules';
|
|
4
4
|
import * as service_types from '@powersync/service-types';
|
|
@@ -22,5 +22,6 @@ export declare class PostgresRouteAPIAdapter implements api.RouteAPI {
|
|
|
22
22
|
protected getDebugTableInfo(tablePattern: sync_rules.TablePattern, name: string, relationId: number | null, syncRules: sync_rules.SqlSyncRules): Promise<service_types.TableInfo>;
|
|
23
23
|
getReplicationLag(options: api.ReplicationLagOptions): Promise<number | undefined>;
|
|
24
24
|
getReplicationHead(): Promise<string>;
|
|
25
|
+
createReplicationHead<T>(callback: ReplicationHeadCallback<T>): Promise<T>;
|
|
25
26
|
getConnectionSchema(): Promise<service_types.DatabaseSchema[]>;
|
|
26
27
|
}
|
|
@@ -217,11 +217,16 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`,
|
|
|
217
217
|
// However, on Aurora (Postgres compatible), it can return an entirely different LSN,
|
|
218
218
|
// causing the write checkpoints to never be replicated back to the client.
|
|
219
219
|
// For those, we need to use pg_current_wal_lsn() instead.
|
|
220
|
-
const { results } = await lib_postgres.retriedQuery(this.pool,
|
|
221
|
-
// Specifically use the lsn from the first statement, not the second one.
|
|
220
|
+
const { results } = await lib_postgres.retriedQuery(this.pool, `SELECT pg_current_wal_lsn() as lsn`);
|
|
222
221
|
const lsn = results[0].rows[0][0];
|
|
223
222
|
return String(lsn);
|
|
224
223
|
}
|
|
224
|
+
async createReplicationHead(callback) {
|
|
225
|
+
const currentLsn = await this.getReplicationHead();
|
|
226
|
+
const r = await callback(currentLsn);
|
|
227
|
+
await lib_postgres.retriedQuery(this.pool, KEEPALIVE_STATEMENT);
|
|
228
|
+
return r;
|
|
229
|
+
}
|
|
225
230
|
async getConnectionSchema() {
|
|
226
231
|
// https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts
|
|
227
232
|
const results = await lib_postgres.retriedQuery(this.pool, `SELECT
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PostgresRouteAPIAdapter.js","sourceRoot":"","sources":["../../src/api/PostgresRouteAPIAdapter.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"PostgresRouteAPIAdapter.js","sourceRoot":"","sources":["../../src/api/PostgresRouteAPIAdapter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
-
"version": "0.
|
|
8
|
+
"version": "0.8.0",
|
|
9
9
|
"main": "dist/index.js",
|
|
10
10
|
"license": "FSL-1.1-Apache-2.0",
|
|
11
11
|
"type": "module",
|
|
@@ -28,20 +28,20 @@
|
|
|
28
28
|
"ts-codec": "^1.3.0",
|
|
29
29
|
"uri-js": "^4.4.1",
|
|
30
30
|
"uuid": "^9.0.1",
|
|
31
|
-
"@powersync/lib-service-postgres": "0.3.
|
|
32
|
-
"@powersync/lib-services-framework": "0.5.
|
|
33
|
-
"@powersync/service-core": "
|
|
31
|
+
"@powersync/lib-service-postgres": "0.3.2",
|
|
32
|
+
"@powersync/lib-services-framework": "0.5.2",
|
|
33
|
+
"@powersync/service-core": "1.7.0",
|
|
34
34
|
"@powersync/service-jpgwire": "0.19.0",
|
|
35
35
|
"@powersync/service-jsonbig": "0.17.10",
|
|
36
|
-
"@powersync/service-sync-rules": "0.
|
|
36
|
+
"@powersync/service-sync-rules": "0.24.0",
|
|
37
37
|
"@powersync/service-types": "0.8.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/semver": "^7.5.4",
|
|
41
41
|
"@types/uuid": "^9.0.4",
|
|
42
|
-
"@powersync/service-core-tests": "0.
|
|
43
|
-
"@powersync/service-module-mongodb-storage": "0.
|
|
44
|
-
"@powersync/service-module-postgres-storage": "0.
|
|
42
|
+
"@powersync/service-core-tests": "0.5.0",
|
|
43
|
+
"@powersync/service-module-mongodb-storage": "0.6.0",
|
|
44
|
+
"@powersync/service-module-postgres-storage": "0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"scripts": {
|
|
47
47
|
"build": "tsc -b",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as lib_postgres from '@powersync/lib-service-postgres';
|
|
2
2
|
import { ErrorCode, ServiceError } from '@powersync/lib-services-framework';
|
|
3
|
-
import { api, ParseSyncRulesOptions } from '@powersync/service-core';
|
|
3
|
+
import { api, ParseSyncRulesOptions, ReplicationHeadCallback } from '@powersync/service-core';
|
|
4
4
|
import * as pgwire from '@powersync/service-jpgwire';
|
|
5
5
|
import * as sync_rules from '@powersync/service-sync-rules';
|
|
6
6
|
import * as service_types from '@powersync/service-types';
|
|
@@ -241,17 +241,22 @@ FROM pg_replication_slots WHERE slot_name = $1 LIMIT 1;`,
|
|
|
241
241
|
// However, on Aurora (Postgres compatible), it can return an entirely different LSN,
|
|
242
242
|
// causing the write checkpoints to never be replicated back to the client.
|
|
243
243
|
// For those, we need to use pg_current_wal_lsn() instead.
|
|
244
|
-
const { results } = await lib_postgres.retriedQuery(
|
|
245
|
-
this.pool,
|
|
246
|
-
{ statement: `SELECT pg_current_wal_lsn() as lsn` },
|
|
247
|
-
KEEPALIVE_STATEMENT
|
|
248
|
-
);
|
|
244
|
+
const { results } = await lib_postgres.retriedQuery(this.pool, `SELECT pg_current_wal_lsn() as lsn`);
|
|
249
245
|
|
|
250
|
-
// Specifically use the lsn from the first statement, not the second one.
|
|
251
246
|
const lsn = results[0].rows[0][0];
|
|
252
247
|
return String(lsn);
|
|
253
248
|
}
|
|
254
249
|
|
|
250
|
+
async createReplicationHead<T>(callback: ReplicationHeadCallback<T>): Promise<T> {
|
|
251
|
+
const currentLsn = await this.getReplicationHead();
|
|
252
|
+
|
|
253
|
+
const r = await callback(currentLsn);
|
|
254
|
+
|
|
255
|
+
await lib_postgres.retriedQuery(this.pool, KEEPALIVE_STATEMENT);
|
|
256
|
+
|
|
257
|
+
return r;
|
|
258
|
+
}
|
|
259
|
+
|
|
255
260
|
async getConnectionSchema(): Promise<service_types.DatabaseSchema[]> {
|
|
256
261
|
// https://github.com/Borvik/vscode-postgres/blob/88ec5ed061a0c9bced6c5d4ec122d0759c3f3247/src/language/server.ts
|
|
257
262
|
const results = await lib_postgres.retriedQuery(
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js';
|
|
2
|
+
import { checkpointUserId, createWriteCheckpoint } from '@powersync/service-core';
|
|
3
|
+
import { describe, test } from 'vitest';
|
|
4
|
+
import { INITIALIZED_MONGO_STORAGE_FACTORY } from './util.js';
|
|
5
|
+
import { WalStreamTestContext } from './wal_stream_utils.js';
|
|
6
|
+
import { env } from './env.js';
|
|
7
|
+
|
|
8
|
+
import timers from 'node:timers/promises';
|
|
9
|
+
|
|
10
|
+
const BASIC_SYNC_RULES = `bucket_definitions:
|
|
11
|
+
global:
|
|
12
|
+
data:
|
|
13
|
+
- SELECT id, description, other FROM "test_data"`;
|
|
14
|
+
|
|
15
|
+
describe.skipIf(!(env.CI || env.SLOW_TESTS))('checkpoint tests', () => {
|
|
16
|
+
test('write checkpoints', { timeout: 50_000 }, async () => {
|
|
17
|
+
const factory = INITIALIZED_MONGO_STORAGE_FACTORY;
|
|
18
|
+
await using context = await WalStreamTestContext.open(factory);
|
|
19
|
+
|
|
20
|
+
await context.updateSyncRules(BASIC_SYNC_RULES);
|
|
21
|
+
const { pool } = context;
|
|
22
|
+
const api = new PostgresRouteAPIAdapter(pool);
|
|
23
|
+
|
|
24
|
+
await pool.query(`CREATE TABLE test_data(id text primary key, description text, other text)`);
|
|
25
|
+
|
|
26
|
+
await context.replicateSnapshot();
|
|
27
|
+
|
|
28
|
+
context.startStreaming();
|
|
29
|
+
const storage = context.storage!;
|
|
30
|
+
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
try {
|
|
33
|
+
const stream = storage.watchWriteCheckpoint({
|
|
34
|
+
user_id: checkpointUserId('test_user', 'test_client'),
|
|
35
|
+
signal: controller.signal
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
let lastWriteCheckpoint: bigint | null = null;
|
|
39
|
+
|
|
40
|
+
(async () => {
|
|
41
|
+
try {
|
|
42
|
+
for await (const cp of stream) {
|
|
43
|
+
lastWriteCheckpoint = cp.writeCheckpoint;
|
|
44
|
+
}
|
|
45
|
+
} catch (e) {
|
|
46
|
+
if (e.name != 'AbortError') {
|
|
47
|
+
throw e;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
|
|
52
|
+
for (let i = 0; i < 10; i++) {
|
|
53
|
+
const cp = await createWriteCheckpoint({
|
|
54
|
+
userId: 'test_user',
|
|
55
|
+
clientId: 'test_client',
|
|
56
|
+
api,
|
|
57
|
+
storage: context.factory
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const start = Date.now();
|
|
61
|
+
while (lastWriteCheckpoint == null || lastWriteCheckpoint < BigInt(cp.writeCheckpoint)) {
|
|
62
|
+
if (Date.now() - start > 5_000) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Timeout while waiting for checkpoint. last: ${lastWriteCheckpoint}, waiting for: ${cp.writeCheckpoint}`
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
await timers.setTimeout(5, undefined, { signal: controller.signal });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} finally {
|
|
71
|
+
controller.abort();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -71,21 +71,13 @@ function defineSlowTests(factory: storage.TestStorageFactory) {
|
|
|
71
71
|
// Past issues that this could reproduce intermittently:
|
|
72
72
|
// * Skipping LSNs after a keepalive message
|
|
73
73
|
// * Skipping LSNs when source transactions overlap
|
|
74
|
-
test(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
{
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
test(
|
|
83
|
-
'repeated replication - compacted',
|
|
84
|
-
async () => {
|
|
85
|
-
await testRepeatedReplication({ compact: true, maxBatchSize: 100, numBatches: 2 });
|
|
86
|
-
},
|
|
87
|
-
{ timeout: TEST_DURATION_MS + TIMEOUT_MARGIN_MS }
|
|
88
|
-
);
|
|
74
|
+
test('repeated replication - basic', { timeout: TEST_DURATION_MS + TIMEOUT_MARGIN_MS }, async () => {
|
|
75
|
+
await testRepeatedReplication({ compact: false, maxBatchSize: 50, numBatches: 5 });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('repeated replication - compacted', { timeout: TEST_DURATION_MS + TIMEOUT_MARGIN_MS }, async () => {
|
|
79
|
+
await testRepeatedReplication({ compact: true, maxBatchSize: 100, numBatches: 2 });
|
|
80
|
+
});
|
|
89
81
|
|
|
90
82
|
async function testRepeatedReplication(testOptions: { compact: boolean; maxBatchSize: number; numBatches: number }) {
|
|
91
83
|
const connections = new PgManager(TEST_CONNECTION_OPTIONS, {});
|
|
@@ -101,7 +93,7 @@ bucket_definitions:
|
|
|
101
93
|
- SELECT * FROM "test_data"
|
|
102
94
|
`;
|
|
103
95
|
const syncRules = await f.updateSyncRules({ content: syncRuleContent });
|
|
104
|
-
|
|
96
|
+
const storage = f.getInstance(syncRules);
|
|
105
97
|
abortController = new AbortController();
|
|
106
98
|
const options: WalStreamOptions = {
|
|
107
99
|
abort_signal: abortController.signal,
|
|
@@ -314,116 +306,112 @@ bucket_definitions:
|
|
|
314
306
|
//
|
|
315
307
|
// If the first LSN does not correctly match with the first replication transaction,
|
|
316
308
|
// we may miss some updates.
|
|
317
|
-
test(
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
const syncRuleContent = `
|
|
309
|
+
test('repeated initial replication', { timeout: TEST_DURATION_MS + TIMEOUT_MARGIN_MS }, async () => {
|
|
310
|
+
const pool = await connectPgPool();
|
|
311
|
+
await clearTestDb(pool);
|
|
312
|
+
await using f = await factory();
|
|
313
|
+
|
|
314
|
+
const syncRuleContent = `
|
|
325
315
|
bucket_definitions:
|
|
326
316
|
global:
|
|
327
317
|
data:
|
|
328
318
|
- SELECT id, description FROM "test_data"
|
|
329
319
|
`;
|
|
330
|
-
|
|
331
|
-
|
|
320
|
+
const syncRules = await f.updateSyncRules({ content: syncRuleContent });
|
|
321
|
+
const storage = f.getInstance(syncRules);
|
|
332
322
|
|
|
333
|
-
|
|
334
|
-
|
|
323
|
+
// 1. Setup some base data that will be replicated in initial replication
|
|
324
|
+
await pool.query(`CREATE TABLE test_data(id uuid primary key default uuid_generate_v4(), description text)`);
|
|
335
325
|
|
|
336
|
-
|
|
326
|
+
let statements: pgwire.Statement[] = [];
|
|
337
327
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
328
|
+
const n = Math.floor(Math.random() * 200);
|
|
329
|
+
for (let i = 0; i < n; i++) {
|
|
330
|
+
statements.push({
|
|
331
|
+
statement: `INSERT INTO test_data(description) VALUES('test_init')`
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
await pool.query(...statements);
|
|
335
|
+
|
|
336
|
+
const start = Date.now();
|
|
337
|
+
let i = 0;
|
|
338
|
+
|
|
339
|
+
while (Date.now() - start < TEST_DURATION_MS) {
|
|
340
|
+
// 2. Each iteration starts with a clean slate
|
|
341
|
+
await pool.query(`SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots WHERE active = FALSE`);
|
|
342
|
+
i += 1;
|
|
343
|
+
|
|
344
|
+
const connections = new PgManager(TEST_CONNECTION_OPTIONS, {});
|
|
345
|
+
const replicationConnection = await connections.replicationConnection();
|
|
346
|
+
|
|
347
|
+
abortController = new AbortController();
|
|
348
|
+
const options: WalStreamOptions = {
|
|
349
|
+
abort_signal: abortController.signal,
|
|
350
|
+
connections,
|
|
351
|
+
storage: storage
|
|
352
|
+
};
|
|
353
|
+
walStream = new WalStream(options);
|
|
354
|
+
|
|
355
|
+
await storage.clear();
|
|
356
|
+
|
|
357
|
+
// 3. Start initial replication, then streaming, but don't wait for any of this
|
|
358
|
+
let initialReplicationDone = false;
|
|
359
|
+
streamPromise = (async () => {
|
|
360
|
+
await walStream.initReplication(replicationConnection);
|
|
361
|
+
await storage.autoActivate();
|
|
362
|
+
initialReplicationDone = true;
|
|
363
|
+
await walStream.streamChanges(replicationConnection);
|
|
364
|
+
})()
|
|
365
|
+
.catch((e) => {
|
|
372
366
|
initialReplicationDone = true;
|
|
373
|
-
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
})
|
|
379
|
-
.then((v) => {
|
|
380
|
-
return v;
|
|
381
|
-
});
|
|
367
|
+
throw e;
|
|
368
|
+
})
|
|
369
|
+
.then((v) => {
|
|
370
|
+
return v;
|
|
371
|
+
});
|
|
382
372
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
const results = await pool.query(...statements);
|
|
395
|
-
const ids = results.results.map((sub) => {
|
|
396
|
-
return sub.rows[0][0] as string;
|
|
397
|
-
});
|
|
398
|
-
await new Promise((resolve) => setTimeout(resolve, Math.random() * 30));
|
|
399
|
-
const deleteStatements: pgwire.Statement[] = ids.map((id) => {
|
|
400
|
-
return {
|
|
401
|
-
statement: `DELETE FROM test_data WHERE id = $1`,
|
|
402
|
-
params: [{ type: 'uuid', value: id }]
|
|
403
|
-
};
|
|
373
|
+
// 4. While initial replication is still running, write more changes
|
|
374
|
+
while (!initialReplicationDone) {
|
|
375
|
+
let statements: pgwire.Statement[] = [];
|
|
376
|
+
const n = Math.floor(Math.random() * 10) + 1;
|
|
377
|
+
for (let i = 0; i < n; i++) {
|
|
378
|
+
const description = `test${i}`;
|
|
379
|
+
statements.push({
|
|
380
|
+
statement: `INSERT INTO test_data(description) VALUES('test1') returning id as test_id`,
|
|
381
|
+
params: [{ type: 'varchar', value: description }]
|
|
404
382
|
});
|
|
405
|
-
await pool.query(...deleteStatements);
|
|
406
|
-
await new Promise((resolve) => setTimeout(resolve, Math.random() * 10));
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// 5. Once initial replication is done, wait for the streaming changes to complete syncing.
|
|
410
|
-
// getClientCheckpoint() effectively waits for the above replication to complete
|
|
411
|
-
// Race with streamingPromise to catch replication errors here.
|
|
412
|
-
let checkpoint = await Promise.race([
|
|
413
|
-
getClientCheckpoint(pool, storage.factory, { timeout: TIMEOUT_MARGIN_MS }),
|
|
414
|
-
streamPromise
|
|
415
|
-
]);
|
|
416
|
-
if (typeof checkpoint == undefined) {
|
|
417
|
-
// This indicates an issue with the test setup - streamingPromise completed instead
|
|
418
|
-
// of getClientCheckpoint()
|
|
419
|
-
throw new Error('Test failure - streamingPromise completed');
|
|
420
383
|
}
|
|
384
|
+
const results = await pool.query(...statements);
|
|
385
|
+
const ids = results.results.map((sub) => {
|
|
386
|
+
return sub.rows[0][0] as string;
|
|
387
|
+
});
|
|
388
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 30));
|
|
389
|
+
const deleteStatements: pgwire.Statement[] = ids.map((id) => {
|
|
390
|
+
return {
|
|
391
|
+
statement: `DELETE FROM test_data WHERE id = $1`,
|
|
392
|
+
params: [{ type: 'uuid', value: id }]
|
|
393
|
+
};
|
|
394
|
+
});
|
|
395
|
+
await pool.query(...deleteStatements);
|
|
396
|
+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 10));
|
|
397
|
+
}
|
|
421
398
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
399
|
+
// 5. Once initial replication is done, wait for the streaming changes to complete syncing.
|
|
400
|
+
// getClientCheckpoint() effectively waits for the above replication to complete
|
|
401
|
+
// Race with streamingPromise to catch replication errors here.
|
|
402
|
+
let checkpoint = await Promise.race([
|
|
403
|
+
getClientCheckpoint(pool, storage.factory, { timeout: TIMEOUT_MARGIN_MS }),
|
|
404
|
+
streamPromise
|
|
405
|
+
]);
|
|
406
|
+
if (typeof checkpoint == undefined) {
|
|
407
|
+
// This indicates an issue with the test setup - streamingPromise completed instead
|
|
408
|
+
// of getClientCheckpoint()
|
|
409
|
+
throw new Error('Test failure - streamingPromise completed');
|
|
425
410
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
411
|
+
|
|
412
|
+
abortController.abort();
|
|
413
|
+
await streamPromise;
|
|
414
|
+
await connections.end();
|
|
415
|
+
}
|
|
416
|
+
});
|
|
429
417
|
}
|
package/test/src/util.ts
CHANGED
|
@@ -62,13 +62,13 @@ export function connectPgPool() {
|
|
|
62
62
|
|
|
63
63
|
export async function getClientCheckpoint(
|
|
64
64
|
db: pgwire.PgClient,
|
|
65
|
-
|
|
65
|
+
storageFactory: BucketStorageFactory,
|
|
66
66
|
options?: { timeout?: number }
|
|
67
67
|
): Promise<OpId> {
|
|
68
68
|
const start = Date.now();
|
|
69
69
|
|
|
70
70
|
const api = new PostgresRouteAPIAdapter(db);
|
|
71
|
-
const lsn = await api.
|
|
71
|
+
const lsn = await api.createReplicationHead(async (lsn) => lsn);
|
|
72
72
|
|
|
73
73
|
// This old API needs a persisted checkpoint id.
|
|
74
74
|
// Since we don't use LSNs anymore, the only way to get that is to wait.
|
|
@@ -77,8 +77,9 @@ export async function getClientCheckpoint(
|
|
|
77
77
|
|
|
78
78
|
logger.info(`Waiting for LSN checkpoint: ${lsn}`);
|
|
79
79
|
while (Date.now() - start < timeout) {
|
|
80
|
-
const
|
|
81
|
-
|
|
80
|
+
const storage = await storageFactory.getActiveStorage();
|
|
81
|
+
const cp = await storage?.getCheckpoint();
|
|
82
|
+
if (cp == null) {
|
|
82
83
|
throw new Error('No sync rules available');
|
|
83
84
|
}
|
|
84
85
|
if (cp.lsn && cp.lsn >= lsn) {
|