@powersync/service-module-postgres 0.0.4 → 0.2.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 +31 -0
- package/dist/api/PostgresRouteAPIAdapter.d.ts +6 -2
- package/dist/api/PostgresRouteAPIAdapter.js +24 -10
- package/dist/api/PostgresRouteAPIAdapter.js.map +1 -1
- package/dist/module/PostgresModule.js +9 -2
- package/dist/module/PostgresModule.js.map +1 -1
- package/dist/replication/PgManager.js +15 -1
- package/dist/replication/PgManager.js.map +1 -1
- package/dist/replication/WalStream.d.ts +9 -2
- package/dist/replication/WalStream.js +185 -151
- package/dist/replication/WalStream.js.map +1 -1
- package/package.json +5 -5
- package/src/api/PostgresRouteAPIAdapter.ts +31 -12
- package/src/module/PostgresModule.ts +11 -2
- package/src/replication/PgManager.ts +19 -1
- package/src/replication/WalStream.ts +205 -165
- package/test/src/large_batch.test.ts +268 -148
- package/test/src/schema_changes.test.ts +562 -513
- package/test/src/slow_tests.test.ts +2 -1
- package/test/src/util.ts +3 -1
- package/test/src/validation.test.ts +45 -48
- package/test/src/wal_stream.test.ts +224 -249
- package/test/src/wal_stream_utils.ts +61 -22
- package/tsconfig.tsbuildinfo +1 -1
- package/test/src/__snapshots__/pg_test.test.ts.snap +0 -256
|
@@ -1,29 +1,10 @@
|
|
|
1
1
|
import { fromAsync } from '@core-tests/stream_utils.js';
|
|
2
2
|
import { PgManager } from '@module/replication/PgManager.js';
|
|
3
3
|
import { PUBLICATION_NAME, WalStream, WalStreamOptions } from '@module/replication/WalStream.js';
|
|
4
|
-
import { BucketStorageFactory, SyncRulesBucketStorage } from '@powersync/service-core';
|
|
4
|
+
import { BucketStorageFactory, OplogEntry, SyncRulesBucketStorage } from '@powersync/service-core';
|
|
5
5
|
import * as pgwire from '@powersync/service-jpgwire';
|
|
6
6
|
import { clearTestDb, getClientCheckpoint, TEST_CONNECTION_OPTIONS } from './util.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Tests operating on the wal stream need to configure the stream and manage asynchronous
|
|
10
|
-
* replication, which gets a little tricky.
|
|
11
|
-
*
|
|
12
|
-
* This wraps a test in a function that configures all the context, and tears it down afterwards.
|
|
13
|
-
*/
|
|
14
|
-
export function walStreamTest(
|
|
15
|
-
factory: () => Promise<BucketStorageFactory>,
|
|
16
|
-
test: (context: WalStreamTestContext) => Promise<void>
|
|
17
|
-
): () => Promise<void> {
|
|
18
|
-
return async () => {
|
|
19
|
-
const f = await factory();
|
|
20
|
-
const connectionManager = new PgManager(TEST_CONNECTION_OPTIONS, {});
|
|
21
|
-
|
|
22
|
-
await clearTestDb(connectionManager.pool);
|
|
23
|
-
await using context = new WalStreamTestContext(f, connectionManager);
|
|
24
|
-
await test(context);
|
|
25
|
-
};
|
|
26
|
-
}
|
|
7
|
+
import { StorageOptions } from '@core-tests/util.js';
|
|
27
8
|
|
|
28
9
|
export class WalStreamTestContext implements AsyncDisposable {
|
|
29
10
|
private _walStream?: WalStream;
|
|
@@ -32,12 +13,36 @@ export class WalStreamTestContext implements AsyncDisposable {
|
|
|
32
13
|
public storage?: SyncRulesBucketStorage;
|
|
33
14
|
private replicationConnection?: pgwire.PgConnection;
|
|
34
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Tests operating on the wal stream need to configure the stream and manage asynchronous
|
|
18
|
+
* replication, which gets a little tricky.
|
|
19
|
+
*
|
|
20
|
+
* This configures all the context, and tears it down afterwards.
|
|
21
|
+
*/
|
|
22
|
+
static async open(
|
|
23
|
+
factory: (options: StorageOptions) => Promise<BucketStorageFactory>,
|
|
24
|
+
options?: { doNotClear?: boolean }
|
|
25
|
+
) {
|
|
26
|
+
const f = await factory({ doNotClear: options?.doNotClear });
|
|
27
|
+
const connectionManager = new PgManager(TEST_CONNECTION_OPTIONS, {});
|
|
28
|
+
|
|
29
|
+
if (!options?.doNotClear) {
|
|
30
|
+
await clearTestDb(connectionManager.pool);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return new WalStreamTestContext(f, connectionManager);
|
|
34
|
+
}
|
|
35
|
+
|
|
35
36
|
constructor(
|
|
36
37
|
public factory: BucketStorageFactory,
|
|
37
38
|
public connectionManager: PgManager
|
|
38
39
|
) {}
|
|
39
40
|
|
|
40
41
|
async [Symbol.asyncDispose]() {
|
|
42
|
+
await this.dispose();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async dispose() {
|
|
41
46
|
this.abortController.abort();
|
|
42
47
|
await this.streamPromise;
|
|
43
48
|
await this.connectionManager.destroy();
|
|
@@ -62,6 +67,16 @@ export class WalStreamTestContext implements AsyncDisposable {
|
|
|
62
67
|
return this.storage!;
|
|
63
68
|
}
|
|
64
69
|
|
|
70
|
+
async loadNextSyncRules() {
|
|
71
|
+
const syncRules = await this.factory.getNextSyncRulesContent();
|
|
72
|
+
if (syncRules == null) {
|
|
73
|
+
throw new Error(`Next sync rules not available`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.storage = this.factory.getInstance(syncRules);
|
|
77
|
+
return this.storage!;
|
|
78
|
+
}
|
|
79
|
+
|
|
65
80
|
get walStream() {
|
|
66
81
|
if (this.storage == null) {
|
|
67
82
|
throw new Error('updateSyncRules() first');
|
|
@@ -110,9 +125,33 @@ export class WalStreamTestContext implements AsyncDisposable {
|
|
|
110
125
|
return fromAsync(this.storage!.getBucketDataBatch(checkpoint, map));
|
|
111
126
|
}
|
|
112
127
|
|
|
128
|
+
/**
|
|
129
|
+
* This waits for a client checkpoint.
|
|
130
|
+
*/
|
|
113
131
|
async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) {
|
|
114
132
|
start ??= '0';
|
|
115
|
-
|
|
133
|
+
const checkpoint = await this.getCheckpoint(options);
|
|
134
|
+
const map = new Map<string, string>([[bucket, start]]);
|
|
135
|
+
let data: OplogEntry[] = [];
|
|
136
|
+
while (true) {
|
|
137
|
+
const batch = this.storage!.getBucketDataBatch(checkpoint, map);
|
|
138
|
+
|
|
139
|
+
const batches = await fromAsync(batch);
|
|
140
|
+
data = data.concat(batches[0]?.batch.data ?? []);
|
|
141
|
+
if (batches.length == 0 || !batches[0]!.batch.has_more) {
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
map.set(bucket, batches[0]!.batch.next_after);
|
|
145
|
+
}
|
|
146
|
+
return data;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* This does not wait for a client checkpoint.
|
|
151
|
+
*/
|
|
152
|
+
async getCurrentBucketData(bucket: string, start?: string) {
|
|
153
|
+
start ??= '0';
|
|
154
|
+
const { checkpoint } = await this.storage!.getCheckpoint();
|
|
116
155
|
const map = new Map<string, string>([[bucket, start]]);
|
|
117
156
|
const batch = this.storage!.getBucketDataBatch(checkpoint, map);
|
|
118
157
|
const batches = await fromAsync(batch);
|