@powersync/service-core 0.0.0-dev-20241022094219 → 0.0.0-dev-20241022111143
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 +6 -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.d.ts +1 -13
- package/dist/replication/ReplicationModule.js +4 -7
- 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/checkpointing.js +82 -14
- package/dist/routes/endpoints/checkpointing.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 +15 -19
- package/dist/storage/BucketStorage.js +0 -6
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +3 -9
- package/dist/storage/MongoBucketStorage.js +4 -21
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/StorageEngine.d.ts +1 -5
- package/dist/storage/StorageEngine.js +1 -19
- package/dist/storage/StorageEngine.js.map +1 -1
- package/dist/storage/StorageProvider.d.ts +1 -8
- package/dist/storage/{write-checkpoint.d.ts → WriteCheckpointAPI.d.ts} +29 -10
- package/dist/storage/{write-checkpoint.js → WriteCheckpointAPI.js} +2 -2
- package/dist/storage/WriteCheckpointAPI.js.map +1 -0
- package/dist/storage/mongo/MongoBucketBatch.d.ts +2 -2
- package/dist/storage/mongo/MongoBucketBatch.js +0 -1
- package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
- package/dist/storage/mongo/MongoStorageProvider.js +1 -2
- package/dist/storage/mongo/MongoStorageProvider.js.map +1 -1
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +13 -3
- package/dist/storage/mongo/MongoSyncBucketStorage.js +35 -7
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +4 -2
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js +13 -7
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +1 -1
- package/dist/storage/storage-index.d.ts +2 -2
- package/dist/storage/storage-index.js +2 -2
- package/dist/storage/storage-index.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 +5 -26
- 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/checkpointing.ts +8 -1
- package/src/routes/endpoints/sync-rules.ts +0 -1
- package/src/routes/router.ts +1 -7
- package/src/storage/BucketStorage.ts +16 -23
- package/src/storage/MongoBucketStorage.ts +9 -39
- package/src/storage/StorageEngine.ts +2 -24
- package/src/storage/StorageProvider.ts +1 -9
- package/src/storage/{write-checkpoint.ts → WriteCheckpointAPI.ts} +31 -13
- package/src/storage/mongo/MongoBucketBatch.ts +2 -3
- package/src/storage/mongo/MongoStorageProvider.ts +1 -2
- package/src/storage/mongo/MongoSyncBucketStorage.ts +52 -9
- package/src/storage/mongo/MongoWriteCheckpointAPI.ts +16 -8
- package/src/storage/storage-index.ts +2 -2
- 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
- package/dist/storage/write-checkpoint.js.map +0 -1
|
@@ -137,8 +137,8 @@ export const reprocess = routeDefinition({
|
|
|
137
137
|
connections: [
|
|
138
138
|
{
|
|
139
139
|
// Previously the connection was asserted with `!`
|
|
140
|
-
tag: baseConfig
|
|
141
|
-
id: baseConfig
|
|
140
|
+
tag: baseConfig!.tag!,
|
|
141
|
+
id: baseConfig!.id,
|
|
142
142
|
slot_name: new_rules.slot_name
|
|
143
143
|
}
|
|
144
144
|
]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { logger, router, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import * as t from 'ts-codec';
|
|
3
3
|
|
|
4
|
+
import * as framework from '@powersync/lib-services-framework';
|
|
4
5
|
import * as util from '../../util/util-index.js';
|
|
5
6
|
import { authUser } from '../auth.js';
|
|
6
7
|
import { routeDefinition } from '../router.js';
|
|
@@ -63,7 +64,13 @@ export const writeCheckpoint2 = routeDefinition({
|
|
|
63
64
|
storageEngine: { activeBucketStorage }
|
|
64
65
|
} = service_context;
|
|
65
66
|
|
|
66
|
-
const
|
|
67
|
+
const activeSyncRules = await activeBucketStorage.getActiveSyncRulesContent();
|
|
68
|
+
if (!activeSyncRules) {
|
|
69
|
+
throw new framework.errors.ValidationError(`Cannot create Write Checkpoint since no sync rules are active.`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
using syncBucketStorage = activeBucketStorage.getInstance(activeSyncRules);
|
|
73
|
+
const writeCheckpoint = await syncBucketStorage.createManagedWriteCheckpoint({
|
|
67
74
|
user_id: full_user_id,
|
|
68
75
|
heads: { '1': currentCheckpoint }
|
|
69
76
|
});
|
|
@@ -3,7 +3,6 @@ import { SqlSyncRules, SyncRulesErrors } from '@powersync/service-sync-rules';
|
|
|
3
3
|
import type { FastifyPluginAsync } from 'fastify';
|
|
4
4
|
import * as t from 'ts-codec';
|
|
5
5
|
|
|
6
|
-
import * as system from '../../system/system-index.js';
|
|
7
6
|
import { authApi } from '../auth.js';
|
|
8
7
|
import { routeDefinition } from '../router.js';
|
|
9
8
|
import { RouteAPI } from '../../api/RouteAPI.js';
|
package/src/routes/router.ts
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import { router } from '@powersync/lib-services-framework';
|
|
2
2
|
import * as auth from '../auth/auth-index.js';
|
|
3
3
|
import { ServiceContext } from '../system/ServiceContext.js';
|
|
4
|
-
import { RouterEngine } from './RouterEngine.js';
|
|
5
4
|
|
|
6
|
-
/**
|
|
7
|
-
* The {@link RouterEngine} must be provided for these routes
|
|
8
|
-
*/
|
|
9
|
-
export type RouterServiceContext = ServiceContext & { routerEngine: RouterEngine };
|
|
10
5
|
/**
|
|
11
6
|
* Common context for routes
|
|
12
7
|
*/
|
|
13
8
|
export type Context = {
|
|
14
9
|
user_id?: string;
|
|
15
|
-
|
|
16
|
-
service_context: RouterServiceContext;
|
|
10
|
+
service_context: ServiceContext;
|
|
17
11
|
|
|
18
12
|
token_payload?: auth.JwtPayload;
|
|
19
13
|
token_errors?: string[];
|
|
@@ -12,16 +12,15 @@ import * as util from '../util/util-index.js';
|
|
|
12
12
|
import { ReplicationEventPayload } from './ReplicationEventPayload.js';
|
|
13
13
|
import { SourceEntityDescriptor } from './SourceEntity.js';
|
|
14
14
|
import { SourceTable } from './SourceTable.js';
|
|
15
|
-
import { BatchedCustomWriteCheckpointOptions, ReplicaId
|
|
15
|
+
import { BatchedCustomWriteCheckpointOptions, ReplicaId } from './storage-index.js';
|
|
16
|
+
import { SyncStorageWriteCheckpointAPI } from './WriteCheckpointAPI.js';
|
|
16
17
|
|
|
17
18
|
export interface BucketStorageFactoryListener extends DisposableListener {
|
|
18
19
|
syncStorageCreated: (storage: SyncRulesBucketStorage) => void;
|
|
19
20
|
replicationEvent: (event: ReplicationEventPayload) => void;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export interface BucketStorageFactory
|
|
23
|
-
extends DisposableObserverClient<BucketStorageFactoryListener>,
|
|
24
|
-
WriteCheckpointAPI {
|
|
23
|
+
export interface BucketStorageFactory extends DisposableObserverClient<BucketStorageFactoryListener> {
|
|
25
24
|
/**
|
|
26
25
|
* Update sync rules from configuration, if changed.
|
|
27
26
|
*/
|
|
@@ -106,22 +105,20 @@ export interface BucketStorageFactory
|
|
|
106
105
|
getPowerSyncInstanceId(): Promise<string>;
|
|
107
106
|
}
|
|
108
107
|
|
|
109
|
-
export interface
|
|
108
|
+
export interface WriteCheckpoint {
|
|
109
|
+
base: ActiveCheckpoint;
|
|
110
|
+
writeCheckpoint: bigint | null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface ActiveCheckpoint {
|
|
110
114
|
readonly checkpoint: util.OpId;
|
|
111
115
|
readonly lsn: string | null;
|
|
112
|
-
}
|
|
113
116
|
|
|
114
|
-
export interface ActiveCheckpoint extends Checkpoint {
|
|
115
117
|
hasSyncRules(): boolean;
|
|
116
118
|
|
|
117
119
|
getBucketStorage(): Promise<SyncRulesBucketStorage | null>;
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
export interface WriteCheckpoint {
|
|
121
|
-
base: ActiveCheckpoint;
|
|
122
|
-
writeCheckpoint: bigint | null;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
122
|
export interface StorageMetrics {
|
|
126
123
|
/**
|
|
127
124
|
* Size of operations (bucket_data)
|
|
@@ -208,7 +205,9 @@ export interface SyncRulesBucketStorageListener extends DisposableListener {
|
|
|
208
205
|
batchStarted: (batch: BucketStorageBatch) => void;
|
|
209
206
|
}
|
|
210
207
|
|
|
211
|
-
export interface SyncRulesBucketStorage
|
|
208
|
+
export interface SyncRulesBucketStorage
|
|
209
|
+
extends DisposableObserverClient<SyncRulesBucketStorageListener>,
|
|
210
|
+
SyncStorageWriteCheckpointAPI {
|
|
212
211
|
readonly group_id: number;
|
|
213
212
|
readonly slot_name: string;
|
|
214
213
|
|
|
@@ -221,7 +220,7 @@ export interface SyncRulesBucketStorage extends DisposableObserverClient<SyncRul
|
|
|
221
220
|
callback: (batch: BucketStorageBatch) => Promise<void>
|
|
222
221
|
): Promise<FlushedResult | null>;
|
|
223
222
|
|
|
224
|
-
getCheckpoint(): Promise<
|
|
223
|
+
getCheckpoint(): Promise<{ checkpoint: util.OpId }>;
|
|
225
224
|
|
|
226
225
|
getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules;
|
|
227
226
|
|
|
@@ -387,14 +386,8 @@ export type SaveOp = 'insert' | 'update' | 'delete';
|
|
|
387
386
|
|
|
388
387
|
export type SaveOptions = SaveInsert | SaveUpdate | SaveDelete;
|
|
389
388
|
|
|
390
|
-
export enum SaveOperationTag {
|
|
391
|
-
INSERT = 'insert',
|
|
392
|
-
UPDATE = 'update',
|
|
393
|
-
DELETE = 'delete'
|
|
394
|
-
}
|
|
395
|
-
|
|
396
389
|
export interface SaveInsert {
|
|
397
|
-
tag:
|
|
390
|
+
tag: 'insert';
|
|
398
391
|
sourceTable: SourceTable;
|
|
399
392
|
before?: undefined;
|
|
400
393
|
beforeReplicaId?: undefined;
|
|
@@ -403,7 +396,7 @@ export interface SaveInsert {
|
|
|
403
396
|
}
|
|
404
397
|
|
|
405
398
|
export interface SaveUpdate {
|
|
406
|
-
tag:
|
|
399
|
+
tag: 'update';
|
|
407
400
|
sourceTable: SourceTable;
|
|
408
401
|
|
|
409
402
|
/**
|
|
@@ -422,7 +415,7 @@ export interface SaveUpdate {
|
|
|
422
415
|
}
|
|
423
416
|
|
|
424
417
|
export interface SaveDelete {
|
|
425
|
-
tag:
|
|
418
|
+
tag: 'delete';
|
|
426
419
|
sourceTable: SourceTable;
|
|
427
420
|
before?: SqliteRow;
|
|
428
421
|
beforeReplicaId: ReplicaId;
|
|
@@ -21,20 +21,13 @@ import {
|
|
|
21
21
|
UpdateSyncRulesOptions,
|
|
22
22
|
WriteCheckpoint
|
|
23
23
|
} from './BucketStorage.js';
|
|
24
|
-
import { PowerSyncMongo } from './mongo/db.js';
|
|
24
|
+
import { PowerSyncMongo, PowerSyncMongoOptions } from './mongo/db.js';
|
|
25
25
|
import { SyncRuleDocument, SyncRuleState } from './mongo/models.js';
|
|
26
26
|
import { MongoPersistedSyncRulesContent } from './mongo/MongoPersistedSyncRulesContent.js';
|
|
27
27
|
import { MongoSyncBucketStorage } from './mongo/MongoSyncBucketStorage.js';
|
|
28
|
-
import { MongoWriteCheckpointAPI } from './mongo/MongoWriteCheckpointAPI.js';
|
|
29
28
|
import { generateSlotName } from './mongo/util.js';
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
DEFAULT_WRITE_CHECKPOINT_MODE,
|
|
33
|
-
LastWriteCheckpointFilters,
|
|
34
|
-
ManagedWriteCheckpointOptions,
|
|
35
|
-
WriteCheckpointAPI,
|
|
36
|
-
WriteCheckpointMode
|
|
37
|
-
} from './write-checkpoint.js';
|
|
29
|
+
|
|
30
|
+
export interface MongoBucketStorageOptions extends PowerSyncMongoOptions {}
|
|
38
31
|
|
|
39
32
|
export class MongoBucketStorage
|
|
40
33
|
extends DisposableObserver<BucketStorageFactoryListener>
|
|
@@ -45,10 +38,6 @@ export class MongoBucketStorage
|
|
|
45
38
|
// TODO: This is still Postgres specific and needs to be reworked
|
|
46
39
|
public readonly slot_name_prefix: string;
|
|
47
40
|
|
|
48
|
-
readonly write_checkpoint_mode: WriteCheckpointMode;
|
|
49
|
-
|
|
50
|
-
protected readonly writeCheckpointAPI: WriteCheckpointAPI;
|
|
51
|
-
|
|
52
41
|
private readonly storageCache = new LRUCache<number, MongoSyncBucketStorage>({
|
|
53
42
|
max: 3,
|
|
54
43
|
fetchMethod: async (id) => {
|
|
@@ -76,7 +65,6 @@ export class MongoBucketStorage
|
|
|
76
65
|
db: PowerSyncMongo,
|
|
77
66
|
options: {
|
|
78
67
|
slot_name_prefix: string;
|
|
79
|
-
write_checkpoint_mode?: WriteCheckpointMode;
|
|
80
68
|
}
|
|
81
69
|
) {
|
|
82
70
|
super();
|
|
@@ -84,11 +72,6 @@ export class MongoBucketStorage
|
|
|
84
72
|
this.db = db;
|
|
85
73
|
this.session = this.client.startSession();
|
|
86
74
|
this.slot_name_prefix = options.slot_name_prefix;
|
|
87
|
-
this.write_checkpoint_mode = options.write_checkpoint_mode ?? DEFAULT_WRITE_CHECKPOINT_MODE;
|
|
88
|
-
this.writeCheckpointAPI = new MongoWriteCheckpointAPI({
|
|
89
|
-
db,
|
|
90
|
-
mode: this.write_checkpoint_mode
|
|
91
|
-
});
|
|
92
75
|
}
|
|
93
76
|
|
|
94
77
|
getInstance(options: PersistedSyncRulesContent): MongoSyncBucketStorage {
|
|
@@ -297,22 +280,6 @@ export class MongoBucketStorage
|
|
|
297
280
|
});
|
|
298
281
|
}
|
|
299
282
|
|
|
300
|
-
async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise<void> {
|
|
301
|
-
return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints(checkpoints);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise<bigint> {
|
|
305
|
-
return this.writeCheckpointAPI.createCustomWriteCheckpoint(options);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async createManagedWriteCheckpoint(options: ManagedWriteCheckpointOptions): Promise<bigint> {
|
|
309
|
-
return this.writeCheckpointAPI.createManagedWriteCheckpoint(options);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise<bigint | null> {
|
|
313
|
-
return this.writeCheckpointAPI.lastWriteCheckpoint(filters);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
283
|
async getActiveCheckpoint(): Promise<ActiveCheckpoint> {
|
|
317
284
|
const doc = await this.db.sync_rules.findOne(
|
|
318
285
|
{
|
|
@@ -424,7 +391,7 @@ export class MongoBucketStorage
|
|
|
424
391
|
}
|
|
425
392
|
return (await this.storageCache.fetch(doc._id)) ?? null;
|
|
426
393
|
}
|
|
427
|
-
};
|
|
394
|
+
} satisfies ActiveCheckpoint;
|
|
428
395
|
}
|
|
429
396
|
|
|
430
397
|
/**
|
|
@@ -514,6 +481,7 @@ export class MongoBucketStorage
|
|
|
514
481
|
if (doc == null) {
|
|
515
482
|
continue;
|
|
516
483
|
}
|
|
484
|
+
|
|
517
485
|
const op = this.makeActiveCheckpoint(doc);
|
|
518
486
|
// Check for LSN / checkpoint changes - ignore other metadata changes
|
|
519
487
|
if (lastOp == null || op.lsn != lastOp.lsn || op.checkpoint != lastOp.checkpoint) {
|
|
@@ -544,12 +512,14 @@ export class MongoBucketStorage
|
|
|
544
512
|
// 1. checkpoint (op_id) changes.
|
|
545
513
|
// 2. write checkpoint changes for the specific user
|
|
546
514
|
const bucketStorage = await cp.getBucketStorage();
|
|
515
|
+
if (!bucketStorage) {
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
547
518
|
|
|
548
519
|
const lsnFilters: Record<string, string> = lsn ? { 1: lsn } : {};
|
|
549
520
|
|
|
550
|
-
const currentWriteCheckpoint = await
|
|
521
|
+
const currentWriteCheckpoint = await bucketStorage.lastWriteCheckpoint({
|
|
551
522
|
user_id,
|
|
552
|
-
sync_rules_id: bucketStorage?.group_id,
|
|
553
523
|
heads: {
|
|
554
524
|
...lsnFilters
|
|
555
525
|
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { DisposableListener, DisposableObserver, logger } from '@powersync/lib-services-framework';
|
|
2
2
|
import { ResolvedPowerSyncConfig } from '../util/util-index.js';
|
|
3
3
|
import { BucketStorageFactory } from './BucketStorage.js';
|
|
4
|
-
import { ActiveStorage, BucketStorageProvider
|
|
5
|
-
import { DEFAULT_WRITE_CHECKPOINT_MODE } from './write-checkpoint.js';
|
|
4
|
+
import { ActiveStorage, BucketStorageProvider } from './StorageProvider.js';
|
|
6
5
|
|
|
7
6
|
export type StorageEngineOptions = {
|
|
8
7
|
configuration: ResolvedPowerSyncConfig;
|
|
9
8
|
};
|
|
10
9
|
|
|
11
|
-
export const DEFAULT_STORAGE_SETTINGS: StorageSettings = {
|
|
12
|
-
writeCheckpointMode: DEFAULT_WRITE_CHECKPOINT_MODE
|
|
13
|
-
};
|
|
14
|
-
|
|
15
10
|
export interface StorageEngineListener extends DisposableListener {
|
|
16
11
|
storageActivated: (storage: BucketStorageFactory) => void;
|
|
17
12
|
}
|
|
@@ -20,11 +15,9 @@ export class StorageEngine extends DisposableObserver<StorageEngineListener> {
|
|
|
20
15
|
// TODO: This will need to revisited when we actually support multiple storage providers.
|
|
21
16
|
private storageProviders: Map<string, BucketStorageProvider> = new Map();
|
|
22
17
|
private currentActiveStorage: ActiveStorage | null = null;
|
|
23
|
-
private _activeSettings: StorageSettings;
|
|
24
18
|
|
|
25
19
|
constructor(private options: StorageEngineOptions) {
|
|
26
20
|
super();
|
|
27
|
-
this._activeSettings = DEFAULT_STORAGE_SETTINGS;
|
|
28
21
|
}
|
|
29
22
|
|
|
30
23
|
get activeBucketStorage(): BucketStorageFactory {
|
|
@@ -39,20 +32,6 @@ export class StorageEngine extends DisposableObserver<StorageEngineListener> {
|
|
|
39
32
|
return this.currentActiveStorage;
|
|
40
33
|
}
|
|
41
34
|
|
|
42
|
-
get activeSettings(): StorageSettings {
|
|
43
|
-
return { ...this._activeSettings };
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
updateSettings(settings: Partial<StorageSettings>) {
|
|
47
|
-
if (this.currentActiveStorage) {
|
|
48
|
-
throw new Error(`Storage is already active, settings cannot be modified.`);
|
|
49
|
-
}
|
|
50
|
-
this._activeSettings = {
|
|
51
|
-
...this._activeSettings,
|
|
52
|
-
...settings
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
35
|
/**
|
|
57
36
|
* Register a provider which generates a {@link BucketStorageFactory}
|
|
58
37
|
* given the matching config specified in the loaded {@link ResolvedPowerSyncConfig}
|
|
@@ -65,8 +44,7 @@ export class StorageEngine extends DisposableObserver<StorageEngineListener> {
|
|
|
65
44
|
logger.info('Starting Storage Engine...');
|
|
66
45
|
const { configuration } = this.options;
|
|
67
46
|
this.currentActiveStorage = await this.storageProviders.get(configuration.storage.type)!.getStorage({
|
|
68
|
-
resolvedConfig: configuration
|
|
69
|
-
...this.activeSettings
|
|
47
|
+
resolvedConfig: configuration
|
|
70
48
|
});
|
|
71
49
|
this.iterateListeners((cb) => cb.storageActivated?.(this.activeBucketStorage));
|
|
72
50
|
logger.info(`Successfully activated storage: ${configuration.storage.type}.`);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as util from '../util/util-index.js';
|
|
2
2
|
import { BucketStorageFactory } from './BucketStorage.js';
|
|
3
|
-
import { WriteCheckpointMode } from './write-checkpoint.js';
|
|
4
3
|
|
|
5
4
|
export interface ActiveStorage {
|
|
6
5
|
storage: BucketStorageFactory;
|
|
@@ -12,14 +11,7 @@ export interface ActiveStorage {
|
|
|
12
11
|
tearDown(): Promise<boolean>;
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
* Settings which can be modified by various modules in their initialization.
|
|
17
|
-
*/
|
|
18
|
-
export interface StorageSettings {
|
|
19
|
-
writeCheckpointMode: WriteCheckpointMode;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface GetStorageOptions extends StorageSettings {
|
|
14
|
+
export interface GetStorageOptions {
|
|
23
15
|
// TODO: This should just be the storage config. Update once the slot name prefix coupling has been removed from the storage
|
|
24
16
|
resolvedConfig: util.ResolvedPowerSyncConfig;
|
|
25
17
|
}
|
|
@@ -3,7 +3,7 @@ export enum WriteCheckpointMode {
|
|
|
3
3
|
* Raw mappings of `user_id` to `write_checkpoint`s should
|
|
4
4
|
* be supplied for each set of sync rules.
|
|
5
5
|
*/
|
|
6
|
-
CUSTOM = '
|
|
6
|
+
CUSTOM = 'custom',
|
|
7
7
|
/**
|
|
8
8
|
* Write checkpoints are stored as a mapping of `user_id` plus
|
|
9
9
|
* replication HEAD (lsn in Postgres) to an automatically generated
|
|
@@ -26,19 +26,19 @@ export interface CustomWriteCheckpointFilters extends BaseWriteCheckpointIdentif
|
|
|
26
26
|
sync_rules_id: number;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export interface
|
|
29
|
+
export interface BatchedCustomWriteCheckpointOptions extends BaseWriteCheckpointIdentifier {
|
|
30
30
|
/**
|
|
31
31
|
* A supplied incrementing Write Checkpoint number
|
|
32
32
|
*/
|
|
33
33
|
checkpoint: bigint;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
export interface CustomWriteCheckpointOptions extends BatchedCustomWriteCheckpointOptions {
|
|
37
|
+
/**
|
|
38
|
+
* Sync rules which were active when this checkpoint was created.
|
|
39
|
+
*/
|
|
40
|
+
sync_rules_id: number;
|
|
41
|
+
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
44
|
* Managed Write Checkpoints are a mapping of User ID to replication HEAD
|
|
@@ -52,15 +52,33 @@ export interface ManagedWriteCheckpointFilters extends BaseWriteCheckpointIdenti
|
|
|
52
52
|
|
|
53
53
|
export type ManagedWriteCheckpointOptions = ManagedWriteCheckpointFilters;
|
|
54
54
|
|
|
55
|
+
export type SyncStorageLastWriteCheckpointFilters = BaseWriteCheckpointIdentifier | ManagedWriteCheckpointFilters;
|
|
55
56
|
export type LastWriteCheckpointFilters = CustomWriteCheckpointFilters | ManagedWriteCheckpointFilters;
|
|
56
57
|
|
|
57
|
-
export interface
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
createCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): Promise<bigint>;
|
|
61
|
-
|
|
58
|
+
export interface BaseWriteCheckpointAPI {
|
|
59
|
+
readonly writeCheckpointMode: WriteCheckpointMode;
|
|
60
|
+
setWriteCheckpointMode(mode: WriteCheckpointMode): void;
|
|
62
61
|
createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise<bigint>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Write Checkpoint API to be used in conjunction with a {@link SyncRulesBucketStorage}.
|
|
66
|
+
* This storage corresponds with a set of sync rules. These APIs don't require specifying a
|
|
67
|
+
* sync rules id.
|
|
68
|
+
*/
|
|
69
|
+
export interface SyncStorageWriteCheckpointAPI extends BaseWriteCheckpointAPI {
|
|
70
|
+
batchCreateCustomWriteCheckpoints(checkpoints: BatchedCustomWriteCheckpointOptions[]): Promise<void>;
|
|
71
|
+
createCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): Promise<bigint>;
|
|
72
|
+
lastWriteCheckpoint(filters: SyncStorageLastWriteCheckpointFilters): Promise<bigint | null>;
|
|
73
|
+
}
|
|
63
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Write Checkpoint API which is interfaced directly with the storage layer. This requires
|
|
77
|
+
* sync rules identifiers for custom write checkpoints.
|
|
78
|
+
*/
|
|
79
|
+
export interface WriteCheckpointAPI extends BaseWriteCheckpointAPI {
|
|
80
|
+
batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise<void>;
|
|
81
|
+
createCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): Promise<bigint>;
|
|
64
82
|
lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise<bigint | null>;
|
|
65
83
|
}
|
|
66
84
|
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
SaveOptions
|
|
13
13
|
} from '../BucketStorage.js';
|
|
14
14
|
import { SourceTable } from '../SourceTable.js';
|
|
15
|
-
import { CustomWriteCheckpointOptions } from '../
|
|
15
|
+
import { BatchedCustomWriteCheckpointOptions, CustomWriteCheckpointOptions } from '../WriteCheckpointAPI.js';
|
|
16
16
|
import { PowerSyncMongo } from './db.js';
|
|
17
17
|
import { CurrentBucket, CurrentDataDocument, SourceKey, SyncRuleDocument } from './models.js';
|
|
18
18
|
import { MongoIdSequence } from './MongoIdSequence.js';
|
|
@@ -81,10 +81,9 @@ 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
|
-
addCustomWriteCheckpoint(checkpoint:
|
|
86
|
+
addCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): void {
|
|
88
87
|
this.write_checkpoint_batch.push({
|
|
89
88
|
...checkpoint,
|
|
90
89
|
sync_rules_id: this.group_id
|
|
@@ -19,8 +19,7 @@ export class MongoStorageProvider implements BucketStorageProvider {
|
|
|
19
19
|
return {
|
|
20
20
|
storage: new MongoBucketStorage(database, {
|
|
21
21
|
// TODO currently need the entire resolved config due to this
|
|
22
|
-
slot_name_prefix: resolvedConfig.slot_name_prefix
|
|
23
|
-
write_checkpoint_mode: options.writeCheckpointMode
|
|
22
|
+
slot_name_prefix: resolvedConfig.slot_name_prefix
|
|
24
23
|
}),
|
|
25
24
|
shutDown: () => client.close(),
|
|
26
25
|
tearDown: () => {
|
|
@@ -2,13 +2,13 @@ import { SqliteJsonRow, SqliteJsonValue, SqlSyncRules } from '@powersync/service
|
|
|
2
2
|
import * as bson from 'bson';
|
|
3
3
|
import * as mongo from 'mongodb';
|
|
4
4
|
|
|
5
|
-
import { DisposableObserver } from '@powersync/lib-services-framework';
|
|
5
|
+
import { DisposableObserver, logger } from '@powersync/lib-services-framework';
|
|
6
|
+
import * as timers from 'timers/promises';
|
|
6
7
|
import * as db from '../../db/db-index.js';
|
|
7
8
|
import * as util from '../../util/util-index.js';
|
|
8
9
|
import {
|
|
9
10
|
BucketDataBatchOptions,
|
|
10
11
|
BucketStorageBatch,
|
|
11
|
-
Checkpoint,
|
|
12
12
|
CompactOptions,
|
|
13
13
|
DEFAULT_DOCUMENT_BATCH_LIMIT,
|
|
14
14
|
DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES,
|
|
@@ -27,13 +27,19 @@ import {
|
|
|
27
27
|
import { ChecksumCache, FetchPartialBucketChecksum, PartialChecksum, PartialChecksumMap } from '../ChecksumCache.js';
|
|
28
28
|
import { MongoBucketStorage } from '../MongoBucketStorage.js';
|
|
29
29
|
import { SourceTable } from '../SourceTable.js';
|
|
30
|
+
import {
|
|
31
|
+
BatchedCustomWriteCheckpointOptions,
|
|
32
|
+
ManagedWriteCheckpointOptions,
|
|
33
|
+
SyncStorageLastWriteCheckpointFilters,
|
|
34
|
+
WriteCheckpointAPI,
|
|
35
|
+
WriteCheckpointMode
|
|
36
|
+
} from '../WriteCheckpointAPI.js';
|
|
30
37
|
import { PowerSyncMongo } from './db.js';
|
|
31
38
|
import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js';
|
|
32
39
|
import { MongoBucketBatch } from './MongoBucketBatch.js';
|
|
33
40
|
import { MongoCompactor } from './MongoCompactor.js';
|
|
41
|
+
import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
|
|
34
42
|
import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js';
|
|
35
|
-
import { logger } from '@powersync/lib-services-framework';
|
|
36
|
-
import * as timers from 'timers/promises';
|
|
37
43
|
|
|
38
44
|
export class MongoSyncBucketStorage
|
|
39
45
|
extends DisposableObserver<SyncRulesBucketStorageListener>
|
|
@@ -47,15 +53,53 @@ export class MongoSyncBucketStorage
|
|
|
47
53
|
});
|
|
48
54
|
|
|
49
55
|
private parsedSyncRulesCache: SqlSyncRules | undefined;
|
|
56
|
+
private writeCheckpointAPI: WriteCheckpointAPI;
|
|
50
57
|
|
|
51
58
|
constructor(
|
|
52
59
|
public readonly factory: MongoBucketStorage,
|
|
53
60
|
public readonly group_id: number,
|
|
54
61
|
private readonly sync_rules: PersistedSyncRulesContent,
|
|
55
|
-
public readonly slot_name: string
|
|
62
|
+
public readonly slot_name: string,
|
|
63
|
+
writeCheckpointMode: WriteCheckpointMode = WriteCheckpointMode.MANAGED
|
|
56
64
|
) {
|
|
57
65
|
super();
|
|
58
66
|
this.db = factory.db;
|
|
67
|
+
this.writeCheckpointAPI = new MongoWriteCheckpointAPI({
|
|
68
|
+
db: this.db,
|
|
69
|
+
mode: writeCheckpointMode
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get writeCheckpointMode() {
|
|
74
|
+
return this.writeCheckpointAPI.writeCheckpointMode;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setWriteCheckpointMode(mode: WriteCheckpointMode): void {
|
|
78
|
+
this.writeCheckpointAPI.setWriteCheckpointMode(mode);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
batchCreateCustomWriteCheckpoints(checkpoints: BatchedCustomWriteCheckpointOptions[]): Promise<void> {
|
|
82
|
+
return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints(
|
|
83
|
+
checkpoints.map((checkpoint) => ({ ...checkpoint, sync_rules_id: this.group_id }))
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
createCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): Promise<bigint> {
|
|
88
|
+
return this.writeCheckpointAPI.createCustomWriteCheckpoint({
|
|
89
|
+
...checkpoint,
|
|
90
|
+
sync_rules_id: this.group_id
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise<bigint> {
|
|
95
|
+
return this.writeCheckpointAPI.createManagedWriteCheckpoint(checkpoint);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
lastWriteCheckpoint(filters: SyncStorageLastWriteCheckpointFilters): Promise<bigint | null> {
|
|
99
|
+
return this.writeCheckpointAPI.lastWriteCheckpoint({
|
|
100
|
+
...filters,
|
|
101
|
+
sync_rules_id: this.group_id
|
|
102
|
+
});
|
|
59
103
|
}
|
|
60
104
|
|
|
61
105
|
getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules {
|
|
@@ -63,16 +107,15 @@ export class MongoSyncBucketStorage
|
|
|
63
107
|
return this.parsedSyncRulesCache;
|
|
64
108
|
}
|
|
65
109
|
|
|
66
|
-
async getCheckpoint()
|
|
110
|
+
async getCheckpoint() {
|
|
67
111
|
const doc = await this.db.sync_rules.findOne(
|
|
68
112
|
{ _id: this.group_id },
|
|
69
113
|
{
|
|
70
|
-
projection: { last_checkpoint: 1
|
|
114
|
+
projection: { last_checkpoint: 1 }
|
|
71
115
|
}
|
|
72
116
|
);
|
|
73
117
|
return {
|
|
74
|
-
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n)
|
|
75
|
-
lsn: doc?.last_checkpoint_lsn ?? null
|
|
118
|
+
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n)
|
|
76
119
|
};
|
|
77
120
|
}
|
|
78
121
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ManagedWriteCheckpointOptions,
|
|
8
8
|
WriteCheckpointAPI,
|
|
9
9
|
WriteCheckpointMode
|
|
10
|
-
} from '../
|
|
10
|
+
} from '../WriteCheckpointAPI.js';
|
|
11
11
|
import { PowerSyncMongo } from './db.js';
|
|
12
12
|
|
|
13
13
|
export type MongoCheckpointAPIOptions = {
|
|
@@ -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 writeCheckpointMode() {
|
|
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,9 +37,9 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI {
|
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise<bigint> {
|
|
32
|
-
if (this.
|
|
40
|
+
if (this.writeCheckpointMode !== WriteCheckpointMode.CUSTOM) {
|
|
33
41
|
throw new framework.errors.ValidationError(
|
|
34
|
-
`Creating a custom Write Checkpoint when the current Write Checkpoint mode is set to "${this.
|
|
42
|
+
`Creating a custom Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"`
|
|
35
43
|
);
|
|
36
44
|
}
|
|
37
45
|
|
|
@@ -52,9 +60,9 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI {
|
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
async createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise<bigint> {
|
|
55
|
-
if (this.
|
|
63
|
+
if (this.writeCheckpointMode !== WriteCheckpointMode.MANAGED) {
|
|
56
64
|
throw new framework.errors.ValidationError(
|
|
57
|
-
`
|
|
65
|
+
`Attempting to create a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"`
|
|
58
66
|
);
|
|
59
67
|
}
|
|
60
68
|
|
|
@@ -77,7 +85,7 @@ export class MongoWriteCheckpointAPI implements WriteCheckpointAPI {
|
|
|
77
85
|
}
|
|
78
86
|
|
|
79
87
|
async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise<bigint | null> {
|
|
80
|
-
switch (this.
|
|
88
|
+
switch (this.writeCheckpointMode) {
|
|
81
89
|
case WriteCheckpointMode.CUSTOM:
|
|
82
90
|
if (false == 'sync_rules_id' in filters) {
|
|
83
91
|
throw new framework.errors.ValidationError(`Sync rules ID is required for custom Write Checkpoint filtering`);
|
|
@@ -5,6 +5,7 @@ export * from './SourceEntity.js';
|
|
|
5
5
|
export * from './SourceTable.js';
|
|
6
6
|
export * from './StorageEngine.js';
|
|
7
7
|
|
|
8
|
+
export * from './mongo/config.js';
|
|
8
9
|
export * from './mongo/db.js';
|
|
9
10
|
export * from './mongo/models.js';
|
|
10
11
|
export * from './mongo/MongoBucketBatch.js';
|
|
@@ -17,5 +18,4 @@ export * from './mongo/MongoSyncRulesLock.js';
|
|
|
17
18
|
export * from './mongo/OperationBatch.js';
|
|
18
19
|
export * from './mongo/PersistedBatch.js';
|
|
19
20
|
export * from './mongo/util.js';
|
|
20
|
-
export * from './
|
|
21
|
-
export * from './write-checkpoint.js';
|
|
21
|
+
export * from './WriteCheckpointAPI.js';
|
|
@@ -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;
|