@powersync/service-module-postgres 0.19.2 → 0.19.4

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.
Files changed (76) hide show
  1. package/dist/api/PostgresRouteAPIAdapter.d.ts +1 -1
  2. package/dist/api/PostgresRouteAPIAdapter.js +63 -72
  3. package/dist/api/PostgresRouteAPIAdapter.js.map +1 -1
  4. package/dist/module/PostgresModule.js.map +1 -1
  5. package/dist/replication/MissingReplicationSlotError.d.ts +41 -0
  6. package/dist/replication/MissingReplicationSlotError.js +33 -0
  7. package/dist/replication/MissingReplicationSlotError.js.map +1 -0
  8. package/dist/replication/PostgresErrorRateLimiter.js +1 -1
  9. package/dist/replication/PostgresErrorRateLimiter.js.map +1 -1
  10. package/dist/replication/SnapshotQuery.js +2 -2
  11. package/dist/replication/SnapshotQuery.js.map +1 -1
  12. package/dist/replication/WalStream.d.ts +37 -14
  13. package/dist/replication/WalStream.js +145 -41
  14. package/dist/replication/WalStream.js.map +1 -1
  15. package/dist/replication/WalStreamReplicationJob.d.ts +1 -1
  16. package/dist/replication/WalStreamReplicationJob.js +7 -4
  17. package/dist/replication/WalStreamReplicationJob.js.map +1 -1
  18. package/dist/replication/WalStreamReplicator.d.ts +0 -1
  19. package/dist/replication/WalStreamReplicator.js +0 -22
  20. package/dist/replication/WalStreamReplicator.js.map +1 -1
  21. package/dist/replication/replication-index.d.ts +3 -1
  22. package/dist/replication/replication-index.js +3 -1
  23. package/dist/replication/replication-index.js.map +1 -1
  24. package/dist/replication/replication-utils.d.ts +3 -11
  25. package/dist/replication/replication-utils.js +101 -164
  26. package/dist/replication/replication-utils.js.map +1 -1
  27. package/dist/replication/wal-budget-utils.d.ts +23 -0
  28. package/dist/replication/wal-budget-utils.js +57 -0
  29. package/dist/replication/wal-budget-utils.js.map +1 -0
  30. package/dist/types/registry.js +1 -1
  31. package/dist/types/registry.js.map +1 -1
  32. package/package.json +15 -11
  33. package/sql/check-source-configuration.plpgsql +13 -0
  34. package/sql/debug-tables-info-batched.plpgsql +230 -0
  35. package/CHANGELOG.md +0 -843
  36. package/src/api/PostgresRouteAPIAdapter.ts +0 -356
  37. package/src/index.ts +0 -1
  38. package/src/module/PostgresModule.ts +0 -122
  39. package/src/replication/ConnectionManagerFactory.ts +0 -33
  40. package/src/replication/PgManager.ts +0 -122
  41. package/src/replication/PgRelation.ts +0 -41
  42. package/src/replication/PostgresErrorRateLimiter.ts +0 -48
  43. package/src/replication/SnapshotQuery.ts +0 -213
  44. package/src/replication/WalStream.ts +0 -1157
  45. package/src/replication/WalStreamReplicationJob.ts +0 -138
  46. package/src/replication/WalStreamReplicator.ts +0 -79
  47. package/src/replication/replication-index.ts +0 -5
  48. package/src/replication/replication-utils.ts +0 -398
  49. package/src/types/registry.ts +0 -275
  50. package/src/types/resolver.ts +0 -227
  51. package/src/types/types.ts +0 -44
  52. package/src/utils/application-name.ts +0 -8
  53. package/src/utils/migration_lib.ts +0 -80
  54. package/src/utils/populate_test_data.ts +0 -37
  55. package/src/utils/populate_test_data_worker.ts +0 -53
  56. package/src/utils/postgres_version.ts +0 -8
  57. package/test/src/checkpoints.test.ts +0 -86
  58. package/test/src/chunked_snapshots.test.ts +0 -161
  59. package/test/src/env.ts +0 -11
  60. package/test/src/large_batch.test.ts +0 -241
  61. package/test/src/pg_test.test.ts +0 -729
  62. package/test/src/resuming_snapshots.test.ts +0 -160
  63. package/test/src/route_api_adapter.test.ts +0 -62
  64. package/test/src/schema_changes.test.ts +0 -655
  65. package/test/src/setup.ts +0 -12
  66. package/test/src/slow_tests.test.ts +0 -519
  67. package/test/src/storage_combination.test.ts +0 -35
  68. package/test/src/types/registry.test.ts +0 -149
  69. package/test/src/util.ts +0 -151
  70. package/test/src/validation.test.ts +0 -63
  71. package/test/src/wal_stream.test.ts +0 -607
  72. package/test/src/wal_stream_utils.ts +0 -284
  73. package/test/tsconfig.json +0 -27
  74. package/tsconfig.json +0 -34
  75. package/tsconfig.tsbuildinfo +0 -1
  76. package/vitest.config.ts +0 -3
@@ -1,284 +0,0 @@
1
- import { PgManager } from '@module/replication/PgManager.js';
2
- import { PUBLICATION_NAME, WalStream, WalStreamOptions } from '@module/replication/WalStream.js';
3
- import { ReplicationAbortedError } from '@powersync/lib-services-framework';
4
- import {
5
- BucketStorageFactory,
6
- createCoreReplicationMetrics,
7
- initializeCoreReplicationMetrics,
8
- InternalOpId,
9
- LEGACY_STORAGE_VERSION,
10
- OplogEntry,
11
- settledPromise,
12
- storage,
13
- STORAGE_VERSION_CONFIG,
14
- SyncRulesBucketStorage,
15
- unsettledPromise,
16
- updateSyncRulesFromYaml
17
- } from '@powersync/service-core';
18
- import { bucketRequest, METRICS_HELPER, test_utils } from '@powersync/service-core-tests';
19
- import * as pgwire from '@powersync/service-jpgwire';
20
- import { clearTestDb, getClientCheckpoint, TEST_CONNECTION_OPTIONS } from './util.js';
21
-
22
- export class WalStreamTestContext implements AsyncDisposable {
23
- private _walStream?: WalStream;
24
- private abortController = new AbortController();
25
- private syncRulesId?: number;
26
- private syncRulesContent?: storage.PersistedSyncRulesContent;
27
- public storage?: SyncRulesBucketStorage;
28
- private settledReplicationPromise?: Promise<PromiseSettledResult<void>>;
29
-
30
- /**
31
- * Tests operating on the wal stream need to configure the stream and manage asynchronous
32
- * replication, which gets a little tricky.
33
- *
34
- * This configures all the context, and tears it down afterwards.
35
- */
36
- static async open(
37
- factory: (options: storage.TestStorageOptions) => Promise<BucketStorageFactory>,
38
- options?: { doNotClear?: boolean; storageVersion?: number; walStreamOptions?: Partial<WalStreamOptions> }
39
- ) {
40
- const f = await factory({ doNotClear: options?.doNotClear });
41
- const connectionManager = new PgManager(TEST_CONNECTION_OPTIONS, {});
42
-
43
- if (!options?.doNotClear) {
44
- await clearTestDb(connectionManager.pool);
45
- }
46
-
47
- const storageVersion = options?.storageVersion ?? LEGACY_STORAGE_VERSION;
48
- const versionedBuckets = STORAGE_VERSION_CONFIG[storageVersion]?.versionedBuckets ?? false;
49
-
50
- return new WalStreamTestContext(f, connectionManager, options?.walStreamOptions, storageVersion, versionedBuckets);
51
- }
52
-
53
- constructor(
54
- public factory: BucketStorageFactory,
55
- public connectionManager: PgManager,
56
- private walStreamOptions?: Partial<WalStreamOptions>,
57
- private storageVersion: number = LEGACY_STORAGE_VERSION,
58
- private versionedBuckets: boolean = STORAGE_VERSION_CONFIG[storageVersion]?.versionedBuckets ?? false
59
- ) {
60
- createCoreReplicationMetrics(METRICS_HELPER.metricsEngine);
61
- initializeCoreReplicationMetrics(METRICS_HELPER.metricsEngine);
62
- }
63
-
64
- async [Symbol.asyncDispose]() {
65
- await this.dispose();
66
- }
67
-
68
- async dispose() {
69
- this.abortController.abort();
70
- try {
71
- await this.settledReplicationPromise;
72
- await this.connectionManager.destroy();
73
- await this.factory?.[Symbol.asyncDispose]();
74
- } catch (e) {
75
- // Throwing here may result in SuppressedError. The underlying errors often don't show up
76
- // in the test output, so we log it here.
77
- // If we could get vitest to log SuppressedError.error and SuppressedError.suppressed, we
78
- // could remove this.
79
- console.error('Error during WalStreamTestContext dispose', e);
80
- throw e;
81
- }
82
- }
83
-
84
- get pool() {
85
- return this.connectionManager.pool;
86
- }
87
-
88
- get connectionTag() {
89
- return this.connectionManager.connectionTag;
90
- }
91
-
92
- get publicationName() {
93
- return PUBLICATION_NAME;
94
- }
95
-
96
- async updateSyncRules(content: string) {
97
- const syncRules = await this.factory.updateSyncRules(
98
- updateSyncRulesFromYaml(content, { validate: true, storageVersion: this.storageVersion })
99
- );
100
- this.syncRulesId = syncRules.id;
101
- this.syncRulesContent = syncRules;
102
- this.storage = this.factory.getInstance(syncRules);
103
- return this.storage!;
104
- }
105
-
106
- async loadNextSyncRules() {
107
- const syncRules = await this.factory.getNextSyncRulesContent();
108
- if (syncRules == null) {
109
- throw new Error(`Next sync rules not available`);
110
- }
111
-
112
- this.syncRulesId = syncRules.id;
113
- this.syncRulesContent = syncRules;
114
- this.storage = this.factory.getInstance(syncRules);
115
- return this.storage!;
116
- }
117
-
118
- async loadActiveSyncRules() {
119
- const syncRules = await this.factory.getActiveSyncRulesContent();
120
- if (syncRules == null) {
121
- throw new Error(`Active sync rules not available`);
122
- }
123
-
124
- this.syncRulesId = syncRules.id;
125
- this.syncRulesContent = syncRules;
126
- this.storage = this.factory.getInstance(syncRules);
127
- return this.storage!;
128
- }
129
-
130
- private getSyncRulesContent(): storage.PersistedSyncRulesContent {
131
- if (this.syncRulesContent == null) {
132
- throw new Error('Sync rules not configured - call updateSyncRules() first');
133
- }
134
- return this.syncRulesContent;
135
- }
136
-
137
- get walStream() {
138
- if (this.storage == null) {
139
- throw new Error('updateSyncRules() first');
140
- }
141
- if (this._walStream) {
142
- return this._walStream;
143
- }
144
- const options: WalStreamOptions = {
145
- storage: this.storage,
146
- metrics: METRICS_HELPER.metricsEngine,
147
- connections: this.connectionManager,
148
- abort_signal: this.abortController.signal,
149
- ...this.walStreamOptions
150
- };
151
- this._walStream = new WalStream(options);
152
- return this._walStream!;
153
- }
154
-
155
- /**
156
- * Replicate a snapshot, start streaming, and wait for a consistent checkpoint.
157
- */
158
- async initializeReplication() {
159
- await this.replicateSnapshot();
160
- // Make sure we're up to date
161
- await this.getCheckpoint();
162
- }
163
-
164
- /**
165
- * Replicate the initial snapshot, and start streaming.
166
- */
167
- async replicateSnapshot() {
168
- // Use a settledPromise to avoid unhandled rejections
169
- this.settledReplicationPromise = settledPromise(this.walStream.replicate());
170
- try {
171
- await Promise.race([unsettledPromise(this.settledReplicationPromise), this.walStream.waitForInitialSnapshot()]);
172
- } catch (e) {
173
- if (e instanceof ReplicationAbortedError && e.cause != null) {
174
- // Edge case for tests: replicate() can throw an error, but we'd receive the ReplicationAbortedError from
175
- // waitForInitialSnapshot() first. In that case, prioritize the cause, e.g. MissingReplicationSlotError.
176
- // This is not a concern for production use, since we only use waitForInitialSnapshot() in tests.
177
- throw e.cause;
178
- }
179
- throw e;
180
- }
181
- }
182
-
183
- async getCheckpoint(options?: { timeout?: number }) {
184
- let checkpoint = await Promise.race([
185
- getClientCheckpoint(this.pool, this.factory, { timeout: options?.timeout ?? 15_000 }),
186
- unsettledPromise(this.settledReplicationPromise!)
187
- ]);
188
- if (checkpoint == null) {
189
- // This indicates an issue with the test setup - replicationPromise completed instead
190
- // of getClientCheckpoint()
191
- throw new Error('Test failure - replicationPromise completed');
192
- }
193
- return checkpoint;
194
- }
195
-
196
- async getBucketsDataBatch(buckets: Record<string, InternalOpId>, options?: { timeout?: number }) {
197
- let checkpoint = await this.getCheckpoint(options);
198
- const syncRules = this.getSyncRulesContent();
199
- const map = Object.entries(buckets).map(([bucket, start]) => bucketRequest(syncRules, bucket, start));
200
- return test_utils.fromAsync(this.storage!.getBucketDataBatch(checkpoint, map));
201
- }
202
-
203
- /**
204
- * This waits for a client checkpoint.
205
- */
206
- async getBucketData(bucket: string, start?: InternalOpId | string | undefined, options?: { timeout?: number }) {
207
- start ??= 0n;
208
- if (typeof start == 'string') {
209
- start = BigInt(start);
210
- }
211
- const syncRules = this.getSyncRulesContent();
212
- const checkpoint = await this.getCheckpoint(options);
213
- let map = [bucketRequest(syncRules, bucket, start)];
214
- let data: OplogEntry[] = [];
215
- while (true) {
216
- const batch = this.storage!.getBucketDataBatch(checkpoint, map);
217
-
218
- const batches = await test_utils.fromAsync(batch);
219
- data = data.concat(batches[0]?.chunkData.data ?? []);
220
- if (batches.length == 0 || !batches[0]!.chunkData.has_more) {
221
- break;
222
- }
223
- map = [bucketRequest(syncRules, bucket, BigInt(batches[0]!.chunkData.next_after))];
224
- }
225
- return data;
226
- }
227
-
228
- async getChecksums(buckets: string[], options?: { timeout?: number }) {
229
- const checkpoint = await this.getCheckpoint(options);
230
- const syncRules = this.getSyncRulesContent();
231
- const versionedBuckets = buckets.map((bucket) => bucketRequest(syncRules, bucket, 0n));
232
- const checksums = await this.storage!.getChecksums(checkpoint, versionedBuckets);
233
-
234
- const unversioned = new Map();
235
- for (let i = 0; i < buckets.length; i++) {
236
- unversioned.set(buckets[i], checksums.get(versionedBuckets[i].bucket)!);
237
- }
238
-
239
- return unversioned;
240
- }
241
-
242
- async getChecksum(bucket: string, options?: { timeout?: number }) {
243
- const checksums = await this.getChecksums([bucket], options);
244
- return checksums.get(bucket);
245
- }
246
-
247
- /**
248
- * This does not wait for a client checkpoint.
249
- */
250
- async getCurrentBucketData(bucket: string, start?: InternalOpId | string | undefined) {
251
- start ??= 0n;
252
- if (typeof start == 'string') {
253
- start = BigInt(start);
254
- }
255
- const syncRules = this.getSyncRulesContent();
256
- const { checkpoint } = await this.storage!.getCheckpoint();
257
- const map = [bucketRequest(syncRules, bucket, start)];
258
- const batch = this.storage!.getBucketDataBatch(checkpoint, map);
259
- const batches = await test_utils.fromAsync(batch);
260
- return batches[0]?.chunkData.data ?? [];
261
- }
262
- }
263
-
264
- export async function withMaxWalSize(db: pgwire.PgClient, size: string) {
265
- try {
266
- const r1 = await db.query(`SHOW max_slot_wal_keep_size`);
267
-
268
- await db.query(`ALTER SYSTEM SET max_slot_wal_keep_size = '100MB'`);
269
- await db.query(`SELECT pg_reload_conf()`);
270
-
271
- const oldSize = r1.results[0].rows[0].decodeWithoutCustomTypes(0);
272
-
273
- return {
274
- [Symbol.asyncDispose]: async () => {
275
- await db.query(`ALTER SYSTEM SET max_slot_wal_keep_size = '${oldSize}'`);
276
- await db.query(`SELECT pg_reload_conf()`);
277
- }
278
- };
279
- } catch (e) {
280
- const err = new Error(`Failed to configure max_slot_wal_keep_size for test`);
281
- err.cause = e;
282
- throw err;
283
- }
284
- }
@@ -1,27 +0,0 @@
1
- {
2
- "extends": "../../../tsconfig.tests.json",
3
- "compilerOptions": {
4
- "baseUrl": "./",
5
- "paths": {
6
- "@/*": ["../../../packages/service-core/src/*"],
7
- "@module/*": ["../src/*"],
8
- "@core-tests/*": ["../../../packages/service-core/test/src/*"]
9
- },
10
- "rootDir": "src"
11
- },
12
- "include": ["src"],
13
- "references": [
14
- {
15
- "path": "../"
16
- },
17
- {
18
- "path": "../../../packages/service-core-tests"
19
- },
20
- {
21
- "path": "../../module-mongodb-storage"
22
- },
23
- {
24
- "path": "../../module-postgres-storage"
25
- }
26
- ]
27
- }
package/tsconfig.json DELETED
@@ -1,34 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "dist",
6
- "esModuleInterop": true,
7
- "skipLibCheck": true,
8
- "sourceMap": true
9
- },
10
- "include": ["src"],
11
- "references": [
12
- {
13
- "path": "../../packages/types"
14
- },
15
- {
16
- "path": "../../packages/jsonbig"
17
- },
18
- {
19
- "path": "../../packages/jpgwire"
20
- },
21
- {
22
- "path": "../../packages/sync-rules"
23
- },
24
- {
25
- "path": "../../packages/service-core"
26
- },
27
- {
28
- "path": "../../libs/lib-services"
29
- },
30
- {
31
- "path": "../../libs/lib-postgres"
32
- }
33
- ]
34
- }