@powersync/service-core 0.0.0-dev-20240718134716 → 0.0.0-dev-20240918082156
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 +89 -6
- package/dist/api/RouteAPI.d.ts +68 -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 +11 -65
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/api/schema.d.ts +3 -5
- package/dist/api/schema.js +9 -79
- package/dist/api/schema.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/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/entry/cli-entry.js +4 -2
- package/dist/entry/cli-entry.js.map +1 -1
- package/dist/entry/commands/compact-action.d.ts +2 -0
- package/dist/entry/commands/compact-action.js +52 -0
- package/dist/entry/commands/compact-action.js.map +1 -0
- 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/entry/entry-index.d.ts +1 -0
- package/dist/entry/entry-index.js +1 -0
- package/dist/entry/entry-index.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/metrics/Metrics.d.ts +6 -5
- package/dist/metrics/Metrics.js +53 -10
- 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/migrations.d.ts +8 -0
- package/dist/migrations/migrations.js +19 -7
- package/dist/migrations/migrations.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 +38 -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 +187 -0
- package/dist/replication/AbstractReplicator.js.map +1 -0
- package/dist/replication/ErrorRateLimiter.d.ts +0 -9
- 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 +39 -0
- package/dist/replication/ReplicationModule.js +65 -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 +737 -0
- package/dist/routes/configure-fastify.js +57 -0
- package/dist/routes/configure-fastify.js.map +1 -0
- package/dist/routes/configure-rsocket.d.ts +13 -0
- package/dist/routes/configure-rsocket.js +47 -0
- package/dist/routes/configure-rsocket.js.map +1 -0
- package/dist/routes/endpoints/admin.d.ts +0 -34
- package/dist/routes/endpoints/admin.js +48 -89
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/checkpointing.d.ts +56 -16
- package/dist/routes/endpoints/checkpointing.js +33 -12
- package/dist/routes/endpoints/checkpointing.js.map +1 -1
- package/dist/routes/endpoints/route-endpoints-index.d.ts +0 -1
- package/dist/routes/endpoints/route-endpoints-index.js +0 -1
- package/dist/routes/endpoints/route-endpoints-index.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +46 -39
- 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 +10 -0
- package/dist/routes/endpoints/sync-stream.js +17 -13
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/routes/route-register.d.ts +1 -1
- package/dist/routes/route-register.js +1 -1
- package/dist/routes/route-register.js.map +1 -1
- package/dist/routes/router-socket.d.ts +5 -4
- package/dist/routes/router-socket.js +2 -1
- package/dist/routes/router-socket.js.map +1 -1
- package/dist/routes/router.d.ts +7 -2
- package/dist/routes/router.js.map +1 -1
- package/dist/routes/routes-index.d.ts +3 -0
- package/dist/routes/routes-index.js +3 -0
- package/dist/routes/routes-index.js.map +1 -1
- package/dist/runner/teardown.js +47 -76
- package/dist/runner/teardown.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +61 -20
- package/dist/storage/BucketStorage.js +0 -10
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/MongoBucketStorage.d.ts +4 -4
- package/dist/storage/MongoBucketStorage.js +19 -24
- package/dist/storage/MongoBucketStorage.js.map +1 -1
- 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 +4 -5
- package/dist/storage/SourceTable.js +3 -4
- package/dist/storage/SourceTable.js.map +1 -1
- package/dist/storage/StorageEngine.d.ts +24 -0
- package/dist/storage/StorageEngine.js +43 -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/mongo/MongoBucketBatch.d.ts +1 -1
- package/dist/storage/mongo/MongoBucketBatch.js +6 -7
- package/dist/storage/mongo/MongoBucketBatch.js.map +1 -1
- package/dist/storage/mongo/MongoCompactor.d.ts +40 -0
- package/dist/storage/mongo/MongoCompactor.js +293 -0
- package/dist/storage/mongo/MongoCompactor.js.map +1 -0
- 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 +9 -7
- package/dist/storage/mongo/MongoSyncBucketStorage.js +43 -28
- 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/OperationBatch.d.ts +7 -3
- package/dist/storage/mongo/OperationBatch.js +16 -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/models.d.ts +17 -7
- package/dist/storage/mongo/models.js.map +1 -1
- package/dist/storage/mongo/util.d.ts +14 -0
- package/dist/storage/mongo/util.js +70 -0
- package/dist/storage/mongo/util.js.map +1 -1
- package/dist/storage/storage-index.d.ts +5 -2
- package/dist/storage/storage-index.js +5 -2
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/sync/RequestTracker.js +2 -3
- package/dist/sync/RequestTracker.js.map +1 -1
- package/dist/sync/sync-index.d.ts +1 -0
- package/dist/sync/sync-index.js +1 -0
- package/dist/sync/sync-index.js.map +1 -1
- package/dist/sync/sync.d.ts +2 -1
- package/dist/sync/sync.js +56 -17
- package/dist/sync/sync.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/config/collectors/config-collector.d.ts +12 -0
- package/dist/util/config/collectors/config-collector.js +43 -0
- package/dist/util/config/collectors/config-collector.js.map +1 -1
- package/dist/util/config/compound-config-collector.d.ts +10 -29
- package/dist/util/config/compound-config-collector.js +28 -84
- package/dist/util/config/compound-config-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 +6 -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/protocol-types.d.ts +4 -0
- package/dist/util/protocol-types.js +5 -1
- package/dist/util/protocol-types.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 -6
- package/dist/util/utils.js +45 -25
- package/dist/util/utils.js.map +1 -1
- package/package.json +7 -7
- package/src/api/RouteAPI.ts +78 -0
- package/src/api/api-index.ts +1 -0
- package/src/api/diagnostics.ts +16 -71
- package/src/api/schema.ts +13 -89
- package/src/auth/KeyStore.ts +9 -6
- package/src/auth/auth-index.ts +0 -1
- package/src/entry/cli-entry.ts +4 -2
- package/src/entry/commands/compact-action.ts +57 -0
- package/src/entry/commands/migrate-action.ts +5 -8
- package/src/entry/commands/teardown-action.ts +2 -2
- package/src/entry/entry-index.ts +1 -0
- package/src/index.ts +5 -2
- package/src/metrics/Metrics.ts +70 -15
- 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/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 +227 -0
- package/src/replication/ErrorRateLimiter.ts +0 -44
- package/src/replication/ReplicationEngine.ts +43 -0
- package/src/replication/ReplicationModule.ts +101 -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 +101 -0
- package/src/routes/configure-rsocket.ts +60 -0
- package/src/routes/endpoints/admin.ts +74 -100
- package/src/routes/endpoints/checkpointing.ts +46 -12
- package/src/routes/endpoints/route-endpoints-index.ts +0 -1
- package/src/routes/endpoints/socket-route.ts +50 -42
- package/src/routes/endpoints/sync-rules.ts +41 -25
- package/src/routes/endpoints/sync-stream.ts +17 -13
- package/src/routes/route-register.ts +2 -2
- package/src/routes/router-socket.ts +6 -5
- package/src/routes/router.ts +7 -2
- package/src/routes/routes-index.ts +3 -0
- package/src/runner/teardown.ts +50 -88
- package/src/storage/BucketStorage.ts +74 -26
- package/src/storage/MongoBucketStorage.ts +23 -26
- package/src/storage/SourceEntity.ts +22 -0
- package/src/storage/SourceTable.ts +4 -6
- package/src/storage/StorageEngine.ts +55 -0
- package/src/storage/StorageProvider.ts +27 -0
- package/src/storage/mongo/MongoBucketBatch.ts +8 -8
- package/src/storage/mongo/MongoCompactor.ts +372 -0
- package/src/storage/mongo/MongoPersistedSyncRulesContent.ts +3 -3
- package/src/storage/mongo/MongoStorageProvider.ts +31 -0
- package/src/storage/mongo/MongoSyncBucketStorage.ts +64 -34
- package/src/storage/mongo/MongoSyncRulesLock.ts +1 -1
- package/src/storage/mongo/OperationBatch.ts +18 -11
- package/src/storage/mongo/PersistedBatch.ts +6 -5
- package/src/storage/mongo/models.ts +17 -7
- package/src/storage/mongo/util.ts +71 -1
- package/src/storage/storage-index.ts +5 -2
- package/src/sync/RequestTracker.ts +3 -3
- package/src/sync/sync-index.ts +1 -0
- package/src/sync/sync.ts +66 -17
- package/src/system/ServiceContext.ts +68 -0
- package/src/system/system-index.ts +1 -1
- package/src/util/config/collectors/config-collector.ts +48 -0
- package/src/util/config/compound-config-collector.ts +45 -110
- package/src/util/config/sync-rules/sync-rules-provider.ts +18 -0
- package/src/util/config/types.ts +6 -5
- package/src/util/config.ts +6 -23
- package/src/util/protocol-types.ts +6 -1
- package/src/util/util-index.ts +3 -6
- package/src/util/utils.ts +55 -39
- package/test/src/__snapshots__/sync.test.ts.snap +90 -5
- package/test/src/auth.test.ts +7 -7
- package/test/src/broadcast_iterable.test.ts +1 -1
- package/test/src/bucket_validation.test.ts +142 -0
- package/test/src/bucket_validation.ts +116 -0
- package/test/src/checksum_cache.test.ts +3 -3
- package/test/src/compacting.test.ts +216 -0
- package/test/src/data_storage.test.ts +275 -204
- package/test/src/env.ts +1 -3
- package/test/src/merge_iterable.test.ts +1 -6
- package/test/src/setup.ts +1 -1
- package/test/src/stream_utils.ts +42 -0
- package/test/src/sync.test.ts +209 -48
- package/test/src/util.ts +110 -55
- package/test/tsconfig.json +1 -1
- package/tsconfig.tsbuildinfo +1 -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 -517
- 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/routes/endpoints/dev.d.ts +0 -312
- package/dist/routes/endpoints/dev.js +0 -172
- package/dist/routes/endpoints/dev.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 -628
- package/src/replication/WalStreamManager.ts +0 -213
- package/src/replication/WalStreamRunner.ts +0 -180
- package/src/replication/util.ts +0 -76
- package/src/routes/endpoints/dev.ts +0 -199
- 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 -296
- package/test/src/validation.test.ts +0 -63
- package/test/src/wal_stream.test.ts +0 -314
- package/test/src/wal_stream_utils.ts +0 -147
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import { AnyBulkWriteOperation, MaxKey, MinKey } from 'mongodb';
|
|
3
|
+
import { addChecksums } from '../../util/utils.js';
|
|
4
|
+
import { PowerSyncMongo } from './db.js';
|
|
5
|
+
import { BucketDataDocument, BucketDataKey } from './models.js';
|
|
6
|
+
import { CompactOptions } from '../BucketStorage.js';
|
|
7
|
+
import { cacheKey } from './OperationBatch.js';
|
|
8
|
+
|
|
9
|
+
interface CurrentBucketState {
|
|
10
|
+
/** Bucket name */
|
|
11
|
+
bucket: string;
|
|
12
|
+
/**
|
|
13
|
+
* Rows seen in the bucket, with the last op_id of each.
|
|
14
|
+
*/
|
|
15
|
+
seen: Map<string, bigint>;
|
|
16
|
+
/**
|
|
17
|
+
* Estimated memory usage of the seen Map.
|
|
18
|
+
*/
|
|
19
|
+
trackingSize: number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Last (lowest) seen op_id that is not a PUT.
|
|
23
|
+
*/
|
|
24
|
+
lastNotPut: bigint | null;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Number of REMOVE/MOVE operations seen since lastNotPut.
|
|
28
|
+
*/
|
|
29
|
+
opsSincePut: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Additional options, primarily for testing.
|
|
34
|
+
*/
|
|
35
|
+
export interface MongoCompactOptions extends CompactOptions {
|
|
36
|
+
/** Minimum of 2 */
|
|
37
|
+
clearBatchLimit?: number;
|
|
38
|
+
/** Minimum of 1 */
|
|
39
|
+
moveBatchLimit?: number;
|
|
40
|
+
/** Minimum of 1 */
|
|
41
|
+
moveBatchQueryLimit?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_CLEAR_BATCH_LIMIT = 5000;
|
|
45
|
+
const DEFAULT_MOVE_BATCH_LIMIT = 2000;
|
|
46
|
+
const DEFAULT_MOVE_BATCH_QUERY_LIMIT = 10_000;
|
|
47
|
+
|
|
48
|
+
/** This default is primarily for tests. */
|
|
49
|
+
const DEFAULT_MEMORY_LIMIT_MB = 64;
|
|
50
|
+
|
|
51
|
+
export class MongoCompactor {
|
|
52
|
+
private updates: AnyBulkWriteOperation<BucketDataDocument>[] = [];
|
|
53
|
+
|
|
54
|
+
private idLimitBytes: number;
|
|
55
|
+
private moveBatchLimit: number;
|
|
56
|
+
private moveBatchQueryLimit: number;
|
|
57
|
+
private clearBatchLimit: number;
|
|
58
|
+
private maxOpId: bigint | undefined;
|
|
59
|
+
private buckets: string[] | undefined;
|
|
60
|
+
|
|
61
|
+
constructor(private db: PowerSyncMongo, private group_id: number, options?: MongoCompactOptions) {
|
|
62
|
+
this.idLimitBytes = (options?.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
|
|
63
|
+
this.moveBatchLimit = options?.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT;
|
|
64
|
+
this.moveBatchQueryLimit = options?.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT;
|
|
65
|
+
this.clearBatchLimit = options?.clearBatchLimit ?? DEFAULT_CLEAR_BATCH_LIMIT;
|
|
66
|
+
this.maxOpId = options?.maxOpId;
|
|
67
|
+
this.buckets = options?.compactBuckets;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Compact buckets by converting operations into MOVE and/or CLEAR operations.
|
|
72
|
+
*
|
|
73
|
+
* See /docs/compacting-operations.md for details.
|
|
74
|
+
*/
|
|
75
|
+
async compact() {
|
|
76
|
+
if (this.buckets) {
|
|
77
|
+
for (let bucket of this.buckets) {
|
|
78
|
+
// We can make this more efficient later on by iterating
|
|
79
|
+
// through the buckets in a single query.
|
|
80
|
+
// That makes batching more tricky, so we leave for later.
|
|
81
|
+
await this.compactInternal(bucket);
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
await this.compactInternal(undefined);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async compactInternal(bucket: string | undefined) {
|
|
89
|
+
const idLimitBytes = this.idLimitBytes;
|
|
90
|
+
|
|
91
|
+
let currentState: CurrentBucketState | null = null;
|
|
92
|
+
|
|
93
|
+
// Constant lower bound
|
|
94
|
+
const lowerBound: BucketDataKey = {
|
|
95
|
+
g: this.group_id,
|
|
96
|
+
b: bucket ?? (new MinKey() as any),
|
|
97
|
+
o: new MinKey() as any
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Upper bound is adjusted for each batch
|
|
101
|
+
let upperBound: BucketDataKey = {
|
|
102
|
+
g: this.group_id,
|
|
103
|
+
b: bucket ?? (new MaxKey() as any),
|
|
104
|
+
o: new MaxKey() as any
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
while (true) {
|
|
108
|
+
// Query one batch at a time, to avoid cursor timeouts
|
|
109
|
+
const batch = await this.db.bucket_data
|
|
110
|
+
.find(
|
|
111
|
+
{
|
|
112
|
+
_id: {
|
|
113
|
+
$gte: lowerBound,
|
|
114
|
+
$lt: upperBound
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
projection: {
|
|
119
|
+
_id: 1,
|
|
120
|
+
op: 1,
|
|
121
|
+
table: 1,
|
|
122
|
+
row_id: 1,
|
|
123
|
+
source_table: 1,
|
|
124
|
+
source_key: 1
|
|
125
|
+
},
|
|
126
|
+
limit: this.moveBatchQueryLimit,
|
|
127
|
+
sort: { _id: -1 },
|
|
128
|
+
singleBatch: true
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
.toArray();
|
|
132
|
+
|
|
133
|
+
if (batch.length == 0) {
|
|
134
|
+
// We've reached the end
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Set upperBound for the next batch
|
|
139
|
+
upperBound = batch[batch.length - 1]._id;
|
|
140
|
+
|
|
141
|
+
for (let doc of batch) {
|
|
142
|
+
if (currentState == null || doc._id.b != currentState.bucket) {
|
|
143
|
+
if (currentState != null && currentState.lastNotPut != null && currentState.opsSincePut >= 1) {
|
|
144
|
+
// Important to flush before clearBucket()
|
|
145
|
+
await this.flush();
|
|
146
|
+
logger.info(
|
|
147
|
+
`Inserting CLEAR at ${this.group_id}:${currentState.bucket}:${currentState.lastNotPut} to remove ${currentState.opsSincePut} operations`
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const bucket = currentState.bucket;
|
|
151
|
+
const clearOp = currentState.lastNotPut;
|
|
152
|
+
// Free memory before clearing bucket
|
|
153
|
+
currentState = null;
|
|
154
|
+
await this.clearBucket(bucket, clearOp);
|
|
155
|
+
}
|
|
156
|
+
currentState = {
|
|
157
|
+
bucket: doc._id.b,
|
|
158
|
+
seen: new Map(),
|
|
159
|
+
trackingSize: 0,
|
|
160
|
+
lastNotPut: null,
|
|
161
|
+
opsSincePut: 0
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.maxOpId != null && doc._id.o > this.maxOpId) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let isPersistentPut = doc.op == 'PUT';
|
|
170
|
+
|
|
171
|
+
if (doc.op == 'REMOVE' || doc.op == 'PUT') {
|
|
172
|
+
const key = `${doc.table}/${doc.row_id}/${cacheKey(doc.source_table!, doc.source_key!)}`;
|
|
173
|
+
const targetOp = currentState.seen.get(key);
|
|
174
|
+
if (targetOp) {
|
|
175
|
+
// Will convert to MOVE, so don't count as PUT
|
|
176
|
+
isPersistentPut = false;
|
|
177
|
+
|
|
178
|
+
this.updates.push({
|
|
179
|
+
updateOne: {
|
|
180
|
+
filter: {
|
|
181
|
+
_id: doc._id
|
|
182
|
+
},
|
|
183
|
+
update: {
|
|
184
|
+
$set: {
|
|
185
|
+
op: 'MOVE',
|
|
186
|
+
target_op: targetOp
|
|
187
|
+
},
|
|
188
|
+
$unset: {
|
|
189
|
+
source_table: 1,
|
|
190
|
+
source_key: 1,
|
|
191
|
+
table: 1,
|
|
192
|
+
row_id: 1,
|
|
193
|
+
data: 1
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
if (currentState.trackingSize >= idLimitBytes) {
|
|
200
|
+
// Reached memory limit.
|
|
201
|
+
// Keep the highest seen values in this case.
|
|
202
|
+
} else {
|
|
203
|
+
// flatstr reduces the memory usage by flattening the string
|
|
204
|
+
currentState.seen.set(flatstr(key), doc._id.o);
|
|
205
|
+
// length + 16 for the string
|
|
206
|
+
// 24 for the bigint
|
|
207
|
+
// 50 for map overhead
|
|
208
|
+
// 50 for additional overhead
|
|
209
|
+
currentState.trackingSize += key.length + 140;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (isPersistentPut) {
|
|
215
|
+
currentState.lastNotPut = null;
|
|
216
|
+
currentState.opsSincePut = 0;
|
|
217
|
+
} else if (doc.op != 'CLEAR') {
|
|
218
|
+
if (currentState.lastNotPut == null) {
|
|
219
|
+
currentState.lastNotPut = doc._id.o;
|
|
220
|
+
}
|
|
221
|
+
currentState.opsSincePut += 1;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (this.updates.length >= this.moveBatchLimit) {
|
|
225
|
+
await this.flush();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
await this.flush();
|
|
231
|
+
currentState?.seen.clear();
|
|
232
|
+
if (currentState?.lastNotPut != null && currentState?.opsSincePut > 1) {
|
|
233
|
+
logger.info(
|
|
234
|
+
`Inserting CLEAR at ${this.group_id}:${currentState.bucket}:${currentState.lastNotPut} to remove ${currentState.opsSincePut} operations`
|
|
235
|
+
);
|
|
236
|
+
const bucket = currentState.bucket;
|
|
237
|
+
const clearOp = currentState.lastNotPut;
|
|
238
|
+
// Free memory before clearing bucket
|
|
239
|
+
currentState = null;
|
|
240
|
+
await this.clearBucket(bucket, clearOp);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private async flush() {
|
|
245
|
+
if (this.updates.length > 0) {
|
|
246
|
+
logger.info(`Compacting ${this.updates.length} ops`);
|
|
247
|
+
await this.db.bucket_data.bulkWrite(this.updates, {
|
|
248
|
+
// Order is not important.
|
|
249
|
+
// Since checksums are not affected, these operations can happen in any order,
|
|
250
|
+
// and it's fine if the operations are partially applied.
|
|
251
|
+
// Each individual operation is atomic.
|
|
252
|
+
ordered: false
|
|
253
|
+
});
|
|
254
|
+
this.updates = [];
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Perform a CLEAR compact for a bucket.
|
|
260
|
+
*
|
|
261
|
+
* @param bucket bucket name
|
|
262
|
+
* @param op op_id of the last non-PUT operation, which will be converted to CLEAR.
|
|
263
|
+
*/
|
|
264
|
+
private async clearBucket(bucket: string, op: bigint) {
|
|
265
|
+
const opFilter = {
|
|
266
|
+
_id: {
|
|
267
|
+
$gte: {
|
|
268
|
+
g: this.group_id,
|
|
269
|
+
b: bucket,
|
|
270
|
+
o: new MinKey() as any
|
|
271
|
+
},
|
|
272
|
+
$lte: {
|
|
273
|
+
g: this.group_id,
|
|
274
|
+
b: bucket,
|
|
275
|
+
o: op
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const session = this.db.client.startSession();
|
|
281
|
+
try {
|
|
282
|
+
let done = false;
|
|
283
|
+
while (!done) {
|
|
284
|
+
// Do the CLEAR operation in batches, with each batch a separate transaction.
|
|
285
|
+
// The state after each batch is fully consistent.
|
|
286
|
+
// We need a transaction per batch to make sure checksums stay consistent.
|
|
287
|
+
await session.withTransaction(
|
|
288
|
+
async () => {
|
|
289
|
+
const query = this.db.bucket_data.find(opFilter, {
|
|
290
|
+
session,
|
|
291
|
+
sort: { _id: 1 },
|
|
292
|
+
projection: {
|
|
293
|
+
_id: 1,
|
|
294
|
+
op: 1,
|
|
295
|
+
checksum: 1,
|
|
296
|
+
target_op: 1
|
|
297
|
+
},
|
|
298
|
+
limit: this.clearBatchLimit
|
|
299
|
+
});
|
|
300
|
+
let checksum = 0;
|
|
301
|
+
let lastOpId: BucketDataKey | null = null;
|
|
302
|
+
let targetOp: bigint | null = null;
|
|
303
|
+
let gotAnOp = false;
|
|
304
|
+
for await (let op of query.stream()) {
|
|
305
|
+
if (op.op == 'MOVE' || op.op == 'REMOVE' || op.op == 'CLEAR') {
|
|
306
|
+
checksum = addChecksums(checksum, op.checksum);
|
|
307
|
+
lastOpId = op._id;
|
|
308
|
+
if (op.op != 'CLEAR') {
|
|
309
|
+
gotAnOp = true;
|
|
310
|
+
}
|
|
311
|
+
if (op.target_op != null) {
|
|
312
|
+
if (targetOp == null || op.target_op > targetOp) {
|
|
313
|
+
targetOp = op.target_op;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
throw new Error(`Unexpected ${op.op} operation at ${op._id.g}:${op._id.b}:${op._id.o}`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (!gotAnOp) {
|
|
321
|
+
done = true;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
logger.info(`Flushing CLEAR at ${lastOpId?.o}`);
|
|
326
|
+
await this.db.bucket_data.deleteMany(
|
|
327
|
+
{
|
|
328
|
+
_id: {
|
|
329
|
+
$gte: {
|
|
330
|
+
g: this.group_id,
|
|
331
|
+
b: bucket,
|
|
332
|
+
o: new MinKey() as any
|
|
333
|
+
},
|
|
334
|
+
$lte: lastOpId!
|
|
335
|
+
}
|
|
336
|
+
},
|
|
337
|
+
{ session }
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
await this.db.bucket_data.insertOne(
|
|
341
|
+
{
|
|
342
|
+
_id: lastOpId!,
|
|
343
|
+
op: 'CLEAR',
|
|
344
|
+
checksum: checksum,
|
|
345
|
+
data: null,
|
|
346
|
+
target_op: targetOp
|
|
347
|
+
},
|
|
348
|
+
{ session }
|
|
349
|
+
);
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
writeConcern: { w: 'majority' },
|
|
353
|
+
readConcern: { level: 'snapshot' }
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
} finally {
|
|
358
|
+
await session.endSession();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Flattens string to reduce memory usage (around 320 bytes -> 120 bytes),
|
|
365
|
+
* at the cost of some upfront CPU usage.
|
|
366
|
+
*
|
|
367
|
+
* From: https://github.com/davidmarkclements/flatstr/issues/8
|
|
368
|
+
*/
|
|
369
|
+
function flatstr(s: string) {
|
|
370
|
+
s.match(/\n/g);
|
|
371
|
+
return s;
|
|
372
|
+
}
|
|
@@ -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';
|
|
@@ -30,10 +30,10 @@ export class MongoPersistedSyncRulesContent implements PersistedSyncRulesContent
|
|
|
30
30
|
this.last_keepalive_ts = doc.last_keepalive_ts;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
parsed() {
|
|
33
|
+
parsed(options: ParseSyncRulesOptions) {
|
|
34
34
|
return new MongoPersistedSyncRules(
|
|
35
35
|
this.id,
|
|
36
|
-
SqlSyncRules.fromYaml(this.sync_rules_content),
|
|
36
|
+
SqlSyncRules.fromYaml(this.sync_rules_content, options),
|
|
37
37
|
this.last_checkpoint_lsn,
|
|
38
38
|
this.slot_name
|
|
39
39
|
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import * as db from '../../db/db-index.js';
|
|
2
|
+
import { MongoBucketStorage } from '../MongoBucketStorage.js';
|
|
3
|
+
import { BucketStorageProvider, ActiveStorage, GetStorageOptions } from '../StorageProvider.js';
|
|
4
|
+
import { PowerSyncMongo } from './db.js';
|
|
5
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
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
|
+
}
|
|
@@ -3,26 +3,33 @@ import * as bson from 'bson';
|
|
|
3
3
|
import * as mongo from 'mongodb';
|
|
4
4
|
|
|
5
5
|
import * as db from '../../db/db-index.js';
|
|
6
|
-
import * as replication from '../../replication/WalStream.js';
|
|
7
6
|
import * as util from '../../util/util-index.js';
|
|
8
7
|
import {
|
|
9
8
|
BucketDataBatchOptions,
|
|
10
9
|
BucketStorageBatch,
|
|
10
|
+
CompactOptions,
|
|
11
11
|
DEFAULT_DOCUMENT_BATCH_LIMIT,
|
|
12
12
|
DEFAULT_DOCUMENT_CHUNK_LIMIT_BYTES,
|
|
13
13
|
FlushedResult,
|
|
14
|
+
ParseSyncRulesOptions,
|
|
15
|
+
PersistedSyncRules,
|
|
16
|
+
PersistedSyncRulesContent,
|
|
14
17
|
ResolveTableOptions,
|
|
15
18
|
ResolveTableResult,
|
|
19
|
+
StartBatchOptions,
|
|
20
|
+
SyncBucketDataBatch,
|
|
16
21
|
SyncRulesBucketStorage,
|
|
17
|
-
SyncRuleStatus
|
|
22
|
+
SyncRuleStatus,
|
|
23
|
+
TerminateOptions
|
|
18
24
|
} from '../BucketStorage.js';
|
|
25
|
+
import { ChecksumCache, FetchPartialBucketChecksum } from '../ChecksumCache.js';
|
|
19
26
|
import { MongoBucketStorage } from '../MongoBucketStorage.js';
|
|
20
27
|
import { SourceTable } from '../SourceTable.js';
|
|
21
28
|
import { PowerSyncMongo } from './db.js';
|
|
22
29
|
import { BucketDataDocument, BucketDataKey, SourceKey, SyncRuleState } from './models.js';
|
|
23
30
|
import { MongoBucketBatch } from './MongoBucketBatch.js';
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
31
|
+
import { MongoCompactor } from './MongoCompactor.js';
|
|
32
|
+
import { BSON_DESERIALIZE_OPTIONS, idPrefixFilter, mapOpEntry, readSingleBatch, serializeLookup } from './util.js';
|
|
26
33
|
|
|
27
34
|
export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
28
35
|
private readonly db: PowerSyncMongo;
|
|
@@ -32,29 +39,38 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
32
39
|
}
|
|
33
40
|
});
|
|
34
41
|
|
|
42
|
+
private parsedSyncRulesCache: SqlSyncRules | undefined;
|
|
43
|
+
|
|
35
44
|
constructor(
|
|
36
45
|
public readonly factory: MongoBucketStorage,
|
|
37
46
|
public readonly group_id: number,
|
|
38
|
-
|
|
47
|
+
private readonly sync_rules: PersistedSyncRulesContent,
|
|
39
48
|
public readonly slot_name: string
|
|
40
49
|
) {
|
|
41
50
|
this.db = factory.db;
|
|
42
51
|
}
|
|
43
52
|
|
|
53
|
+
getParsedSyncRules(options: ParseSyncRulesOptions): SqlSyncRules {
|
|
54
|
+
this.parsedSyncRulesCache ??= this.sync_rules.parsed(options).sync_rules;
|
|
55
|
+
return this.parsedSyncRulesCache;
|
|
56
|
+
}
|
|
57
|
+
|
|
44
58
|
async getCheckpoint() {
|
|
45
59
|
const doc = await this.db.sync_rules.findOne(
|
|
46
60
|
{ _id: this.group_id },
|
|
47
61
|
{
|
|
48
|
-
projection: { last_checkpoint: 1
|
|
62
|
+
projection: { last_checkpoint: 1 }
|
|
49
63
|
}
|
|
50
64
|
);
|
|
51
65
|
return {
|
|
52
|
-
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n)
|
|
53
|
-
lsn: doc?.last_checkpoint_lsn ?? replication.ZERO_LSN
|
|
66
|
+
checkpoint: util.timestampToOpId(doc?.last_checkpoint ?? 0n)
|
|
54
67
|
};
|
|
55
68
|
}
|
|
56
69
|
|
|
57
|
-
async startBatch(
|
|
70
|
+
async startBatch(
|
|
71
|
+
options: StartBatchOptions,
|
|
72
|
+
callback: (batch: BucketStorageBatch) => Promise<void>
|
|
73
|
+
): Promise<FlushedResult | null> {
|
|
58
74
|
const doc = await this.db.sync_rules.findOne(
|
|
59
75
|
{
|
|
60
76
|
_id: this.group_id
|
|
@@ -65,11 +81,11 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
65
81
|
|
|
66
82
|
const batch = new MongoBucketBatch(
|
|
67
83
|
this.db,
|
|
68
|
-
this.sync_rules,
|
|
84
|
+
this.sync_rules.parsed(options).sync_rules,
|
|
69
85
|
this.group_id,
|
|
70
86
|
this.slot_name,
|
|
71
87
|
checkpoint_lsn,
|
|
72
|
-
doc?.no_checkpoint_before ??
|
|
88
|
+
doc?.no_checkpoint_before ?? options.zeroLSN
|
|
73
89
|
);
|
|
74
90
|
try {
|
|
75
91
|
await callback(batch);
|
|
@@ -87,11 +103,15 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
87
103
|
}
|
|
88
104
|
|
|
89
105
|
async resolveTable(options: ResolveTableOptions): Promise<ResolveTableResult> {
|
|
90
|
-
const { group_id, connection_id, connection_tag,
|
|
106
|
+
const { group_id, connection_id, connection_tag, entity_descriptor } = options;
|
|
91
107
|
|
|
92
|
-
const { schema, name: table,
|
|
108
|
+
const { schema, name: table, objectId, replicationColumns } = entity_descriptor;
|
|
93
109
|
|
|
94
|
-
const columns = replicationColumns.map((column) => ({
|
|
110
|
+
const columns = replicationColumns.map((column) => ({
|
|
111
|
+
name: column.name,
|
|
112
|
+
type: column.type,
|
|
113
|
+
type_oid: column.typeId
|
|
114
|
+
}));
|
|
95
115
|
let result: ResolveTableResult | null = null;
|
|
96
116
|
await this.db.client.withSession(async (session) => {
|
|
97
117
|
const col = this.db.source_tables;
|
|
@@ -99,7 +119,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
99
119
|
{
|
|
100
120
|
group_id: group_id,
|
|
101
121
|
connection_id: connection_id,
|
|
102
|
-
relation_id:
|
|
122
|
+
relation_id: objectId,
|
|
103
123
|
schema_name: schema,
|
|
104
124
|
table_name: table,
|
|
105
125
|
replica_id_columns2: columns
|
|
@@ -111,7 +131,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
111
131
|
_id: new bson.ObjectId(),
|
|
112
132
|
group_id: group_id,
|
|
113
133
|
connection_id: connection_id,
|
|
114
|
-
relation_id:
|
|
134
|
+
relation_id: objectId,
|
|
115
135
|
schema_name: schema,
|
|
116
136
|
table_name: table,
|
|
117
137
|
replica_id_columns: null,
|
|
@@ -124,7 +144,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
124
144
|
const sourceTable = new SourceTable(
|
|
125
145
|
doc._id,
|
|
126
146
|
connection_tag,
|
|
127
|
-
|
|
147
|
+
objectId,
|
|
128
148
|
schema,
|
|
129
149
|
table,
|
|
130
150
|
replicationColumns,
|
|
@@ -139,7 +159,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
139
159
|
group_id: group_id,
|
|
140
160
|
connection_id: connection_id,
|
|
141
161
|
_id: { $ne: doc._id },
|
|
142
|
-
$or: [{ relation_id:
|
|
162
|
+
$or: [{ relation_id: objectId }, { schema_name: schema, table_name: table }]
|
|
143
163
|
},
|
|
144
164
|
{ session }
|
|
145
165
|
)
|
|
@@ -154,7 +174,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
154
174
|
doc.relation_id ?? 0,
|
|
155
175
|
doc.schema_name,
|
|
156
176
|
doc.table_name,
|
|
157
|
-
doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid })) ?? [],
|
|
177
|
+
doc.replica_id_columns2?.map((c) => ({ name: c.name, typeOid: c.type_oid, type: c.type })) ?? [],
|
|
158
178
|
doc.snapshot_done ?? true
|
|
159
179
|
)
|
|
160
180
|
)
|
|
@@ -201,7 +221,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
201
221
|
checkpoint: util.OpId,
|
|
202
222
|
dataBuckets: Map<string, string>,
|
|
203
223
|
options?: BucketDataBatchOptions
|
|
204
|
-
): AsyncIterable<
|
|
224
|
+
): AsyncIterable<SyncBucketDataBatch> {
|
|
205
225
|
if (dataBuckets.size == 0) {
|
|
206
226
|
return;
|
|
207
227
|
}
|
|
@@ -267,6 +287,7 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
267
287
|
|
|
268
288
|
let batchSize = 0;
|
|
269
289
|
let currentBatch: util.SyncBucketData | null = null;
|
|
290
|
+
let targetOp: bigint | null = null;
|
|
270
291
|
|
|
271
292
|
// Ordered by _id, meaning buckets are grouped together
|
|
272
293
|
for (let rawData of data) {
|
|
@@ -284,7 +305,8 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
284
305
|
start = currentBatch.after;
|
|
285
306
|
currentBatch = null;
|
|
286
307
|
batchSize = 0;
|
|
287
|
-
yield yieldBatch;
|
|
308
|
+
yield { batch: yieldBatch, targetOp: targetOp };
|
|
309
|
+
targetOp = null;
|
|
288
310
|
}
|
|
289
311
|
|
|
290
312
|
start ??= dataBuckets.get(bucket);
|
|
@@ -298,17 +320,18 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
298
320
|
data: [],
|
|
299
321
|
next_after: start
|
|
300
322
|
};
|
|
323
|
+
targetOp = null;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const entry = mapOpEntry(row);
|
|
327
|
+
|
|
328
|
+
if (row.target_op != null) {
|
|
329
|
+
// MOVE, CLEAR
|
|
330
|
+
if (targetOp == null || row.target_op > targetOp) {
|
|
331
|
+
targetOp = row.target_op;
|
|
332
|
+
}
|
|
301
333
|
}
|
|
302
334
|
|
|
303
|
-
const entry: util.OplogEntry = {
|
|
304
|
-
op_id: util.timestampToOpId(row._id.o),
|
|
305
|
-
op: row.op,
|
|
306
|
-
object_type: row.table,
|
|
307
|
-
object_id: row.row_id,
|
|
308
|
-
checksum: Number(row.checksum),
|
|
309
|
-
subkey: `${row.source_table}/${row.source_key.toHexString()}`,
|
|
310
|
-
data: row.data
|
|
311
|
-
};
|
|
312
335
|
currentBatch.data.push(entry);
|
|
313
336
|
currentBatch.next_after = entry.op_id;
|
|
314
337
|
|
|
@@ -318,7 +341,8 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
318
341
|
if (currentBatch != null) {
|
|
319
342
|
const yieldBatch = currentBatch;
|
|
320
343
|
currentBatch = null;
|
|
321
|
-
yield yieldBatch;
|
|
344
|
+
yield { batch: yieldBatch, targetOp: targetOp };
|
|
345
|
+
targetOp = null;
|
|
322
346
|
}
|
|
323
347
|
}
|
|
324
348
|
|
|
@@ -379,9 +403,11 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
379
403
|
);
|
|
380
404
|
}
|
|
381
405
|
|
|
382
|
-
async terminate() {
|
|
383
|
-
|
|
384
|
-
|
|
406
|
+
async terminate(options?: TerminateOptions) {
|
|
407
|
+
// Default is to clear the storage except when explicitly requested not to.
|
|
408
|
+
if (!options || options?.clearStorage) {
|
|
409
|
+
await this.clear();
|
|
410
|
+
}
|
|
385
411
|
await this.db.sync_rules.updateOne(
|
|
386
412
|
{
|
|
387
413
|
_id: this.group_id
|
|
@@ -530,4 +556,8 @@ export class MongoSyncBucketStorage implements SyncRulesBucketStorage {
|
|
|
530
556
|
}
|
|
531
557
|
);
|
|
532
558
|
}
|
|
559
|
+
|
|
560
|
+
async compact(options?: CompactOptions) {
|
|
561
|
+
return new MongoCompactor(this.db, this.group_id, options).compact();
|
|
562
|
+
}
|
|
533
563
|
}
|
|
@@ -30,7 +30,7 @@ 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
|
}
|