@powersync/service-core 0.0.0-dev-20241022094219 → 0.0.0-dev-20241023191639

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 (47) hide show
  1. package/CHANGELOG.md +6 -5
  2. package/dist/routes/endpoints/checkpointing.js +82 -14
  3. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  4. package/dist/storage/BucketStorage.d.ts +4 -3
  5. package/dist/storage/BucketStorage.js.map +1 -1
  6. package/dist/storage/MongoBucketStorage.d.ts +0 -8
  7. package/dist/storage/MongoBucketStorage.js +4 -21
  8. package/dist/storage/MongoBucketStorage.js.map +1 -1
  9. package/dist/storage/StorageEngine.d.ts +1 -5
  10. package/dist/storage/StorageEngine.js +1 -19
  11. package/dist/storage/StorageEngine.js.map +1 -1
  12. package/dist/storage/StorageProvider.d.ts +1 -8
  13. package/dist/storage/{write-checkpoint.d.ts → WriteCheckpointAPI.d.ts} +29 -10
  14. package/dist/storage/{write-checkpoint.js → WriteCheckpointAPI.js} +2 -2
  15. package/dist/storage/WriteCheckpointAPI.js.map +1 -0
  16. package/dist/storage/mongo/MongoBucketBatch.d.ts +2 -2
  17. package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
  18. package/dist/storage/mongo/MongoStorageProvider.js +1 -2
  19. package/dist/storage/mongo/MongoStorageProvider.js.map +1 -1
  20. package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +9 -1
  21. package/dist/storage/mongo/MongoSyncBucketStorage.js +33 -4
  22. package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
  23. package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +4 -2
  24. package/dist/storage/mongo/MongoWriteCheckpointAPI.js +13 -7
  25. package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +1 -1
  26. package/dist/storage/storage-index.d.ts +2 -2
  27. package/dist/storage/storage-index.js +2 -2
  28. package/dist/storage/storage-index.js.map +1 -1
  29. package/dist/util/config/compound-config-collector.js +2 -1
  30. package/dist/util/config/compound-config-collector.js.map +1 -1
  31. package/dist/util/config/types.d.ts +1 -0
  32. package/package.json +5 -5
  33. package/src/routes/endpoints/checkpointing.ts +8 -1
  34. package/src/storage/BucketStorage.ts +6 -5
  35. package/src/storage/MongoBucketStorage.ts +6 -38
  36. package/src/storage/StorageEngine.ts +2 -24
  37. package/src/storage/StorageProvider.ts +1 -9
  38. package/src/storage/{write-checkpoint.ts → WriteCheckpointAPI.ts} +31 -13
  39. package/src/storage/mongo/MongoBucketBatch.ts +2 -2
  40. package/src/storage/mongo/MongoStorageProvider.ts +1 -2
  41. package/src/storage/mongo/MongoSyncBucketStorage.ts +49 -4
  42. package/src/storage/mongo/MongoWriteCheckpointAPI.ts +16 -8
  43. package/src/storage/storage-index.ts +2 -2
  44. package/src/util/config/compound-config-collector.ts +2 -1
  45. package/src/util/config/types.ts +1 -0
  46. package/tsconfig.tsbuildinfo +1 -1
  47. package/dist/storage/write-checkpoint.js.map +0 -1
@@ -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, StorageSettings } from './StorageProvider.js';
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 = 'manual',
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 CustomWriteCheckpointOptions extends CustomWriteCheckpointFilters {
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
- * Options for creating a custom Write Checkpoint in a batch.
38
- * A {@link BucketStorageBatch} is already associated with a Sync Rules instance.
39
- * The `sync_rules_id` is not required here.
40
- */
41
- export type BatchedCustomWriteCheckpointOptions = Omit<CustomWriteCheckpointOptions, 'sync_rules_id'>;
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 WriteCheckpointAPI {
58
- batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise<void>;
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 '../write-checkpoint.js';
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';
@@ -84,7 +84,7 @@ export class MongoBucketBatch extends DisposableObserver<BucketBatchStorageListe
84
84
  this.batch = new OperationBatch();
85
85
  }
86
86
 
87
- addCustomWriteCheckpoint(checkpoint: CustomWriteCheckpointOptions): void {
87
+ addCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): void {
88
88
  this.write_checkpoint_batch.push({
89
89
  ...checkpoint,
90
90
  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,7 +2,8 @@ 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 {
@@ -27,13 +28,19 @@ import {
27
28
  import { ChecksumCache, FetchPartialBucketChecksum, PartialChecksum, PartialChecksumMap } from '../ChecksumCache.js';
28
29
  import { MongoBucketStorage } from '../MongoBucketStorage.js';
29
30
  import { SourceTable } from '../SourceTable.js';
31
+ import {
32
+ BatchedCustomWriteCheckpointOptions,
33
+ ManagedWriteCheckpointOptions,
34
+ SyncStorageLastWriteCheckpointFilters,
35
+ WriteCheckpointAPI,
36
+ WriteCheckpointMode
37
+ } from '../WriteCheckpointAPI.js';
30
38
  import { PowerSyncMongo } from './db.js';
31
39
  import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js';
32
40
  import { MongoBucketBatch } from './MongoBucketBatch.js';
33
41
  import { MongoCompactor } from './MongoCompactor.js';
42
+ import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
34
43
  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
44
 
38
45
  export class MongoSyncBucketStorage
39
46
  extends DisposableObserver<SyncRulesBucketStorageListener>
@@ -47,15 +54,53 @@ export class MongoSyncBucketStorage
47
54
  });
48
55
 
49
56
  private parsedSyncRulesCache: SqlSyncRules | undefined;
57
+ private writeCheckpointAPI: WriteCheckpointAPI;
50
58
 
51
59
  constructor(
52
60
  public readonly factory: MongoBucketStorage,
53
61
  public readonly group_id: number,
54
62
  private readonly sync_rules: PersistedSyncRulesContent,
55
- public readonly slot_name: string
63
+ public readonly slot_name: string,
64
+ writeCheckpointMode: WriteCheckpointMode = WriteCheckpointMode.MANAGED
56
65
  ) {
57
66
  super();
58
67
  this.db = factory.db;
68
+ this.writeCheckpointAPI = new MongoWriteCheckpointAPI({
69
+ db: this.db,
70
+ mode: writeCheckpointMode
71
+ });
72
+ }
73
+
74
+ get writeCheckpointMode() {
75
+ return this.writeCheckpointAPI.writeCheckpointMode;
76
+ }
77
+
78
+ setWriteCheckpointMode(mode: WriteCheckpointMode): void {
79
+ this.writeCheckpointAPI.setWriteCheckpointMode(mode);
80
+ }
81
+
82
+ batchCreateCustomWriteCheckpoints(checkpoints: BatchedCustomWriteCheckpointOptions[]): Promise<void> {
83
+ return this.writeCheckpointAPI.batchCreateCustomWriteCheckpoints(
84
+ checkpoints.map((checkpoint) => ({ ...checkpoint, sync_rules_id: this.group_id }))
85
+ );
86
+ }
87
+
88
+ createCustomWriteCheckpoint(checkpoint: BatchedCustomWriteCheckpointOptions): Promise<bigint> {
89
+ return this.writeCheckpointAPI.createCustomWriteCheckpoint({
90
+ ...checkpoint,
91
+ sync_rules_id: this.group_id
92
+ });
93
+ }
94
+
95
+ createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise<bigint> {
96
+ return this.writeCheckpointAPI.createManagedWriteCheckpoint(checkpoint);
97
+ }
98
+
99
+ lastWriteCheckpoint(filters: SyncStorageLastWriteCheckpointFilters): Promise<bigint | null> {
100
+ return this.writeCheckpointAPI.lastWriteCheckpoint({
101
+ ...filters,
102
+ sync_rules_id: this.group_id
103
+ });
59
104
  }
60
105
 
61
106
  getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules {
@@ -7,7 +7,7 @@ import {
7
7
  ManagedWriteCheckpointOptions,
8
8
  WriteCheckpointAPI,
9
9
  WriteCheckpointMode
10
- } from '../write-checkpoint.js';
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
- readonly mode: WriteCheckpointMode;
20
+ private _mode: WriteCheckpointMode;
21
21
 
22
22
  constructor(options: MongoCheckpointAPIOptions) {
23
23
  this.db = options.db;
24
- this.mode = options.mode;
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.mode !== WriteCheckpointMode.CUSTOM) {
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.mode}"`
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.mode !== WriteCheckpointMode.MANAGED) {
63
+ if (this.writeCheckpointMode !== WriteCheckpointMode.MANAGED) {
56
64
  throw new framework.errors.ValidationError(
57
- `Creating a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.mode}"`
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.mode) {
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 './mongo/config.js';
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;
@@ -64,4 +64,5 @@ export type ResolvedPowerSyncConfig = {
64
64
 
65
65
  /** Prefix for postgres replication slot names. May eventually be connection-specific. */
66
66
  slot_name_prefix: string;
67
+ parameters: Record<string, number | string | boolean | null>;
67
68
  };