@powersync/service-core 0.8.8 → 0.9.0
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 +31 -0
- package/dist/api/RouteAPI.d.ts +67 -0
- package/dist/api/RouteAPI.js +2 -0
- package/dist/api/RouteAPI.js.map +1 -0
- package/dist/api/api-index.d.ts +1 -0
- package/dist/api/api-index.js +1 -0
- package/dist/api/api-index.js.map +1 -1
- package/dist/api/diagnostics.d.ts +4 -4
- package/dist/api/diagnostics.js +170 -158
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/api/schema.d.ts +3 -5
- package/dist/api/schema.js +14 -80
- package/dist/api/schema.js.map +1 -1
- package/dist/auth/CachedKeyCollector.js.map +1 -1
- package/dist/auth/KeySpec.js.map +1 -1
- package/dist/auth/KeyStore.d.ts +7 -4
- package/dist/auth/KeyStore.js +1 -1
- package/dist/auth/KeyStore.js.map +1 -1
- package/dist/auth/LeakyBucket.js.map +1 -1
- package/dist/auth/RemoteJWKSCollector.d.ts +0 -2
- package/dist/auth/RemoteJWKSCollector.js.map +1 -1
- package/dist/auth/auth-index.d.ts +0 -1
- package/dist/auth/auth-index.js +0 -1
- package/dist/auth/auth-index.js.map +1 -1
- package/dist/db/mongo.js +5 -3
- package/dist/db/mongo.js.map +1 -1
- package/dist/entry/cli-entry.js +3 -2
- package/dist/entry/cli-entry.js.map +1 -1
- package/dist/entry/commands/compact-action.js +90 -14
- package/dist/entry/commands/compact-action.js.map +1 -1
- package/dist/entry/commands/migrate-action.js +4 -5
- package/dist/entry/commands/migrate-action.js.map +1 -1
- package/dist/entry/commands/teardown-action.js +2 -2
- package/dist/entry/commands/teardown-action.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/locks/MongoLocks.js.map +1 -1
- package/dist/metrics/Metrics.d.ts +2 -2
- package/dist/metrics/Metrics.js +5 -13
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/migrations/db/migrations/1684951997326-init.d.ts +2 -2
- package/dist/migrations/db/migrations/1684951997326-init.js +4 -2
- package/dist/migrations/db/migrations/1684951997326-init.js.map +1 -1
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.d.ts +2 -2
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js +4 -2
- package/dist/migrations/db/migrations/1702295701188-sync-rule-state.js.map +1 -1
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.d.ts +2 -2
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js +4 -2
- package/dist/migrations/db/migrations/1711543888062-write-checkpoint-index.js.map +1 -1
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.d.ts +3 -0
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js +31 -0
- package/dist/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.js.map +1 -0
- package/dist/migrations/executor.js.map +1 -1
- package/dist/migrations/migrations.d.ts +8 -0
- package/dist/migrations/migrations.js +19 -7
- package/dist/migrations/migrations.js.map +1 -1
- package/dist/migrations/store/migration-store.js.map +1 -1
- package/dist/modules/AbstractModule.d.ts +26 -0
- package/dist/modules/AbstractModule.js +11 -0
- package/dist/modules/AbstractModule.js.map +1 -0
- package/dist/modules/ModuleManager.d.ts +11 -0
- package/dist/modules/ModuleManager.js +32 -0
- package/dist/modules/ModuleManager.js.map +1 -0
- package/dist/modules/modules-index.d.ts +2 -0
- package/dist/modules/modules-index.js +3 -0
- package/dist/modules/modules-index.js.map +1 -0
- package/dist/replication/AbstractReplicationJob.d.ts +37 -0
- package/dist/replication/AbstractReplicationJob.js +51 -0
- package/dist/replication/AbstractReplicationJob.js.map +1 -0
- package/dist/replication/AbstractReplicator.d.ts +53 -0
- package/dist/replication/AbstractReplicator.js +250 -0
- package/dist/replication/AbstractReplicator.js.map +1 -0
- package/dist/replication/ErrorRateLimiter.d.ts +0 -10
- package/dist/replication/ErrorRateLimiter.js +1 -42
- package/dist/replication/ErrorRateLimiter.js.map +1 -1
- package/dist/replication/ReplicationEngine.d.ts +18 -0
- package/dist/replication/ReplicationEngine.js +41 -0
- package/dist/replication/ReplicationEngine.js.map +1 -0
- package/dist/replication/ReplicationModule.d.ts +51 -0
- package/dist/replication/ReplicationModule.js +68 -0
- package/dist/replication/ReplicationModule.js.map +1 -0
- package/dist/replication/replication-index.d.ts +4 -6
- package/dist/replication/replication-index.js +4 -6
- package/dist/replication/replication-index.js.map +1 -1
- package/dist/routes/RouterEngine.d.ts +42 -0
- package/dist/routes/RouterEngine.js +80 -0
- package/dist/routes/RouterEngine.js.map +1 -0
- package/dist/routes/auth.d.ts +2 -2
- package/dist/routes/auth.js +11 -11
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +37 -23
- package/dist/routes/configure-fastify.js +18 -18
- package/dist/routes/configure-fastify.js.map +1 -1
- package/dist/routes/configure-rsocket.d.ts +3 -4
- package/dist/routes/configure-rsocket.js +7 -4
- package/dist/routes/configure-rsocket.js.map +1 -1
- package/dist/routes/endpoints/admin.d.ts +30 -0
- package/dist/routes/endpoints/admin.js +46 -67
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/checkpointing.js +103 -15
- package/dist/routes/endpoints/checkpointing.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +8 -6
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.d.ts +1 -1
- package/dist/routes/endpoints/sync-rules.js +32 -23
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.d.ts +0 -1
- package/dist/routes/endpoints/sync-stream.js +8 -8
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/routes/hooks.js.map +1 -1
- package/dist/routes/route-register.js.map +1 -1
- package/dist/routes/router.d.ts +9 -2
- package/dist/routes/router.js.map +1 -1
- package/dist/routes/routes-index.d.ts +1 -0
- package/dist/routes/routes-index.js +1 -0
- package/dist/routes/routes-index.js.map +1 -1
- package/dist/runner/teardown.js +109 -76
- package/dist/runner/teardown.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +86 -36
- package/dist/storage/BucketStorage.js +6 -10
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/ChecksumCache.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +7 -11
- package/dist/storage/MongoBucketStorage.js +48 -41
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- package/dist/storage/ReplicationEventPayload.d.ts +14 -0
- package/dist/storage/ReplicationEventPayload.js +2 -0
- package/dist/storage/ReplicationEventPayload.js.map +1 -0
- package/dist/storage/SourceEntity.d.ts +20 -0
- package/dist/storage/SourceEntity.js +2 -0
- package/dist/storage/SourceEntity.js.map +1 -0
- package/dist/storage/SourceTable.d.ts +12 -5
- package/dist/storage/SourceTable.js +12 -5
- package/dist/storage/SourceTable.js.map +1 -1
- package/dist/storage/StorageEngine.d.ts +28 -0
- package/dist/storage/StorageEngine.js +45 -0
- package/dist/storage/StorageEngine.js.map +1 -0
- package/dist/storage/StorageProvider.d.ts +21 -0
- package/dist/storage/StorageProvider.js +2 -0
- package/dist/storage/StorageProvider.js.map +1 -0
- package/dist/storage/WriteCheckpointAPI.d.ts +74 -0
- package/dist/storage/WriteCheckpointAPI.js +16 -0
- package/dist/storage/WriteCheckpointAPI.js.map +1 -0
- package/dist/storage/mongo/MongoBucketBatch.d.ts +24 -5
- package/dist/storage/mongo/MongoBucketBatch.js +119 -62
- package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
- package/dist/storage/mongo/MongoCompactor.js +20 -3
- package/dist/storage/mongo/MongoCompactor.js.map +1 -1
- package/dist/storage/mongo/MongoIdSequence.js.map +1 -1
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.d.ts +2 -2
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js +2 -2
- package/dist/storage/mongo/MongoPersistedSyncRulesContent.js.map +1 -1
- package/dist/storage/mongo/MongoStorageProvider.d.ts +5 -0
- package/dist/storage/mongo/MongoStorageProvider.js +26 -0
- package/dist/storage/mongo/MongoStorageProvider.js.map +1 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +18 -10
- package/dist/storage/mongo/MongoSyncBucketStorage.js +140 -25
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoSyncRulesLock.js +1 -1
- package/dist/storage/mongo/MongoSyncRulesLock.js.map +1 -1
- package/dist/storage/mongo/MongoWriteCheckpointAPI.d.ts +20 -0
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js +103 -0
- package/dist/storage/mongo/MongoWriteCheckpointAPI.js.map +1 -0
- package/dist/storage/mongo/OperationBatch.d.ts +13 -4
- package/dist/storage/mongo/OperationBatch.js +25 -7
- package/dist/storage/mongo/OperationBatch.js.map +1 -1
- package/dist/storage/mongo/PersistedBatch.d.ts +3 -3
- package/dist/storage/mongo/PersistedBatch.js +2 -2
- package/dist/storage/mongo/PersistedBatch.js.map +1 -1
- package/dist/storage/mongo/config.d.ts +19 -0
- package/dist/storage/mongo/config.js +26 -0
- package/dist/storage/mongo/config.js.map +1 -0
- package/dist/storage/mongo/db.d.ts +3 -2
- package/dist/storage/mongo/db.js +1 -0
- package/dist/storage/mongo/db.js.map +1 -1
- package/dist/storage/mongo/models.d.ts +20 -5
- package/dist/storage/mongo/models.js.map +1 -1
- package/dist/storage/mongo/util.d.ts +12 -1
- package/dist/storage/mongo/util.js +50 -2
- package/dist/storage/mongo/util.js.map +1 -1
- package/dist/storage/storage-index.d.ts +8 -2
- package/dist/storage/storage-index.js +8 -2
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/sync/BroadcastIterable.d.ts +0 -1
- package/dist/sync/BroadcastIterable.js.map +1 -1
- package/dist/sync/LastValueSink.d.ts +0 -1
- package/dist/sync/LastValueSink.js.map +1 -1
- package/dist/sync/merge.d.ts +0 -1
- package/dist/sync/merge.js.map +1 -1
- package/dist/sync/safeRace.js.map +1 -1
- package/dist/sync/sync.d.ts +1 -1
- package/dist/sync/sync.js +5 -5
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/util.d.ts +0 -2
- package/dist/sync/util.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +37 -0
- package/dist/system/ServiceContext.js +48 -0
- package/dist/system/ServiceContext.js.map +1 -0
- package/dist/system/system-index.d.ts +1 -1
- package/dist/system/system-index.js +1 -1
- package/dist/system/system-index.js.map +1 -1
- package/dist/util/Mutex.js.map +1 -1
- package/dist/util/config/collectors/config-collector.js.map +1 -1
- package/dist/util/config/collectors/impl/base64-config-collector.js.map +1 -1
- package/dist/util/config/collectors/impl/filesystem-config-collector.js.map +1 -1
- package/dist/util/config/compound-config-collector.d.ts +9 -2
- package/dist/util/config/compound-config-collector.js +16 -24
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/inline-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/sync-rules-provider.d.ts +9 -0
- package/dist/util/config/sync-rules/sync-rules-provider.js +15 -0
- package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -0
- package/dist/util/config/types.d.ts +7 -4
- package/dist/util/config/types.js.map +1 -1
- package/dist/util/config.d.ts +3 -4
- package/dist/util/config.js +5 -20
- package/dist/util/config.js.map +1 -1
- package/dist/util/memory-tracking.js.map +1 -1
- package/dist/util/secs.js.map +1 -1
- package/dist/util/util-index.d.ts +3 -6
- package/dist/util/util-index.js +3 -6
- package/dist/util/util-index.js.map +1 -1
- package/dist/util/utils.d.ts +10 -7
- package/dist/util/utils.js +36 -25
- package/dist/util/utils.js.map +1 -1
- package/package.json +8 -12
- package/src/api/RouteAPI.ts +78 -0
- package/src/api/api-index.ts +1 -0
- package/src/api/diagnostics.ts +18 -70
- package/src/api/schema.ts +18 -90
- package/src/auth/KeyStore.ts +9 -6
- package/src/auth/RemoteJWKSCollector.ts +4 -1
- package/src/auth/auth-index.ts +0 -1
- package/src/db/mongo.ts +5 -3
- package/src/entry/cli-entry.ts +3 -2
- package/src/entry/commands/compact-action.ts +24 -12
- package/src/entry/commands/migrate-action.ts +5 -8
- package/src/entry/commands/teardown-action.ts +2 -2
- package/src/index.ts +5 -2
- package/src/metrics/Metrics.ts +6 -16
- package/src/migrations/db/migrations/1684951997326-init.ts +9 -4
- package/src/migrations/db/migrations/1702295701188-sync-rule-state.ts +7 -4
- package/src/migrations/db/migrations/1711543888062-write-checkpoint-index.ts +6 -4
- package/src/migrations/db/migrations/1727099539247-custom-write-checkpoint-index.ts +37 -0
- package/src/migrations/migrations.ts +24 -8
- package/src/modules/AbstractModule.ts +37 -0
- package/src/modules/ModuleManager.ts +34 -0
- package/src/modules/modules-index.ts +2 -0
- package/src/replication/AbstractReplicationJob.ts +79 -0
- package/src/replication/AbstractReplicator.ts +228 -0
- package/src/replication/ErrorRateLimiter.ts +0 -44
- package/src/replication/ReplicationEngine.ts +43 -0
- package/src/replication/ReplicationModule.ts +122 -0
- package/src/replication/replication-index.ts +4 -6
- package/src/routes/RouterEngine.ts +120 -0
- package/src/routes/auth.ts +21 -12
- package/src/routes/configure-fastify.ts +26 -27
- package/src/routes/configure-rsocket.ts +13 -8
- package/src/routes/endpoints/admin.ts +72 -76
- package/src/routes/endpoints/checkpointing.ts +51 -11
- package/src/routes/endpoints/socket-route.ts +10 -6
- package/src/routes/endpoints/sync-rules.ts +41 -25
- package/src/routes/endpoints/sync-stream.ts +8 -8
- package/src/routes/router.ts +8 -3
- package/src/routes/routes-index.ts +1 -0
- package/src/runner/teardown.ts +50 -88
- package/src/storage/BucketStorage.ts +103 -41
- package/src/storage/MongoBucketStorage.ts +65 -53
- package/src/storage/ReplicationEventPayload.ts +16 -0
- package/src/storage/SourceEntity.ts +22 -0
- package/src/storage/SourceTable.ts +14 -7
- package/src/storage/StorageEngine.ts +62 -0
- package/src/storage/StorageProvider.ts +27 -0
- package/src/storage/WriteCheckpointAPI.ts +85 -0
- package/src/storage/mongo/MongoBucketBatch.ts +164 -84
- package/src/storage/mongo/MongoCompactor.ts +25 -4
- package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +7 -4
- package/src/storage/mongo/MongoStorageProvider.ts +31 -0
- package/src/storage/mongo/MongoSyncBucketStorage.ts +118 -41
- package/src/storage/mongo/MongoSyncRulesLock.ts +7 -3
- package/src/storage/mongo/MongoWriteCheckpointAPI.ts +151 -0
- package/src/storage/mongo/OperationBatch.ts +28 -12
- package/src/storage/mongo/PersistedBatch.ts +10 -6
- package/src/storage/mongo/config.ts +40 -0
- package/src/storage/mongo/db.ts +4 -1
- package/src/storage/mongo/models.ts +21 -5
- package/src/storage/mongo/util.ts +48 -3
- package/src/storage/storage-index.ts +8 -2
- package/src/sync/sync.ts +7 -4
- package/src/sync/util.ts +0 -1
- package/src/system/ServiceContext.ts +68 -0
- package/src/system/system-index.ts +1 -1
- package/src/util/config/compound-config-collector.ts +31 -31
- package/src/util/config/sync-rules/sync-rules-provider.ts +18 -0
- package/src/util/config/types.ts +7 -5
- package/src/util/config.ts +6 -23
- package/src/util/util-index.ts +3 -6
- package/src/util/utils.ts +48 -41
- package/test/src/__snapshots__/sync.test.ts.snap +14 -14
- package/test/src/auth.test.ts +7 -7
- package/test/src/broadcast_iterable.test.ts +1 -1
- package/test/src/compacting.test.ts +50 -40
- package/test/src/data_storage.test.ts +382 -202
- package/test/src/env.ts +1 -3
- package/test/src/merge_iterable.test.ts +1 -6
- package/test/src/routes/probes.integration.test.ts +34 -30
- package/test/src/setup.ts +1 -1
- package/test/src/stream_utils.ts +42 -0
- package/test/src/sync.test.ts +115 -39
- package/test/src/util.ts +48 -51
- package/test/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +7 -1
- package/dist/auth/SupabaseKeyCollector.d.ts +0 -22
- package/dist/auth/SupabaseKeyCollector.js +0 -61
- package/dist/auth/SupabaseKeyCollector.js.map +0 -1
- package/dist/replication/PgRelation.d.ts +0 -16
- package/dist/replication/PgRelation.js +0 -26
- package/dist/replication/PgRelation.js.map +0 -1
- package/dist/replication/WalConnection.d.ts +0 -34
- package/dist/replication/WalConnection.js +0 -190
- package/dist/replication/WalConnection.js.map +0 -1
- package/dist/replication/WalStream.d.ts +0 -57
- package/dist/replication/WalStream.js +0 -519
- package/dist/replication/WalStream.js.map +0 -1
- package/dist/replication/WalStreamManager.d.ts +0 -30
- package/dist/replication/WalStreamManager.js +0 -198
- package/dist/replication/WalStreamManager.js.map +0 -1
- package/dist/replication/WalStreamRunner.d.ts +0 -38
- package/dist/replication/WalStreamRunner.js +0 -155
- package/dist/replication/WalStreamRunner.js.map +0 -1
- package/dist/replication/util.d.ts +0 -9
- package/dist/replication/util.js +0 -62
- package/dist/replication/util.js.map +0 -1
- package/dist/system/CorePowerSyncSystem.d.ts +0 -23
- package/dist/system/CorePowerSyncSystem.js +0 -52
- package/dist/system/CorePowerSyncSystem.js.map +0 -1
- package/dist/util/PgManager.d.ts +0 -24
- package/dist/util/PgManager.js +0 -55
- package/dist/util/PgManager.js.map +0 -1
- package/dist/util/migration_lib.d.ts +0 -11
- package/dist/util/migration_lib.js +0 -64
- package/dist/util/migration_lib.js.map +0 -1
- package/dist/util/pgwire_utils.d.ts +0 -24
- package/dist/util/pgwire_utils.js +0 -117
- package/dist/util/pgwire_utils.js.map +0 -1
- package/dist/util/populate_test_data.d.ts +0 -8
- package/dist/util/populate_test_data.js +0 -65
- package/dist/util/populate_test_data.js.map +0 -1
- package/src/auth/SupabaseKeyCollector.ts +0 -67
- package/src/replication/PgRelation.ts +0 -42
- package/src/replication/WalConnection.ts +0 -227
- package/src/replication/WalStream.ts +0 -631
- package/src/replication/WalStreamManager.ts +0 -213
- package/src/replication/WalStreamRunner.ts +0 -180
- package/src/replication/util.ts +0 -76
- package/src/system/CorePowerSyncSystem.ts +0 -64
- package/src/util/PgManager.ts +0 -64
- package/src/util/migration_lib.ts +0 -79
- package/src/util/pgwire_utils.ts +0 -139
- package/src/util/populate_test_data.ts +0 -78
- package/test/src/__snapshots__/pg_test.test.ts.snap +0 -256
- package/test/src/large_batch.test.ts +0 -194
- package/test/src/pg_test.test.ts +0 -450
- package/test/src/schema_changes.test.ts +0 -545
- package/test/src/slow_tests.test.ts +0 -338
- package/test/src/validation.test.ts +0 -63
- package/test/src/wal_stream.test.ts +0 -319
- package/test/src/wal_stream_utils.ts +0 -156
|
@@ -4,6 +4,7 @@ import { addChecksums } from '../../util/utils.js';
|
|
|
4
4
|
import { PowerSyncMongo } from './db.js';
|
|
5
5
|
import { BucketDataDocument, BucketDataKey } from './models.js';
|
|
6
6
|
import { CompactOptions } from '../BucketStorage.js';
|
|
7
|
+
import { cacheKey } from './OperationBatch.js';
|
|
7
8
|
|
|
8
9
|
interface CurrentBucketState {
|
|
9
10
|
/** Bucket name */
|
|
@@ -57,7 +58,11 @@ export class MongoCompactor {
|
|
|
57
58
|
private maxOpId: bigint | undefined;
|
|
58
59
|
private buckets: string[] | undefined;
|
|
59
60
|
|
|
60
|
-
constructor(
|
|
61
|
+
constructor(
|
|
62
|
+
private db: PowerSyncMongo,
|
|
63
|
+
private group_id: number,
|
|
64
|
+
options?: MongoCompactOptions
|
|
65
|
+
) {
|
|
61
66
|
this.idLimitBytes = (options?.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
|
|
62
67
|
this.moveBatchLimit = options?.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT;
|
|
63
68
|
this.moveBatchQueryLimit = options?.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT;
|
|
@@ -89,17 +94,33 @@ export class MongoCompactor {
|
|
|
89
94
|
|
|
90
95
|
let currentState: CurrentBucketState | null = null;
|
|
91
96
|
|
|
97
|
+
let bucketLower: string | MinKey;
|
|
98
|
+
let bucketUpper: string | MaxKey;
|
|
99
|
+
|
|
100
|
+
if (bucket == null) {
|
|
101
|
+
bucketLower = new MinKey();
|
|
102
|
+
bucketUpper = new MaxKey();
|
|
103
|
+
} else if (bucket.includes('[')) {
|
|
104
|
+
// Exact bucket name
|
|
105
|
+
bucketLower = bucket;
|
|
106
|
+
bucketUpper = bucket;
|
|
107
|
+
} else {
|
|
108
|
+
// Bucket definition name
|
|
109
|
+
bucketLower = `${bucket}[`;
|
|
110
|
+
bucketUpper = `${bucket}[\uFFFF`;
|
|
111
|
+
}
|
|
112
|
+
|
|
92
113
|
// Constant lower bound
|
|
93
114
|
const lowerBound: BucketDataKey = {
|
|
94
115
|
g: this.group_id,
|
|
95
|
-
b:
|
|
116
|
+
b: bucketLower as string,
|
|
96
117
|
o: new MinKey() as any
|
|
97
118
|
};
|
|
98
119
|
|
|
99
120
|
// Upper bound is adjusted for each batch
|
|
100
121
|
let upperBound: BucketDataKey = {
|
|
101
122
|
g: this.group_id,
|
|
102
|
-
b:
|
|
123
|
+
b: bucketUpper as string,
|
|
103
124
|
o: new MaxKey() as any
|
|
104
125
|
};
|
|
105
126
|
|
|
@@ -168,7 +189,7 @@ export class MongoCompactor {
|
|
|
168
189
|
let isPersistentPut = doc.op == 'PUT';
|
|
169
190
|
|
|
170
191
|
if (doc.op == 'REMOVE' || doc.op == 'PUT') {
|
|
171
|
-
const key = `${doc.table}/${doc.row_id}/${doc.source_table
|
|
192
|
+
const key = `${doc.table}/${doc.row_id}/${cacheKey(doc.source_table!, doc.source_key!)}`;
|
|
172
193
|
const targetOp = currentState.seen.get(key);
|
|
173
194
|
if (targetOp) {
|
|
174
195
|
// Will convert to MOVE, so don't count as PUT
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SqlSyncRules } from '@powersync/service-sync-rules';
|
|
2
2
|
import * as mongo from 'mongodb';
|
|
3
3
|
|
|
4
|
-
import { PersistedSyncRulesContent } from '../BucketStorage.js';
|
|
4
|
+
import { ParseSyncRulesOptions, PersistedSyncRulesContent } from '../BucketStorage.js';
|
|
5
5
|
import { MongoPersistedSyncRules } from './MongoPersistedSyncRules.js';
|
|
6
6
|
import { MongoSyncRulesLock } from './MongoSyncRulesLock.js';
|
|
7
7
|
import { PowerSyncMongo } from './db.js';
|
|
@@ -19,7 +19,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent
|
|
|
19
19
|
|
|
20
20
|
public current_lock: MongoSyncRulesLock | null = null;
|
|
21
21
|
|
|
22
|
-
constructor(
|
|
22
|
+
constructor(
|
|
23
|
+
private db: PowerSyncMongo,
|
|
24
|
+
doc: mongo.WithId<SyncRuleDocument>
|
|
25
|
+
) {
|
|
23
26
|
this.id = doc._id;
|
|
24
27
|
this.sync_rules_content = doc.content;
|
|
25
28
|
this.last_checkpoint_lsn = doc.last_checkpoint_lsn;
|
|
@@ -30,10 +33,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent
|
|
|
30
33
|
this.last_keepalive_ts = doc.last_keepalive_ts;
|
|
31
34
|
}
|
|
32
35
|
|
|
33
|
-
parsed() {
|
|
36
|
+
parsed(options: ParseSyncRulesOptions) {
|
|
34
37
|
return new MongoPersistedSyncRules(
|
|
35
38
|
this.id,
|
|
36
|
-
SqlSyncRules.fromYaml(this.sync_rules_content),
|
|
39
|
+
SqlSyncRules.fromYaml(this.sync_rules_content, options),
|
|
37
40
|
this.last_checkpoint_lsn,
|
|
38
41
|
this.slot_name
|
|
39
42
|
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import * as db from '../../db/db-index.js';
|
|
3
|
+
import { MongoBucketStorage } from '../MongoBucketStorage.js';
|
|
4
|
+
import { ActiveStorage, BucketStorageProvider, GetStorageOptions } from '../StorageProvider.js';
|
|
5
|
+
import { PowerSyncMongo } from './db.js';
|
|
6
|
+
|
|
7
|
+
export class MongoStorageProvider implements BucketStorageProvider {
|
|
8
|
+
get type() {
|
|
9
|
+
return 'mongodb';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async getStorage(options: GetStorageOptions): Promise<ActiveStorage> {
|
|
13
|
+
const { resolvedConfig } = options;
|
|
14
|
+
|
|
15
|
+
const client = db.mongo.createMongoClient(resolvedConfig.storage);
|
|
16
|
+
|
|
17
|
+
const database = new PowerSyncMongo(client, { database: resolvedConfig.storage.database });
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
storage: new MongoBucketStorage(database, {
|
|
21
|
+
// TODO currently need the entire resolved config due to this
|
|
22
|
+
slot_name_prefix: resolvedConfig.slot_name_prefix
|
|
23
|
+
}),
|
|
24
|
+
shutDown: () => client.close(),
|
|
25
|
+
tearDown: () => {
|
|
26
|
+
logger.info(`Tearing down storage: ${database.db.namespace}...`);
|
|
27
|
+
return database.db.dropDatabase();
|
|
28
|
+
}
|
|
29
|
+
} satisfies ActiveStorage;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -2,8 +2,9 @@ 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, logger } from '@powersync/lib-services-framework';
|
|
6
|
+
import * as timers from 'timers/promises';
|
|
5
7
|
import * as db from '../../db/db-index.js';
|
|
6
|
-
import * as replication from '../../replication/WalStream.js';
|
|
7
8
|
import * as util from '../../util/util-index.js';
|
|
8
9
|
import {
|
|
9
10
|
BucketDataBatchOptions,
|
|
@@ -12,24 +13,39 @@ import {
|
|
|
12
13
|
DEFAULT_DOCUMENT_BATCH_LIMIT,
|
|
13
14
|
DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES,
|
|
14
15
|
FlushedResult,
|
|
16
|
+
ParseSyncRulesOptions,
|
|
17
|
+
PersistedSyncRulesContent,
|
|
18
|
+
ReplicationCheckpoint,
|
|
15
19
|
ResolveTableOptions,
|
|
16
20
|
ResolveTableResult,
|
|
21
|
+
StartBatchOptions,
|
|
17
22
|
SyncBucketDataBatch,
|
|
18
23
|
SyncRulesBucketStorage,
|
|
19
|
-
|
|
24
|
+
SyncRulesBucketStorageListener,
|
|
25
|
+
SyncRuleStatus,
|
|
26
|
+
TerminateOptions
|
|
20
27
|
} from '../BucketStorage.js';
|
|
21
28
|
import { ChecksumCache, FetchPartialBucketChecksum, PartialChecksum, PartialChecksumMap } from '../ChecksumCache.js';
|
|
22
29
|
import { MongoBucketStorage } from '../MongoBucketStorage.js';
|
|
23
30
|
import { SourceTable } from '../SourceTable.js';
|
|
31
|
+
import {
|
|
32
|
+
BatchedCustomWriteCheckpointOptions,
|
|
33
|
+
ManagedWriteCheckpointOptions,
|
|
34
|
+
SyncStorageLastWriteCheckpointFilters,
|
|
35
|
+
WriteCheckpointAPI,
|
|
36
|
+
WriteCheckpointMode
|
|
37
|
+
} from '../WriteCheckpointAPI.js';
|
|
24
38
|
import { PowerSyncMongo } from './db.js';
|
|
25
39
|
import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js';
|
|
26
40
|
import { MongoBucketBatch } from './MongoBucketBatch.js';
|
|
27
41
|
import { MongoCompactor } from './MongoCompactor.js';
|
|
42
|
+
import { MongoWriteCheckpointAPI } from './MongoWriteCheckpointAPI.js';
|
|
28
43
|
import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js';
|
|
29
|
-
import { logger } from '@powersync/lib-services-framework';
|
|
30
|
-
import * as timers from 'timers/promises';
|
|
31
44
|
|
|
32
|
-
export class MongoSyncBucketStorage
|
|
45
|
+
export class MongoSyncBucketStorage
|
|
46
|
+
extends DisposableObserver<SyncRulesBucketStorageListener>
|
|
47
|
+
implements SyncRulesBucketStorage
|
|
48
|
+
{
|
|
33
49
|
private readonly db: PowerSyncMongo;
|
|
34
50
|
private checksumCache = new ChecksumCache({
|
|
35
51
|
fetchChecksums: (batch) => {
|
|
@@ -37,16 +53,70 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
37
53
|
}
|
|
38
54
|
});
|
|
39
55
|
|
|
56
|
+
private parsedSyncRulesCache: { parsed: SqlSyncRules; options: ParseSyncRulesOptions } | undefined;
|
|
57
|
+
private writeCheckpointAPI: WriteCheckpointAPI;
|
|
58
|
+
|
|
40
59
|
constructor(
|
|
41
60
|
public readonly factory: MongoBucketStorage,
|
|
42
61
|
public readonly group_id: number,
|
|
43
|
-
|
|
44
|
-
public readonly slot_name: string
|
|
62
|
+
private readonly sync_rules: PersistedSyncRulesContent,
|
|
63
|
+
public readonly slot_name: string,
|
|
64
|
+
writeCheckpointMode: WriteCheckpointMode = WriteCheckpointMode.MANAGED
|
|
45
65
|
) {
|
|
66
|
+
super();
|
|
46
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
|
+
});
|
|
47
93
|
}
|
|
48
94
|
|
|
49
|
-
|
|
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
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules {
|
|
107
|
+
const { parsed, options: cachedOptions } = this.parsedSyncRulesCache ?? {};
|
|
108
|
+
/**
|
|
109
|
+
* Check if the cached sync rules, if present, had the same options.
|
|
110
|
+
* Parse sync rules if the options are different or if there is no cached value.
|
|
111
|
+
*/
|
|
112
|
+
if (!parsed || options.defaultSchema != cachedOptions?.defaultSchema) {
|
|
113
|
+
this.parsedSyncRulesCache = { parsed: this.sync_rules.parsed(options).sync_rules, options };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return this.parsedSyncRulesCache!.parsed;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async getCheckpoint(): Promise<ReplicationCheckpoint> {
|
|
50
120
|
const doc = await this.db.sync_rules.findOne(
|
|
51
121
|
{ _id: this.group_id },
|
|
52
122
|
{
|
|
@@ -55,11 +125,14 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
55
125
|
);
|
|
56
126
|
return {
|
|
57
127
|
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n),
|
|
58
|
-
lsn: doc?.last_checkpoint_lsn ??
|
|
128
|
+
lsn: doc?.last_checkpoint_lsn ?? null
|
|
59
129
|
};
|
|
60
130
|
}
|
|
61
131
|
|
|
62
|
-
async startBatch(
|
|
132
|
+
async startBatch(
|
|
133
|
+
options: StartBatchOptions,
|
|
134
|
+
callback: (batch: BucketStorageBatch) => Promise<void>
|
|
135
|
+
): Promise<FlushedResult | null> {
|
|
63
136
|
const doc = await this.db.sync_rules.findOne(
|
|
64
137
|
{
|
|
65
138
|
_id: this.group_id
|
|
@@ -68,35 +141,36 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
68
141
|
);
|
|
69
142
|
const checkpoint_lsn = doc?.last_checkpoint_lsn ?? null;
|
|
70
143
|
|
|
71
|
-
|
|
72
|
-
this.db,
|
|
73
|
-
this.sync_rules,
|
|
74
|
-
this.group_id,
|
|
75
|
-
this.slot_name,
|
|
76
|
-
checkpoint_lsn,
|
|
77
|
-
doc?.no_checkpoint_before ??
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
} catch (e) {
|
|
89
|
-
await batch.abort();
|
|
90
|
-
throw e;
|
|
144
|
+
await using batch = new MongoBucketBatch({
|
|
145
|
+
db: this.db,
|
|
146
|
+
syncRules: this.sync_rules.parsed(options).sync_rules,
|
|
147
|
+
groupId: this.group_id,
|
|
148
|
+
slotName: this.slot_name,
|
|
149
|
+
lastCheckpointLsn: checkpoint_lsn,
|
|
150
|
+
noCheckpointBeforeLsn: doc?.no_checkpoint_before ?? options.zeroLSN,
|
|
151
|
+
storeCurrentData: options.storeCurrentData
|
|
152
|
+
});
|
|
153
|
+
this.iterateListeners((cb) => cb.batchStarted?.(batch));
|
|
154
|
+
|
|
155
|
+
await callback(batch);
|
|
156
|
+
await batch.flush();
|
|
157
|
+
if (batch.last_flushed_op) {
|
|
158
|
+
return { flushed_op: String(batch.last_flushed_op) };
|
|
159
|
+
} else {
|
|
160
|
+
return null;
|
|
91
161
|
}
|
|
92
162
|
}
|
|
93
163
|
|
|
94
164
|
async resolveTable(options: ResolveTableOptions): Promise<ResolveTableResult> {
|
|
95
|
-
const { group_id, connection_id, connection_tag,
|
|
165
|
+
const { group_id, connection_id, connection_tag, entity_descriptor } = options;
|
|
96
166
|
|
|
97
|
-
const { schema, name: table,
|
|
167
|
+
const { schema, name: table, objectId, replicationColumns } = entity_descriptor;
|
|
98
168
|
|
|
99
|
-
const columns = replicationColumns.map((column) => ({
|
|
169
|
+
const columns = replicationColumns.map((column) => ({
|
|
170
|
+
name: column.name,
|
|
171
|
+
type: column.type,
|
|
172
|
+
type_oid: column.typeId
|
|
173
|
+
}));
|
|
100
174
|
let result: ResolveTableResult | null = null;
|
|
101
175
|
await this.db.client.withSession(async (session) => {
|
|
102
176
|
const col = this.db.source_tables;
|
|
@@ -104,7 +178,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
104
178
|
{
|
|
105
179
|
group_id: group_id,
|
|
106
180
|
connection_id: connection_id,
|
|
107
|
-
relation_id:
|
|
181
|
+
relation_id: objectId,
|
|
108
182
|
schema_name: schema,
|
|
109
183
|
table_name: table,
|
|
110
184
|
replica_id_columns2: columns
|
|
@@ -116,7 +190,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
116
190
|
_id: new bson.ObjectId(),
|
|
117
191
|
group_id: group_id,
|
|
118
192
|
connection_id: connection_id,
|
|
119
|
-
relation_id:
|
|
193
|
+
relation_id: objectId,
|
|
120
194
|
schema_name: schema,
|
|
121
195
|
table_name: table,
|
|
122
196
|
replica_id_columns: null,
|
|
@@ -129,12 +203,13 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
129
203
|
const sourceTable = new SourceTable(
|
|
130
204
|
doc._id,
|
|
131
205
|
connection_tag,
|
|
132
|
-
|
|
206
|
+
objectId,
|
|
133
207
|
schema,
|
|
134
208
|
table,
|
|
135
209
|
replicationColumns,
|
|
136
210
|
doc.snapshot_done ?? true
|
|
137
211
|
);
|
|
212
|
+
sourceTable.syncEvent = options.sync_rules.tableTriggersEvent(sourceTable);
|
|
138
213
|
sourceTable.syncData = options.sync_rules.tableSyncsData(sourceTable);
|
|
139
214
|
sourceTable.syncParameters = options.sync_rules.tableSyncsParameters(sourceTable);
|
|
140
215
|
|
|
@@ -144,7 +219,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
144
219
|
group_id: group_id,
|
|
145
220
|
connection_id: connection_id,
|
|
146
221
|
_id: { $ne: doc._id },
|
|
147
|
-
$or: [{ relation_id:
|
|
222
|
+
$or: [{ relation_id: objectId }, { schema_name: schema, table_name: table }]
|
|
148
223
|
},
|
|
149
224
|
{ session }
|
|
150
225
|
)
|
|
@@ -159,7 +234,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
159
234
|
doc.relation_id ?? 0,
|
|
160
235
|
doc.schema_name,
|
|
161
236
|
doc.table_name,
|
|
162
|
-
doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid })) ?? [],
|
|
237
|
+
doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid, type: c.type })) ?? [],
|
|
163
238
|
doc.snapshot_done ?? true
|
|
164
239
|
)
|
|
165
240
|
)
|
|
@@ -398,9 +473,11 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
398
473
|
);
|
|
399
474
|
}
|
|
400
475
|
|
|
401
|
-
async terminate() {
|
|
402
|
-
|
|
403
|
-
|
|
476
|
+
async terminate(options?: TerminateOptions) {
|
|
477
|
+
// Default is to clear the storage except when explicitly requested not to.
|
|
478
|
+
if (!options || options?.clearStorage) {
|
|
479
|
+
await this.clear();
|
|
480
|
+
}
|
|
404
481
|
await this.db.sync_rules.updateOne(
|
|
405
482
|
{
|
|
406
483
|
_id: this.group_id
|
|
@@ -9,7 +9,7 @@ import { logger } from '@powersync/lib-services-framework';
|
|
|
9
9
|
* replicates those sync rules at a time.
|
|
10
10
|
*/
|
|
11
11
|
export class MongoSyncRulesLock implements ReplicationLock {
|
|
12
|
-
private readonly refreshInterval: NodeJS.
|
|
12
|
+
private readonly refreshInterval: NodeJS.Timeout;
|
|
13
13
|
|
|
14
14
|
static async createLock(db: PowerSyncMongo, sync_rules: PersistedSyncRulesContent): Promise<MongoSyncRulesLock> {
|
|
15
15
|
const lockId = crypto.randomBytes(8).toString('hex');
|
|
@@ -30,12 +30,16 @@ export class MongoSyncRulesLock implements ReplicationLock {
|
|
|
30
30
|
);
|
|
31
31
|
|
|
32
32
|
if (doc == null) {
|
|
33
|
-
throw new Error(`
|
|
33
|
+
throw new Error(`Sync rules: ${sync_rules.id} have been locked by another process for replication.`);
|
|
34
34
|
}
|
|
35
35
|
return new MongoSyncRulesLock(db, sync_rules.id, lockId);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
constructor(
|
|
38
|
+
constructor(
|
|
39
|
+
private db: PowerSyncMongo,
|
|
40
|
+
public sync_rules_id: number,
|
|
41
|
+
private lock_id: string
|
|
42
|
+
) {
|
|
39
43
|
this.refreshInterval = setInterval(async () => {
|
|
40
44
|
try {
|
|
41
45
|
await this.refresh();
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as framework from '@powersync/lib-services-framework';
|
|
2
|
+
import {
|
|
3
|
+
CustomWriteCheckpointFilters,
|
|
4
|
+
CustomWriteCheckpointOptions,
|
|
5
|
+
LastWriteCheckpointFilters,
|
|
6
|
+
ManagedWriteCheckpointFilters,
|
|
7
|
+
ManagedWriteCheckpointOptions,
|
|
8
|
+
WriteCheckpointAPI,
|
|
9
|
+
WriteCheckpointMode
|
|
10
|
+
} from '../WriteCheckpointAPI.js';
|
|
11
|
+
import { PowerSyncMongo } from './db.js';
|
|
12
|
+
|
|
13
|
+
export type MongoCheckpointAPIOptions = {
|
|
14
|
+
db: PowerSyncMongo;
|
|
15
|
+
mode: WriteCheckpointMode;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class MongoWriteCheckpointAPI implements WriteCheckpointAPI {
|
|
19
|
+
readonly db: PowerSyncMongo;
|
|
20
|
+
private _mode: WriteCheckpointMode;
|
|
21
|
+
|
|
22
|
+
constructor(options: MongoCheckpointAPIOptions) {
|
|
23
|
+
this.db = options.db;
|
|
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;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async batchCreateCustomWriteCheckpoints(checkpoints: CustomWriteCheckpointOptions[]): Promise<void> {
|
|
36
|
+
return batchCreateCustomWriteCheckpoints(this.db, checkpoints);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async createCustomWriteCheckpoint(options: CustomWriteCheckpointOptions): Promise<bigint> {
|
|
40
|
+
if (this.writeCheckpointMode !== WriteCheckpointMode.CUSTOM) {
|
|
41
|
+
throw new framework.errors.ValidationError(
|
|
42
|
+
`Creating a custom Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"`
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { checkpoint, user_id, sync_rules_id } = options;
|
|
47
|
+
const doc = await this.db.custom_write_checkpoints.findOneAndUpdate(
|
|
48
|
+
{
|
|
49
|
+
user_id: user_id,
|
|
50
|
+
sync_rules_id
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
$set: {
|
|
54
|
+
checkpoint
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
{ upsert: true, returnDocument: 'after' }
|
|
58
|
+
);
|
|
59
|
+
return doc!.checkpoint;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async createManagedWriteCheckpoint(checkpoint: ManagedWriteCheckpointOptions): Promise<bigint> {
|
|
63
|
+
if (this.writeCheckpointMode !== WriteCheckpointMode.MANAGED) {
|
|
64
|
+
throw new framework.errors.ValidationError(
|
|
65
|
+
`Attempting to create a managed Write Checkpoint when the current Write Checkpoint mode is set to "${this.writeCheckpointMode}"`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { user_id, heads: lsns } = checkpoint;
|
|
70
|
+
const doc = await this.db.write_checkpoints.findOneAndUpdate(
|
|
71
|
+
{
|
|
72
|
+
user_id: user_id
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
$set: {
|
|
76
|
+
lsns
|
|
77
|
+
},
|
|
78
|
+
$inc: {
|
|
79
|
+
client_id: 1n
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
{ upsert: true, returnDocument: 'after' }
|
|
83
|
+
);
|
|
84
|
+
return doc!.client_id;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async lastWriteCheckpoint(filters: LastWriteCheckpointFilters): Promise<bigint | null> {
|
|
88
|
+
switch (this.writeCheckpointMode) {
|
|
89
|
+
case WriteCheckpointMode.CUSTOM:
|
|
90
|
+
if (false == 'sync_rules_id' in filters) {
|
|
91
|
+
throw new framework.errors.ValidationError(`Sync rules ID is required for custom Write Checkpoint filtering`);
|
|
92
|
+
}
|
|
93
|
+
return this.lastCustomWriteCheckpoint(filters);
|
|
94
|
+
case WriteCheckpointMode.MANAGED:
|
|
95
|
+
if (false == 'heads' in filters) {
|
|
96
|
+
throw new framework.errors.ValidationError(
|
|
97
|
+
`Replication HEAD is required for managed Write Checkpoint filtering`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
return this.lastManagedWriteCheckpoint(filters);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
protected async lastCustomWriteCheckpoint(filters: CustomWriteCheckpointFilters) {
|
|
105
|
+
const { user_id, sync_rules_id } = filters;
|
|
106
|
+
const lastWriteCheckpoint = await this.db.custom_write_checkpoints.findOne({
|
|
107
|
+
user_id,
|
|
108
|
+
sync_rules_id
|
|
109
|
+
});
|
|
110
|
+
return lastWriteCheckpoint?.checkpoint ?? null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected async lastManagedWriteCheckpoint(filters: ManagedWriteCheckpointFilters) {
|
|
114
|
+
const { user_id, heads } = filters;
|
|
115
|
+
// TODO: support multiple heads when we need to support multiple connections
|
|
116
|
+
const lsn = heads['1'];
|
|
117
|
+
if (lsn == null) {
|
|
118
|
+
// Can happen if we haven't replicated anything yet.
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const lastWriteCheckpoint = await this.db.write_checkpoints.findOne({
|
|
122
|
+
user_id: user_id,
|
|
123
|
+
'lsns.1': { $lte: lsn }
|
|
124
|
+
});
|
|
125
|
+
return lastWriteCheckpoint?.client_id ?? null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function batchCreateCustomWriteCheckpoints(
|
|
130
|
+
db: PowerSyncMongo,
|
|
131
|
+
checkpoints: CustomWriteCheckpointOptions[]
|
|
132
|
+
): Promise<void> {
|
|
133
|
+
if (!checkpoints.length) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
await db.custom_write_checkpoints.bulkWrite(
|
|
138
|
+
checkpoints.map((checkpointOptions) => ({
|
|
139
|
+
updateOne: {
|
|
140
|
+
filter: { user_id: checkpointOptions.user_id, sync_rules_id: checkpointOptions.sync_rules_id },
|
|
141
|
+
update: {
|
|
142
|
+
$set: {
|
|
143
|
+
checkpoint: checkpointOptions.checkpoint,
|
|
144
|
+
sync_rules_id: checkpointOptions.sync_rules_id
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
upsert: true
|
|
148
|
+
}
|
|
149
|
+
}))
|
|
150
|
+
);
|
|
151
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import * as bson from 'bson';
|
|
2
1
|
import { ToastableSqliteRow } from '@powersync/service-sync-rules';
|
|
2
|
+
import * as bson from 'bson';
|
|
3
3
|
|
|
4
|
-
import * as util from '../../util/util-index.js';
|
|
5
4
|
import { SaveOptions } from '../BucketStorage.js';
|
|
5
|
+
import { isUUID } from './util.js';
|
|
6
|
+
import { ReplicaId } from './models.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Maximum number of operations in a batch.
|
|
@@ -42,7 +43,16 @@ export class OperationBatch {
|
|
|
42
43
|
return this.batch.length >= MAX_BATCH_COUNT || this.currentSize > MAX_RECORD_BATCH_SIZE;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @param sizes Map of source key to estimated size of the current_data document, or undefined if current_data is not persisted.
|
|
49
|
+
*
|
|
50
|
+
*/
|
|
51
|
+
*batched(sizes: Map<string, number> | undefined): Generator<RecordOperation[]> {
|
|
52
|
+
if (sizes == null) {
|
|
53
|
+
yield this.batch;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
46
56
|
let currentBatch: RecordOperation[] = [];
|
|
47
57
|
let currentBatchSize = 0;
|
|
48
58
|
for (let op of this.batch) {
|
|
@@ -63,18 +73,15 @@ export class OperationBatch {
|
|
|
63
73
|
}
|
|
64
74
|
|
|
65
75
|
export class RecordOperation {
|
|
66
|
-
public readonly afterId:
|
|
67
|
-
public readonly beforeId:
|
|
76
|
+
public readonly afterId: ReplicaId | null;
|
|
77
|
+
public readonly beforeId: ReplicaId;
|
|
68
78
|
public readonly internalBeforeKey: string;
|
|
69
79
|
public readonly internalAfterKey: string | null;
|
|
70
80
|
public readonly estimatedSize: number;
|
|
71
81
|
|
|
72
82
|
constructor(public readonly record: SaveOptions) {
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const beforeId = record.before
|
|
76
|
-
? util.getUuidReplicaIdentityBson(record.before, record.sourceTable.replicaIdColumns!)
|
|
77
|
-
: afterId!;
|
|
83
|
+
const afterId = record.afterReplicaId ?? null;
|
|
84
|
+
const beforeId = record.beforeReplicaId ?? record.afterReplicaId;
|
|
78
85
|
this.afterId = afterId;
|
|
79
86
|
this.beforeId = beforeId;
|
|
80
87
|
this.internalBeforeKey = cacheKey(record.sourceTable.id, beforeId);
|
|
@@ -84,8 +91,17 @@ export class RecordOperation {
|
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
|
|
87
|
-
|
|
88
|
-
|
|
94
|
+
/**
|
|
95
|
+
* In-memory cache key - must not be persisted.
|
|
96
|
+
*/
|
|
97
|
+
export function cacheKey(table: bson.ObjectId, id: ReplicaId) {
|
|
98
|
+
if (isUUID(id)) {
|
|
99
|
+
return `${table.toHexString()}.${id.toHexString()}`;
|
|
100
|
+
} else if (typeof id == 'string') {
|
|
101
|
+
return `${table.toHexString()}.${id}`;
|
|
102
|
+
} else {
|
|
103
|
+
return `${table.toHexString()}.${(bson.serialize({ id: id }) as Buffer).toString('base64')}`;
|
|
104
|
+
}
|
|
89
105
|
}
|
|
90
106
|
|
|
91
107
|
/**
|