@powersync/service-module-postgres 0.0.0-dev-20250304151813 → 0.0.0-dev-20250306152715

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.
@@ -1,5 +1,5 @@
1
1
  import * as bson from 'bson';
2
- import { afterEach, describe, expect, test } from 'vitest';
2
+ import { afterEach, beforeAll, describe, expect, test } from 'vitest';
3
3
  import { WalStream, WalStreamOptions } from '../../src/replication/WalStream.js';
4
4
  import { env } from './env.js';
5
5
  import {
@@ -15,8 +15,8 @@ import * as pgwire from '@powersync/service-jpgwire';
15
15
  import { SqliteRow } from '@powersync/service-sync-rules';
16
16
 
17
17
  import { PgManager } from '@module/replication/PgManager.js';
18
- import { storage } from '@powersync/service-core';
19
- import { test_utils } from '@powersync/service-core-tests';
18
+ import { createCoreReplicationMetrics, initializeCoreReplicationMetrics, storage } from '@powersync/service-core';
19
+ import { METRICS_HELPER, test_utils } from '@powersync/service-core-tests';
20
20
  import * as mongo_storage from '@powersync/service-module-mongodb-storage';
21
21
  import * as postgres_storage from '@powersync/service-module-postgres-storage';
22
22
  import * as timers from 'node:timers/promises';
@@ -49,6 +49,11 @@ function defineSlowTests(factory: storage.TestStorageFactory) {
49
49
  let abortController: AbortController | undefined;
50
50
  let streamPromise: Promise<void> | undefined;
51
51
 
52
+ beforeAll(async () => {
53
+ createCoreReplicationMetrics(METRICS_HELPER.metricsEngine);
54
+ initializeCoreReplicationMetrics(METRICS_HELPER.metricsEngine);
55
+ });
56
+
52
57
  afterEach(async () => {
53
58
  // This cleans up, similar to WalStreamTestContext.dispose().
54
59
  // These tests are a little more complex than what is supported by WalStreamTestContext.
@@ -98,7 +103,8 @@ bucket_definitions:
98
103
  const options: WalStreamOptions = {
99
104
  abort_signal: abortController.signal,
100
105
  connections,
101
- storage: storage
106
+ storage: storage,
107
+ metrics: METRICS_HELPER.metricsEngine
102
108
  };
103
109
  walStream = new WalStream(options);
104
110
 
@@ -178,7 +184,7 @@ bucket_definitions:
178
184
  break;
179
185
  }
180
186
 
181
- const checkpoint = BigInt((await storage.getCheckpoint()).checkpoint);
187
+ const checkpoint = (await storage.getCheckpoint()).checkpoint;
182
188
  if (f instanceof mongo_storage.storage.MongoBucketStorage) {
183
189
  const opsBefore = (await f.db.bucket_data.find().sort({ _id: 1 }).toArray())
184
190
  .filter((row) => row._id.o <= checkpoint)
@@ -344,13 +350,14 @@ bucket_definitions:
344
350
  const connections = new PgManager(TEST_CONNECTION_OPTIONS, {});
345
351
  const replicationConnection = await connections.replicationConnection();
346
352
 
347
- abortController = new AbortController();
348
- const options: WalStreamOptions = {
349
- abort_signal: abortController.signal,
350
- connections,
351
- storage: storage
352
- };
353
- walStream = new WalStream(options);
353
+ abortController = new AbortController();
354
+ const options: WalStreamOptions = {
355
+ abort_signal: abortController.signal,
356
+ connections,
357
+ storage: storage,
358
+ metrics: METRICS_HELPER.metricsEngine
359
+ };
360
+ walStream = new WalStream(options);
354
361
 
355
362
  await storage.clear();
356
363
 
@@ -403,7 +410,7 @@ bucket_definitions:
403
410
  getClientCheckpoint(pool, storage.factory, { timeout: TIMEOUT_MARGIN_MS }),
404
411
  streamPromise
405
412
  ]);
406
- if (typeof checkpoint == undefined) {
413
+ if (checkpoint == null) {
407
414
  // This indicates an issue with the test setup - streamingPromise completed instead
408
415
  // of getClientCheckpoint()
409
416
  throw new Error('Test failure - streamingPromise completed');
package/test/src/util.ts CHANGED
@@ -2,7 +2,7 @@ import { PostgresRouteAPIAdapter } from '@module/api/PostgresRouteAPIAdapter.js'
2
2
  import * as types from '@module/types/types.js';
3
3
  import * as lib_postgres from '@powersync/lib-service-postgres';
4
4
  import { logger } from '@powersync/lib-services-framework';
5
- import { BucketStorageFactory, OpId } from '@powersync/service-core';
5
+ import { BucketStorageFactory, InternalOpId, TestStorageOptions } from '@powersync/service-core';
6
6
  import * as pgwire from '@powersync/service-jpgwire';
7
7
  import * as mongo_storage from '@powersync/service-module-mongodb-storage';
8
8
  import * as postgres_storage from '@powersync/service-module-postgres-storage';
@@ -64,7 +64,7 @@ export async function getClientCheckpoint(
64
64
  db: pgwire.PgClient,
65
65
  storageFactory: BucketStorageFactory,
66
66
  options?: { timeout?: number }
67
- ): Promise<OpId> {
67
+ ): Promise<InternalOpId> {
68
68
  const start = Date.now();
69
69
 
70
70
  const api = new PostgresRouteAPIAdapter(db);
@@ -1,12 +1,13 @@
1
1
  import { MissingReplicationSlotError } from '@module/replication/WalStream.js';
2
- import { Metrics, storage } from '@powersync/service-core';
3
- import { putOp, removeOp } from '@powersync/service-core-tests';
2
+ import { storage } from '@powersync/service-core';
3
+ import { METRICS_HELPER, putOp, removeOp } from '@powersync/service-core-tests';
4
4
  import { pgwireRows } from '@powersync/service-jpgwire';
5
5
  import * as crypto from 'crypto';
6
6
  import { describe, expect, test } from 'vitest';
7
7
  import { env } from './env.js';
8
8
  import { INITIALIZED_MONGO_STORAGE_FACTORY, INITIALIZED_POSTGRES_STORAGE_FACTORY } from './util.js';
9
9
  import { WalStreamTestContext } from './wal_stream_utils.js';
10
+ import { ReplicationMetric } from '@powersync/service-types';
10
11
 
11
12
  const BASIC_SYNC_RULES = `
12
13
  bucket_definitions:
@@ -40,9 +41,9 @@ bucket_definitions:
40
41
 
41
42
  await context.replicateSnapshot();
42
43
 
43
- const startRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
44
+ const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED_TOTAL)) ?? 0;
44
45
  const startTxCount =
45
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
46
+ (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED_TOTAL)) ?? 0;
46
47
 
47
48
  context.startStreaming();
48
49
 
@@ -55,9 +56,9 @@ bucket_definitions:
55
56
  const data = await context.getBucketData('global[]');
56
57
 
57
58
  expect(data).toMatchObject([putOp('test_data', { id: test_id, description: 'test1', num: 1152921504606846976n })]);
58
- const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
59
+ const endRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED_TOTAL)) ?? 0;
59
60
  const endTxCount =
60
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
61
+ (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED_TOTAL)) ?? 0;
61
62
  expect(endRowCount - startRowCount).toEqual(1);
62
63
  expect(endTxCount - startTxCount).toEqual(1);
63
64
  });
@@ -77,9 +78,9 @@ bucket_definitions:
77
78
 
78
79
  await context.replicateSnapshot();
79
80
 
80
- const startRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
81
+ const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED_TOTAL)) ?? 0;
81
82
  const startTxCount =
82
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
83
+ (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED_TOTAL)) ?? 0;
83
84
 
84
85
  context.startStreaming();
85
86
 
@@ -90,9 +91,9 @@ bucket_definitions:
90
91
  const data = await context.getBucketData('global[]');
91
92
 
92
93
  expect(data).toMatchObject([putOp('test_DATA', { id: test_id, description: 'test1' })]);
93
- const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
94
+ const endRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED_TOTAL)) ?? 0;
94
95
  const endTxCount =
95
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
96
+ (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED_TOTAL)) ?? 0;
96
97
  expect(endRowCount - startRowCount).toEqual(1);
97
98
  expect(endTxCount - startTxCount).toEqual(1);
98
99
  });
@@ -274,9 +275,9 @@ bucket_definitions:
274
275
 
275
276
  await context.replicateSnapshot();
276
277
 
277
- const startRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
278
+ const startRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED_TOTAL)) ?? 0;
278
279
  const startTxCount =
279
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
280
+ (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED_TOTAL)) ?? 0;
280
281
 
281
282
  context.startStreaming();
282
283
 
@@ -287,9 +288,9 @@ bucket_definitions:
287
288
  const data = await context.getBucketData('global[]');
288
289
 
289
290
  expect(data).toMatchObject([]);
290
- const endRowCount = (await Metrics.getInstance().getMetricValueForTests('powersync_rows_replicated_total')) ?? 0;
291
+ const endRowCount = (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.ROWS_REPLICATED_TOTAL)) ?? 0;
291
292
  const endTxCount =
292
- (await Metrics.getInstance().getMetricValueForTests('powersync_transactions_replicated_total')) ?? 0;
293
+ (await METRICS_HELPER.getMetricValueForTests(ReplicationMetric.TRANSACTIONS_REPLICATED_TOTAL)) ?? 0;
293
294
 
294
295
  // There was a transaction, but we should not replicate any actual data
295
296
  expect(endRowCount - startRowCount).toEqual(0);
@@ -1,7 +1,15 @@
1
1
  import { PgManager } from '@module/replication/PgManager.js';
2
2
  import { PUBLICATION_NAME, WalStream, WalStreamOptions } from '@module/replication/WalStream.js';
3
- import { BucketStorageFactory, OplogEntry, storage, SyncRulesBucketStorage } from '@powersync/service-core';
4
- import { test_utils } from '@powersync/service-core-tests';
3
+ import {
4
+ BucketStorageFactory,
5
+ createCoreReplicationMetrics,
6
+ initializeCoreReplicationMetrics,
7
+ InternalOpId,
8
+ OplogEntry,
9
+ storage,
10
+ SyncRulesBucketStorage
11
+ } from '@powersync/service-core';
12
+ import { METRICS_HELPER, test_utils } from '@powersync/service-core-tests';
5
13
  import * as pgwire from '@powersync/service-jpgwire';
6
14
  import { clearTestDb, getClientCheckpoint, TEST_CONNECTION_OPTIONS } from './util.js';
7
15
 
@@ -35,7 +43,10 @@ export class WalStreamTestContext implements AsyncDisposable {
35
43
  constructor(
36
44
  public factory: BucketStorageFactory,
37
45
  public connectionManager: PgManager
38
- ) {}
46
+ ) {
47
+ createCoreReplicationMetrics(METRICS_HELPER.metricsEngine);
48
+ initializeCoreReplicationMetrics(METRICS_HELPER.metricsEngine);
49
+ }
39
50
 
40
51
  async [Symbol.asyncDispose]() {
41
52
  await this.dispose();
@@ -95,6 +106,7 @@ export class WalStreamTestContext implements AsyncDisposable {
95
106
  }
96
107
  const options: WalStreamOptions = {
97
108
  storage: this.storage,
109
+ metrics: METRICS_HELPER.metricsEngine,
98
110
  connections: this.connectionManager,
99
111
  abort_signal: this.abortController.signal
100
112
  };
@@ -120,27 +132,30 @@ export class WalStreamTestContext implements AsyncDisposable {
120
132
  getClientCheckpoint(this.pool, this.factory, { timeout: options?.timeout ?? 15_000 }),
121
133
  this.streamPromise
122
134
  ]);
123
- if (typeof checkpoint == undefined) {
135
+ if (checkpoint == null) {
124
136
  // This indicates an issue with the test setup - streamingPromise completed instead
125
137
  // of getClientCheckpoint()
126
138
  throw new Error('Test failure - streamingPromise completed');
127
139
  }
128
- return checkpoint as string;
140
+ return checkpoint;
129
141
  }
130
142
 
131
- async getBucketsDataBatch(buckets: Record<string, string>, options?: { timeout?: number }) {
143
+ async getBucketsDataBatch(buckets: Record<string, InternalOpId>, options?: { timeout?: number }) {
132
144
  let checkpoint = await this.getCheckpoint(options);
133
- const map = new Map<string, string>(Object.entries(buckets));
145
+ const map = new Map<string, InternalOpId>(Object.entries(buckets));
134
146
  return test_utils.fromAsync(this.storage!.getBucketDataBatch(checkpoint, map));
135
147
  }
136
148
 
137
149
  /**
138
150
  * This waits for a client checkpoint.
139
151
  */
140
- async getBucketData(bucket: string, start?: string, options?: { timeout?: number }) {
141
- start ??= '0';
152
+ async getBucketData(bucket: string, start?: InternalOpId | string | undefined, options?: { timeout?: number }) {
153
+ start ??= 0n;
154
+ if (typeof start == 'string') {
155
+ start = BigInt(start);
156
+ }
142
157
  const checkpoint = await this.getCheckpoint(options);
143
- const map = new Map<string, string>([[bucket, start]]);
158
+ const map = new Map<string, InternalOpId>([[bucket, start]]);
144
159
  let data: OplogEntry[] = [];
145
160
  while (true) {
146
161
  const batch = this.storage!.getBucketDataBatch(checkpoint, map);
@@ -150,7 +165,7 @@ export class WalStreamTestContext implements AsyncDisposable {
150
165
  if (batches.length == 0 || !batches[0]!.batch.has_more) {
151
166
  break;
152
167
  }
153
- map.set(bucket, batches[0]!.batch.next_after);
168
+ map.set(bucket, BigInt(batches[0]!.batch.next_after));
154
169
  }
155
170
  return data;
156
171
  }
@@ -158,10 +173,13 @@ export class WalStreamTestContext implements AsyncDisposable {
158
173
  /**
159
174
  * This does not wait for a client checkpoint.
160
175
  */
161
- async getCurrentBucketData(bucket: string, start?: string) {
162
- start ??= '0';
176
+ async getCurrentBucketData(bucket: string, start?: InternalOpId | string | undefined) {
177
+ start ??= 0n;
178
+ if (typeof start == 'string') {
179
+ start = BigInt(start);
180
+ }
163
181
  const { checkpoint } = await this.storage!.getCheckpoint();
164
- const map = new Map<string, string>([[bucket, start]]);
182
+ const map = new Map<string, InternalOpId>([[bucket, start]]);
165
183
  const batch = this.storage!.getBucketDataBatch(checkpoint, map);
166
184
  const batches = await test_utils.fromAsync(batch);
167
185
  return batches[0]?.batch.data ?? [];