@powersync/service-module-mongodb 0.19.0 → 0.20.1
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 +39 -0
- package/dist/module/MongoModule.js +2 -1
- package/dist/module/MongoModule.js.map +1 -1
- package/dist/replication/ChangeStream.d.ts +8 -0
- package/dist/replication/ChangeStream.js +69 -10
- package/dist/replication/ChangeStream.js.map +1 -1
- package/dist/replication/ChangeStreamReplicationJob.js +2 -1
- package/dist/replication/ChangeStreamReplicationJob.js.map +1 -1
- package/dist/replication/MongoSnapshotter.js +5 -0
- package/dist/replication/MongoSnapshotter.js.map +1 -1
- package/dist/replication/RawChangeStream.js +1 -11
- package/dist/replication/RawChangeStream.js.map +1 -1
- package/dist/replication/checkpoints/CheckpointImplementation.d.ts +4 -1
- package/dist/replication/checkpoints/SentinelCheckpointImplementation.d.ts +4 -1
- package/dist/replication/checkpoints/SentinelCheckpointImplementation.js +10 -8
- package/dist/replication/checkpoints/SentinelCheckpointImplementation.js.map +1 -1
- package/dist/replication/checkpoints/TimestampCheckpointImplementation.d.ts +4 -1
- package/dist/replication/checkpoints/TimestampCheckpointImplementation.js +23 -20
- package/dist/replication/checkpoints/TimestampCheckpointImplementation.js.map +1 -1
- package/dist/types/types.d.ts +5 -0
- package/dist/types/types.js +20 -2
- package/dist/types/types.js.map +1 -1
- package/package.json +9 -9
- package/src/module/MongoModule.ts +2 -1
- package/src/replication/ChangeStream.ts +73 -12
- package/src/replication/ChangeStreamReplicationJob.ts +2 -1
- package/src/replication/MongoSnapshotter.ts +10 -0
- package/src/replication/RawChangeStream.ts +1 -15
- package/src/replication/checkpoints/CheckpointImplementation.ts +1 -1
- package/src/replication/checkpoints/SentinelCheckpointImplementation.ts +11 -9
- package/src/replication/checkpoints/TimestampCheckpointImplementation.ts +27 -24
- package/src/types/types.ts +27 -2
- package/test/src/change_stream_utils.ts +16 -4
- package/test/src/config.test.ts +34 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/types/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MongoConnectionParams } from '@powersync/lib-service-mongodb/types';
|
|
2
2
|
import * as lib_mongo from '@powersync/lib-service-mongodb/types';
|
|
3
|
+
import { ErrorCode, ServiceError } from '@powersync/lib-services-framework';
|
|
3
4
|
import * as service_types from '@powersync/service-types';
|
|
4
5
|
import { LookupFunction } from 'node:net';
|
|
5
6
|
import * as t from 'ts-codec';
|
|
@@ -40,6 +41,10 @@ export enum PostImagesOption {
|
|
|
40
41
|
READ_ONLY = 'read_only'
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
const DEFAULT_PING_INTERVAL_SECONDS = 60;
|
|
45
|
+
const MIN_PING_INTERVAL_SECONDS = 5;
|
|
46
|
+
const MAX_PING_INTERVAL_SECONDS = 60;
|
|
47
|
+
|
|
43
48
|
export interface NormalizedMongoConnectionConfig {
|
|
44
49
|
id: string;
|
|
45
50
|
tag: string;
|
|
@@ -54,13 +59,19 @@ export interface NormalizedMongoConnectionConfig {
|
|
|
54
59
|
|
|
55
60
|
postImages: PostImagesOption;
|
|
56
61
|
|
|
62
|
+
heartbeat_interval_seconds: number;
|
|
63
|
+
|
|
57
64
|
connectionParams: MongoConnectionParams;
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
export const MongoConnectionConfig = service_types.configFile.DataSourceConfig.and(lib_mongo.BaseMongoConfig).and(
|
|
61
68
|
t.object({
|
|
62
69
|
// Replication specific settings
|
|
63
|
-
post_images: t.literal('off').or(t.literal('auto_configure')).or(t.literal('read_only')).optional()
|
|
70
|
+
post_images: t.literal('off').or(t.literal('auto_configure')).or(t.literal('read_only')).optional(),
|
|
71
|
+
/**
|
|
72
|
+
* Interval in seconds between source connection heartbeats. Null or omitted defaults to 60 seconds.
|
|
73
|
+
*/
|
|
74
|
+
heartbeat_interval_seconds: t.number.or(t.Null).optional()
|
|
64
75
|
})
|
|
65
76
|
);
|
|
66
77
|
|
|
@@ -87,6 +98,20 @@ export function normalizeConnectionConfig(options: MongoConnectionConfigDecoded)
|
|
|
87
98
|
...base,
|
|
88
99
|
id: options.id ?? 'default',
|
|
89
100
|
tag: options.tag ?? 'default',
|
|
90
|
-
postImages: (options.post_images as PostImagesOption | undefined) ?? PostImagesOption.OFF
|
|
101
|
+
postImages: (options.post_images as PostImagesOption | undefined) ?? PostImagesOption.OFF,
|
|
102
|
+
heartbeat_interval_seconds: normalizeHeartbeatInterval(options.heartbeat_interval_seconds)
|
|
91
103
|
};
|
|
92
104
|
}
|
|
105
|
+
|
|
106
|
+
function normalizeHeartbeatInterval(value: number | null | undefined): number {
|
|
107
|
+
if (value == null) {
|
|
108
|
+
return DEFAULT_PING_INTERVAL_SECONDS;
|
|
109
|
+
}
|
|
110
|
+
if (!Number.isFinite(value) || value < MIN_PING_INTERVAL_SECONDS || value > MAX_PING_INTERVAL_SECONDS) {
|
|
111
|
+
throw new ServiceError(
|
|
112
|
+
ErrorCode.PSYNC_S1109,
|
|
113
|
+
`MongoDB connection: heartbeat_interval_seconds must be between ${MIN_PING_INTERVAL_SECONDS} and ${MAX_PING_INTERVAL_SECONDS} seconds; null or omitted defaults to ${DEFAULT_PING_INTERVAL_SECONDS}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
createCoreReplicationMetrics,
|
|
6
6
|
initializeCoreReplicationMetrics,
|
|
7
7
|
InternalOpId,
|
|
8
|
+
isBatchEnd,
|
|
8
9
|
LEGACY_STORAGE_VERSION,
|
|
9
10
|
OplogEntry,
|
|
10
11
|
ProtocolOpId,
|
|
@@ -273,12 +274,23 @@ export class ChangeStreamTestContext {
|
|
|
273
274
|
while (true) {
|
|
274
275
|
const batch = this.storage!.getBucketDataBatch(checkpoint, map);
|
|
275
276
|
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
if (batches.length == 0 || !batches[0]!.chunkData.has_more) {
|
|
277
|
+
const chunks = await test_utils.fromAsync(batch);
|
|
278
|
+
if (chunks.length == 0) {
|
|
279
279
|
break;
|
|
280
280
|
}
|
|
281
|
-
|
|
281
|
+
for (let chunk of chunks) {
|
|
282
|
+
if (isBatchEnd(chunk)) {
|
|
283
|
+
if (!chunk.hasMore) {
|
|
284
|
+
return data;
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
data = data.concat(chunk.chunkData.data ?? []);
|
|
288
|
+
map = [bucketRequest(syncConfigContent, bucket, BigInt(chunk.chunkData.next_after))];
|
|
289
|
+
if (!chunk.chunkData.has_more) {
|
|
290
|
+
return data;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
282
294
|
}
|
|
283
295
|
return data;
|
|
284
296
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { normalizeConnectionConfig } from '@module/types/types.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
const BASE_CONFIG = {
|
|
5
|
+
type: 'mongodb' as const,
|
|
6
|
+
uri: 'mongodb://localhost:27017/powersync_test'
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
describe('MongoDB connection config', () => {
|
|
10
|
+
test('defaults heartbeat_interval_seconds to 60 seconds', () => {
|
|
11
|
+
expect(normalizeConnectionConfig(BASE_CONFIG).heartbeat_interval_seconds).toBe(60);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test('defaults a null heartbeat_interval_seconds to 60 seconds', () => {
|
|
15
|
+
expect(
|
|
16
|
+
normalizeConnectionConfig({ ...BASE_CONFIG, heartbeat_interval_seconds: null }).heartbeat_interval_seconds
|
|
17
|
+
).toBe(60);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('allows the MongoDB maximum heartbeat interval', () => {
|
|
21
|
+
expect(
|
|
22
|
+
normalizeConnectionConfig({ ...BASE_CONFIG, heartbeat_interval_seconds: 60 }).heartbeat_interval_seconds
|
|
23
|
+
).toBe(60);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test.each([0, 4, 61, Number.NaN, Number.POSITIVE_INFINITY])(
|
|
27
|
+
'rejects invalid heartbeat_interval_seconds: %s',
|
|
28
|
+
(heartbeat_interval_seconds) => {
|
|
29
|
+
expect(() => normalizeConnectionConfig({ ...BASE_CONFIG, heartbeat_interval_seconds })).toThrow(
|
|
30
|
+
'heartbeat_interval_seconds'
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
});
|