@powersync/service-core 0.0.0-dev-20241015210820 → 0.0.0-dev-20241016113053
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 +5 -5
- package/dist/api/RouteAPI.d.ts +4 -6
- package/dist/api/diagnostics.js +1 -3
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/api/schema.js +2 -2
- package/dist/api/schema.js.map +1 -1
- package/dist/replication/AbstractReplicationJob.js +2 -2
- package/dist/replication/AbstractReplicationJob.js.map +1 -1
- package/dist/replication/ReplicationModule.js +0 -3
- package/dist/replication/ReplicationModule.js.map +1 -1
- package/dist/routes/configure-fastify.js +12 -12
- package/dist/routes/configure-fastify.js.map +1 -1
- package/dist/routes/configure-rsocket.js +1 -4
- package/dist/routes/configure-rsocket.js.map +1 -1
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/router.d.ts +1 -8
- package/dist/routes/router.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +16 -16
- package/dist/storage/BucketStorage.js +0 -6
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +9 -1
- package/dist/storage/MongoBucketStorage.js +21 -5
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoBucketBatch.js +0 -1
- package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +4 -2
- package/dist/storage/mongo/MongoSyncBucketStorage.js +2 -3
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +3 -1
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js +12 -4
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/write-checkpoint.d.ts +1 -0
- package/dist/storage/write-checkpoint.js.map +1 -1
- package/dist/util/config/compound-config-collector.js +2 -1
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/types.d.ts +1 -0
- package/dist/util/protocol-types.d.ts +1 -2
- package/package.json +5 -5
- package/src/api/RouteAPI.ts +4 -7
- package/src/api/diagnostics.ts +1 -3
- package/src/api/schema.ts +3 -3
- package/src/replication/AbstractReplicationJob.ts +2 -2
- package/src/replication/ReplicationModule.ts +0 -4
- package/src/routes/configure-fastify.ts +17 -16
- package/src/routes/configure-rsocket.ts +2 -7
- package/src/routes/endpoints/admin.ts +2 -2
- package/src/routes/endpoints/sync-rules.ts +0 -1
- package/src/routes/router.ts +1 -7
- package/src/storage/BucketStorage.ts +16 -17
- package/src/storage/MongoBucketStorage.ts +28 -6
- package/src/storage/mongo/MongoBucketBatch.ts +0 -1
- package/src/storage/mongo/MongoSyncBucketStorage.ts +3 -5
- package/src/storage/mongo/MongoWriteCheckpointAPI.ts +15 -8
- package/src/storage/write-checkpoint.ts +2 -0
- package/src/util/config/compound-config-collector.ts +2 -1
- package/src/util/config/types.ts +1 -0
- package/src/util/protocol-types.ts +1 -1
- package/test/src/compacting.test.ts +15 -13
- package/test/src/data_storage.test.ts +56 -56
- package/test/src/sync.test.ts +9 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -90,6 +90,11 @@ export interface BucketStorageFactory
|
|
|
90
90
|
*/
|
|
91
91
|
getActiveCheckpoint(): Promise<ActiveCheckpoint>;
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Yields the latest sync checkpoint.
|
|
95
|
+
*/
|
|
96
|
+
watchActiveCheckpoint(signal: AbortSignal): AsyncIterable<ActiveCheckpoint>;
|
|
97
|
+
|
|
93
98
|
/**
|
|
94
99
|
* Yields the latest user write checkpoint whenever the sync checkpoint updates.
|
|
95
100
|
*/
|
|
@@ -106,20 +111,20 @@ export interface BucketStorageFactory
|
|
|
106
111
|
getPowerSyncInstanceId(): Promise<string>;
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
export interface
|
|
114
|
+
export interface WriteCheckpoint {
|
|
115
|
+
base: ActiveCheckpoint;
|
|
116
|
+
writeCheckpoint: bigint | null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface ActiveCheckpoint {
|
|
110
120
|
readonly checkpoint: util.OpId;
|
|
111
121
|
readonly lsn: string | null;
|
|
112
|
-
}
|
|
113
122
|
|
|
114
|
-
export interface ActiveCheckpoint extends Checkpoint {
|
|
115
123
|
hasSyncRules(): boolean;
|
|
116
124
|
|
|
117
125
|
getBucketStorage(): Promise<SyncRulesBucketStorage | null>;
|
|
118
|
-
}
|
|
119
126
|
|
|
120
|
-
|
|
121
|
-
base: ActiveCheckpoint;
|
|
122
|
-
writeCheckpoint: bigint | null;
|
|
127
|
+
syncRules: PersistedSyncRules | null;
|
|
123
128
|
}
|
|
124
129
|
|
|
125
130
|
export interface StorageMetrics {
|
|
@@ -221,7 +226,7 @@ export interface SyncRulesBucketStorage extends DisposableObserverClient<SyncRul
|
|
|
221
226
|
callback: (batch: BucketStorageBatch) => Promise<void>
|
|
222
227
|
): Promise<FlushedResult | null>;
|
|
223
228
|
|
|
224
|
-
getCheckpoint(): Promise<
|
|
229
|
+
getCheckpoint(): Promise<{ checkpoint: util.OpId }>;
|
|
225
230
|
|
|
226
231
|
getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules;
|
|
227
232
|
|
|
@@ -389,14 +394,8 @@ export type SaveOp = 'insert' | 'update' | 'delete';
|
|
|
389
394
|
|
|
390
395
|
export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete;
|
|
391
396
|
|
|
392
|
-
export enum SaveOperationTag {
|
|
393
|
-
INSERT = 'insert',
|
|
394
|
-
UPDATE = 'update',
|
|
395
|
-
DELETE = 'delete'
|
|
396
|
-
}
|
|
397
|
-
|
|
398
397
|
export interface SaveInsert {
|
|
399
|
-
tag:
|
|
398
|
+
tag: 'insert';
|
|
400
399
|
sourceTable: SourceTable;
|
|
401
400
|
before?: undefined;
|
|
402
401
|
beforeReplicaId?: undefined;
|
|
@@ -405,7 +404,7 @@ export interface SaveInsert {
|
|
|
405
404
|
}
|
|
406
405
|
|
|
407
406
|
export interface SaveUpdate {
|
|
408
|
-
tag:
|
|
407
|
+
tag: 'update';
|
|
409
408
|
sourceTable: SourceTable;
|
|
410
409
|
|
|
411
410
|
/**
|
|
@@ -424,7 +423,7 @@ export interface SaveUpdate {
|
|
|
424
423
|
}
|
|
425
424
|
|
|
426
425
|
export interface SaveDelete {
|
|
427
|
-
tag:
|
|
426
|
+
tag: 'delete';
|
|
428
427
|
sourceTable: SourceTable;
|
|
429
428
|
before?: SqliteRow;
|
|
430
429
|
beforeReplicaId: ReplicaId;
|
|
@@ -78,6 +78,9 @@ export class MongoBucketStorage
|
|
|
78
78
|
db: PowerSyncMongo,
|
|
79
79
|
options: {
|
|
80
80
|
slot_name_prefix: string;
|
|
81
|
+
/**
|
|
82
|
+
* Initial Write Checkpoint Mode
|
|
83
|
+
*/
|
|
81
84
|
write_checkpoint_mode?: WriteCheckpointMode;
|
|
82
85
|
}
|
|
83
86
|
) {
|
|
@@ -303,6 +306,10 @@ export class MongoBucketStorage
|
|
|
303
306
|
return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints(checkpoints);
|
|
304
307
|
}
|
|
305
308
|
|
|
309
|
+
setWriteCheckpointMode(mode: WriteCheckpointMode): void {
|
|
310
|
+
return this.writeCheckpointAPI.setWriteCheckpointMode(mode);
|
|
311
|
+
}
|
|
312
|
+
|
|
306
313
|
async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise<bigint> {
|
|
307
314
|
return this.writeCheckpointAPI.createCustomWriteCheckpoint(options);
|
|
308
315
|
}
|
|
@@ -408,14 +415,19 @@ export class MongoBucketStorage
|
|
|
408
415
|
return null;
|
|
409
416
|
}
|
|
410
417
|
return (await this.storageCache.fetch(doc._id)) ?? null;
|
|
411
|
-
}
|
|
412
|
-
|
|
418
|
+
},
|
|
419
|
+
syncRules: doc
|
|
420
|
+
? new MongoPersistedSyncRulesContent(this.db, doc).parsed({
|
|
421
|
+
defaultSchema: ''
|
|
422
|
+
})
|
|
423
|
+
: null
|
|
424
|
+
} satisfies ActiveCheckpoint;
|
|
413
425
|
}
|
|
414
426
|
|
|
415
427
|
/**
|
|
416
428
|
* Instance-wide watch on the latest available checkpoint (op_id + lsn).
|
|
417
429
|
*/
|
|
418
|
-
private async *
|
|
430
|
+
private async *_watchActiveCheckpoint(signal: AbortSignal): AsyncIterable<ActiveCheckpoint> {
|
|
419
431
|
const pipeline: mongo.Document[] = [
|
|
420
432
|
{
|
|
421
433
|
$match: {
|
|
@@ -428,7 +440,8 @@ export class MongoBucketStorage
|
|
|
428
440
|
operationType: 1,
|
|
429
441
|
'fullDocument._id': 1,
|
|
430
442
|
'fullDocument.last_checkpoint': 1,
|
|
431
|
-
'fullDocument.last_checkpoint_lsn': 1
|
|
443
|
+
'fullDocument.last_checkpoint_lsn': 1,
|
|
444
|
+
'fullDocument.content': 1
|
|
432
445
|
}
|
|
433
446
|
}
|
|
434
447
|
];
|
|
@@ -450,7 +463,8 @@ export class MongoBucketStorage
|
|
|
450
463
|
projection: {
|
|
451
464
|
_id: 1,
|
|
452
465
|
last_checkpoint: 1,
|
|
453
|
-
last_checkpoint_lsn: 1
|
|
466
|
+
last_checkpoint_lsn: 1,
|
|
467
|
+
content: 1
|
|
454
468
|
}
|
|
455
469
|
}
|
|
456
470
|
);
|
|
@@ -499,6 +513,7 @@ export class MongoBucketStorage
|
|
|
499
513
|
if (doc == null) {
|
|
500
514
|
continue;
|
|
501
515
|
}
|
|
516
|
+
|
|
502
517
|
const op = this.makeActiveCheckpoint(doc);
|
|
503
518
|
// Check for LSN / checkpoint changes - ignore other metadata changes
|
|
504
519
|
if (lastOp == null || op.lsn != lastOp.lsn || op.checkpoint != lastOp.checkpoint) {
|
|
@@ -510,9 +525,16 @@ export class MongoBucketStorage
|
|
|
510
525
|
|
|
511
526
|
// Nothing is done here until a subscriber starts to iterate
|
|
512
527
|
private readonly sharedIter = new sync.BroadcastIterable((signal) => {
|
|
513
|
-
return this.
|
|
528
|
+
return this._watchActiveCheckpoint(signal);
|
|
514
529
|
});
|
|
515
530
|
|
|
531
|
+
/**
|
|
532
|
+
* Watch changes to the active sync rules and checkpoint.
|
|
533
|
+
*/
|
|
534
|
+
watchActiveCheckpoint(signal: AbortSignal): AsyncIterable<ActiveCheckpoint> {
|
|
535
|
+
return wrapWithAbort(this.sharedIter, signal);
|
|
536
|
+
}
|
|
537
|
+
|
|
516
538
|
/**
|
|
517
539
|
* User-specific watch on the latest checkpoint and/or write checkpoint.
|
|
518
540
|
*/
|
|
@@ -81,7 +81,6 @@ export class MongoBucketBatch extends DisposableObserver<BucketBatchStorageListe
|
|
|
81
81
|
this.session = this.client.startSession();
|
|
82
82
|
this.slot_name = slot_name;
|
|
83
83
|
this.sync_rules = sync_rules;
|
|
84
|
-
this.batch = new OperationBatch();
|
|
85
84
|
}
|
|
86
85
|
|
|
87
86
|
addCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): void {
|
|
@@ -8,7 +8,6 @@ import * as util from '../../util/util-index.js';
|
|
|
8
8
|
import {
|
|
9
9
|
BucketDataBatchOptions,
|
|
10
10
|
BucketStorageBatch,
|
|
11
|
-
Checkpoint,
|
|
12
11
|
CompactOptions,
|
|
13
12
|
DEFAULT_DOCUMENT_BATCH_LIMIT,
|
|
14
13
|
DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES,
|
|
@@ -61,16 +60,15 @@ export class MongoSyncBucketStorage
|
|
|
61
60
|
return this.parsedSyncRulesCache;
|
|
62
61
|
}
|
|
63
62
|
|
|
64
|
-
async getCheckpoint()
|
|
63
|
+
async getCheckpoint() {
|
|
65
64
|
const doc = await this.db.sync_rules.findOne(
|
|
66
65
|
{ _id: this.group_id },
|
|
67
66
|
{
|
|
68
|
-
projection: { last_checkpoint: 1
|
|
67
|
+
projection: { last_checkpoint: 1 }
|
|
69
68
|
}
|
|
70
69
|
);
|
|
71
70
|
return {
|
|
72
|
-
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n)
|
|
73
|
-
lsn: doc?.last_checkpoint_lsn ?? null
|
|
71
|
+
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n)
|
|
74
72
|
};
|
|
75
73
|
}
|
|
76
74
|
|
|
@@ -17,11 +17,19 @@ export type MongoCheckpointAPIOptions = {
|
|
|
17
17
|
|
|
18
18
|
export class MongoWriteCheckpointAPI implements WriteCheckpointAPI {
|
|
19
19
|
readonly db: PowerSyncMongo;
|
|
20
|
-
|
|
20
|
+
private _mode: WriteCheckpointMode;
|
|
21
21
|
|
|
22
22
|
constructor(options: MongoCheckpointAPIOptions) {
|
|
23
23
|
this.db = options.db;
|
|
24
|
-
this.
|
|
24
|
+
this._mode = options.mode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get mode() {
|
|
28
|
+
return this._mode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
setWriteCheckpointMode(mode: WriteCheckpointMode): void {
|
|
32
|
+
this._mode = mode;
|
|
25
33
|
}
|
|
26
34
|
|
|
27
35
|
async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise<void> {
|
|
@@ -29,12 +37,11 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI {
|
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise<bigint> {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Allow creating custom checkpoints even if the current mode is not `custom`.
|
|
42
|
+
* There might be a state where the next sync rules rely on replicating custom
|
|
43
|
+
* write checkpoints, but the current active sync rules uses managed checkpoints.
|
|
44
|
+
*/
|
|
38
45
|
const { checkpoint, user_id, sync_rules_id } = options;
|
|
39
46
|
const doc = await this.db.custom_write_checkpoints.findOneAndUpdate(
|
|
40
47
|
{
|
|
@@ -55,6 +55,8 @@ export type ManagedWriteCheckpointOptions = ManagedWriteCheckpointFilters;
|
|
|
55
55
|
export type LastWriteCheckpointFilters = CustomWriteCheckpointFilters | ManagedWriteCheckpointFilters;
|
|
56
56
|
|
|
57
57
|
export interface WriteCheckpointAPI {
|
|
58
|
+
setWriteCheckpointMode(mode: WriteCheckpointMode): void;
|
|
59
|
+
|
|
58
60
|
batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise<void>;
|
|
59
61
|
|
|
60
62
|
createCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): Promise<bigint>;
|
|
@@ -122,7 +122,8 @@ export class CompoundConfigCollector {
|
|
|
122
122
|
},
|
|
123
123
|
// TODO maybe move this out of the connection or something
|
|
124
124
|
// slot_name_prefix: connections[0]?.slot_name_prefix ?? 'powersync_'
|
|
125
|
-
slot_name_prefix: 'powersync_'
|
|
125
|
+
slot_name_prefix: 'powersync_',
|
|
126
|
+
parameters: baseConfig.parameters ?? {}
|
|
126
127
|
};
|
|
127
128
|
|
|
128
129
|
return config;
|
package/src/util/config/types.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { SaveOperationTag } from '@/storage/BucketStorage.js';
|
|
2
1
|
import { MongoCompactOptions } from '@/storage/mongo/MongoCompactor.js';
|
|
2
|
+
import { SqlSyncRules } from '@powersync/service-sync-rules';
|
|
3
3
|
import { describe, expect, test } from 'vitest';
|
|
4
4
|
import { validateCompactedBucket } from './bucket_validation.js';
|
|
5
5
|
import { oneFromAsync } from './stream_utils.js';
|
|
6
|
-
import { BATCH_OPTIONS, makeTestTable, MONGO_STORAGE_FACTORY, rid, testRules } from './util.js';
|
|
6
|
+
import { BATCH_OPTIONS, makeTestTable, MONGO_STORAGE_FACTORY, rid, testRules, ZERO_LSN } from './util.js';
|
|
7
|
+
import { ParseSyncRulesOptions, PersistedSyncRulesContent, StartBatchOptions } from '@/storage/BucketStorage.js';
|
|
8
|
+
import { getUuidReplicaIdentityBson } from '@/util/util-index.js';
|
|
7
9
|
|
|
8
10
|
const TEST_TABLE = makeTestTable('test', ['id']);
|
|
9
11
|
|
|
@@ -29,7 +31,7 @@ bucket_definitions:
|
|
|
29
31
|
const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
|
30
32
|
await batch.save({
|
|
31
33
|
sourceTable: TEST_TABLE,
|
|
32
|
-
tag:
|
|
34
|
+
tag: 'insert',
|
|
33
35
|
after: {
|
|
34
36
|
id: 't1'
|
|
35
37
|
},
|
|
@@ -38,7 +40,7 @@ bucket_definitions:
|
|
|
38
40
|
|
|
39
41
|
await batch.save({
|
|
40
42
|
sourceTable: TEST_TABLE,
|
|
41
|
-
tag:
|
|
43
|
+
tag: 'insert',
|
|
42
44
|
after: {
|
|
43
45
|
id: 't2'
|
|
44
46
|
},
|
|
@@ -47,7 +49,7 @@ bucket_definitions:
|
|
|
47
49
|
|
|
48
50
|
await batch.save({
|
|
49
51
|
sourceTable: TEST_TABLE,
|
|
50
|
-
tag:
|
|
52
|
+
tag: 'update',
|
|
51
53
|
after: {
|
|
52
54
|
id: 't2'
|
|
53
55
|
},
|
|
@@ -126,7 +128,7 @@ bucket_definitions:
|
|
|
126
128
|
const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
|
127
129
|
await batch.save({
|
|
128
130
|
sourceTable: TEST_TABLE,
|
|
129
|
-
tag:
|
|
131
|
+
tag: 'insert',
|
|
130
132
|
after: {
|
|
131
133
|
id: 't1'
|
|
132
134
|
},
|
|
@@ -135,7 +137,7 @@ bucket_definitions:
|
|
|
135
137
|
|
|
136
138
|
await batch.save({
|
|
137
139
|
sourceTable: TEST_TABLE,
|
|
138
|
-
tag:
|
|
140
|
+
tag: 'insert',
|
|
139
141
|
after: {
|
|
140
142
|
id: 't2'
|
|
141
143
|
},
|
|
@@ -144,7 +146,7 @@ bucket_definitions:
|
|
|
144
146
|
|
|
145
147
|
await batch.save({
|
|
146
148
|
sourceTable: TEST_TABLE,
|
|
147
|
-
tag:
|
|
149
|
+
tag: 'delete',
|
|
148
150
|
before: {
|
|
149
151
|
id: 't1'
|
|
150
152
|
},
|
|
@@ -153,7 +155,7 @@ bucket_definitions:
|
|
|
153
155
|
|
|
154
156
|
await batch.save({
|
|
155
157
|
sourceTable: TEST_TABLE,
|
|
156
|
-
tag:
|
|
158
|
+
tag: 'update',
|
|
157
159
|
after: {
|
|
158
160
|
id: 't2'
|
|
159
161
|
},
|
|
@@ -231,7 +233,7 @@ bucket_definitions:
|
|
|
231
233
|
const result = await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
|
232
234
|
await batch.save({
|
|
233
235
|
sourceTable: TEST_TABLE,
|
|
234
|
-
tag:
|
|
236
|
+
tag: 'insert',
|
|
235
237
|
after: {
|
|
236
238
|
id: 't1'
|
|
237
239
|
},
|
|
@@ -240,7 +242,7 @@ bucket_definitions:
|
|
|
240
242
|
|
|
241
243
|
await batch.save({
|
|
242
244
|
sourceTable: TEST_TABLE,
|
|
243
|
-
tag:
|
|
245
|
+
tag: 'insert',
|
|
244
246
|
after: {
|
|
245
247
|
id: 't2'
|
|
246
248
|
},
|
|
@@ -249,7 +251,7 @@ bucket_definitions:
|
|
|
249
251
|
|
|
250
252
|
await batch.save({
|
|
251
253
|
sourceTable: TEST_TABLE,
|
|
252
|
-
tag:
|
|
254
|
+
tag: 'delete',
|
|
253
255
|
before: {
|
|
254
256
|
id: 't1'
|
|
255
257
|
},
|
|
@@ -263,7 +265,7 @@ bucket_definitions:
|
|
|
263
265
|
const result2 = await storage.startBatch(BATCH_OPTIONS, async (batch) => {
|
|
264
266
|
await batch.save({
|
|
265
267
|
sourceTable: TEST_TABLE,
|
|
266
|
-
tag:
|
|
268
|
+
tag: 'delete',
|
|
267
269
|
before: {
|
|
268
270
|
id: 't2'
|
|
269
271
|
},
|