@powersync/service-module-postgres 0.19.3 → 0.19.5

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 (80) 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/PgManager.js +3 -2
  9. package/dist/replication/PgManager.js.map +1 -1
  10. package/dist/replication/PostgresErrorRateLimiter.js +1 -1
  11. package/dist/replication/PostgresErrorRateLimiter.js.map +1 -1
  12. package/dist/replication/SnapshotQuery.js +8 -7
  13. package/dist/replication/SnapshotQuery.js.map +1 -1
  14. package/dist/replication/WalStream.d.ts +35 -3
  15. package/dist/replication/WalStream.js +165 -28
  16. package/dist/replication/WalStream.js.map +1 -1
  17. package/dist/replication/WalStreamReplicationJob.js +8 -5
  18. package/dist/replication/WalStreamReplicationJob.js.map +1 -1
  19. package/dist/replication/replication-index.d.ts +3 -1
  20. package/dist/replication/replication-index.js +3 -1
  21. package/dist/replication/replication-index.js.map +1 -1
  22. package/dist/replication/replication-utils.d.ts +3 -11
  23. package/dist/replication/replication-utils.js +103 -165
  24. package/dist/replication/replication-utils.js.map +1 -1
  25. package/dist/replication/rquery.d.ts +5 -0
  26. package/dist/replication/rquery.js +35 -0
  27. package/dist/replication/rquery.js.map +1 -0
  28. package/dist/replication/wal-budget-utils.d.ts +23 -0
  29. package/dist/replication/wal-budget-utils.js +57 -0
  30. package/dist/replication/wal-budget-utils.js.map +1 -0
  31. package/dist/types/registry.js +1 -1
  32. package/dist/types/registry.js.map +1 -1
  33. package/dist/utils/errors.d.ts +2 -0
  34. package/dist/utils/errors.js +30 -0
  35. package/dist/utils/errors.js.map +1 -0
  36. package/package.json +17 -13
  37. package/sql/check-source-configuration.plpgsql +13 -0
  38. package/sql/debug-tables-info-batched.plpgsql +230 -0
  39. package/CHANGELOG.md +0 -858
  40. package/src/api/PostgresRouteAPIAdapter.ts +0 -356
  41. package/src/index.ts +0 -1
  42. package/src/module/PostgresModule.ts +0 -122
  43. package/src/replication/ConnectionManagerFactory.ts +0 -33
  44. package/src/replication/PgManager.ts +0 -122
  45. package/src/replication/PgRelation.ts +0 -41
  46. package/src/replication/PostgresErrorRateLimiter.ts +0 -48
  47. package/src/replication/SnapshotQuery.ts +0 -213
  48. package/src/replication/WalStream.ts +0 -1137
  49. package/src/replication/WalStreamReplicationJob.ts +0 -138
  50. package/src/replication/WalStreamReplicator.ts +0 -53
  51. package/src/replication/replication-index.ts +0 -5
  52. package/src/replication/replication-utils.ts +0 -398
  53. package/src/types/registry.ts +0 -275
  54. package/src/types/resolver.ts +0 -227
  55. package/src/types/types.ts +0 -44
  56. package/src/utils/application-name.ts +0 -8
  57. package/src/utils/migration_lib.ts +0 -80
  58. package/src/utils/populate_test_data.ts +0 -37
  59. package/src/utils/populate_test_data_worker.ts +0 -53
  60. package/src/utils/postgres_version.ts +0 -8
  61. package/test/src/checkpoints.test.ts +0 -86
  62. package/test/src/chunked_snapshots.test.ts +0 -161
  63. package/test/src/env.ts +0 -11
  64. package/test/src/large_batch.test.ts +0 -241
  65. package/test/src/pg_test.test.ts +0 -729
  66. package/test/src/resuming_snapshots.test.ts +0 -160
  67. package/test/src/route_api_adapter.test.ts +0 -62
  68. package/test/src/schema_changes.test.ts +0 -655
  69. package/test/src/setup.ts +0 -12
  70. package/test/src/slow_tests.test.ts +0 -519
  71. package/test/src/storage_combination.test.ts +0 -35
  72. package/test/src/types/registry.test.ts +0 -149
  73. package/test/src/util.ts +0 -151
  74. package/test/src/validation.test.ts +0 -63
  75. package/test/src/wal_stream.test.ts +0 -607
  76. package/test/src/wal_stream_utils.ts +0 -284
  77. package/test/tsconfig.json +0 -27
  78. package/tsconfig.json +0 -34
  79. package/tsconfig.tsbuildinfo +0 -1
  80. 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
- }