@powersync/service-core 0.4.1 → 0.5.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 +16 -0
- package/dist/entry/cli-entry.js +2 -1
- 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 +49 -0
- package/dist/entry/commands/compact-action.js.map +1 -0
- 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/routes/endpoints/socket-route.js +9 -1
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +9 -1
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +25 -1
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoCompactor.d.ts +38 -0
- package/dist/storage/mongo/MongoCompactor.js +278 -0
- package/dist/storage/mongo/MongoCompactor.js.map +1 -0
- package/dist/storage/mongo/MongoSyncBucketStorage.d.ts +3 -2
- package/dist/storage/mongo/MongoSyncBucketStorage.js +19 -13
- package/dist/storage/mongo/MongoSyncBucketStorage.js.map +1 -1
- package/dist/storage/mongo/models.d.ts +5 -4
- package/dist/storage/mongo/models.js.map +1 -1
- package/dist/storage/mongo/util.d.ts +3 -0
- package/dist/storage/mongo/util.js +22 -0
- package/dist/storage/mongo/util.js.map +1 -1
- package/dist/sync/RequestTracker.d.ts +9 -0
- package/dist/sync/RequestTracker.js +19 -0
- package/dist/sync/RequestTracker.js.map +1 -0
- package/dist/sync/sync.d.ts +2 -0
- package/dist/sync/sync.js +51 -18
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/util.d.ts +2 -1
- package/dist/sync/util.js +2 -3
- package/dist/sync/util.js.map +1 -1
- package/package.json +4 -4
- package/src/entry/cli-entry.ts +2 -1
- package/src/entry/commands/compact-action.ts +55 -0
- package/src/entry/entry-index.ts +1 -0
- package/src/routes/endpoints/socket-route.ts +9 -1
- package/src/routes/endpoints/sync-stream.ts +10 -1
- package/src/storage/BucketStorage.ts +29 -1
- package/src/storage/mongo/MongoCompactor.ts +356 -0
- package/src/storage/mongo/MongoSyncBucketStorage.ts +25 -14
- package/src/storage/mongo/models.ts +5 -4
- package/src/storage/mongo/util.ts +25 -0
- package/src/sync/RequestTracker.ts +21 -0
- package/src/sync/sync.ts +61 -17
- package/src/sync/util.ts +6 -2
- package/test/src/__snapshots__/sync.test.ts.snap +85 -0
- package/test/src/bucket_validation.test.ts +142 -0
- package/test/src/bucket_validation.ts +116 -0
- package/test/src/compacting.test.ts +207 -0
- package/test/src/data_storage.test.ts +19 -60
- package/test/src/slow_tests.test.ts +144 -102
- package/test/src/sync.test.ts +176 -28
- package/test/src/util.ts +65 -1
- package/test/src/wal_stream_utils.ts +13 -4
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @powersync/service-core
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1c1a3bf: Implement a compact command
|
|
8
|
+
|
|
9
|
+
## 0.4.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- bdbf95c: Log user_id and sync stats for each connection
|
|
14
|
+
- Updated dependencies [876f4a0]
|
|
15
|
+
- Updated dependencies [9bff878]
|
|
16
|
+
- @powersync/service-sync-rules@0.18.1
|
|
17
|
+
- @powersync/service-rsocket-router@0.0.10
|
|
18
|
+
|
|
3
19
|
## 0.4.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/entry/cli-entry.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import { registerMigrationAction } from './commands/migrate-action.js';
|
|
3
3
|
import { registerTearDownAction } from './commands/teardown-action.js';
|
|
4
|
-
import { registerStartAction } from './entry-index.js';
|
|
4
|
+
import { registerCompactAction, registerStartAction } from './entry-index.js';
|
|
5
5
|
import { logger } from '@powersync/lib-services-framework';
|
|
6
6
|
/**
|
|
7
7
|
* Generates a Commander program which serves as the entry point
|
|
@@ -14,6 +14,7 @@ export function generateEntryProgram(startHandlers) {
|
|
|
14
14
|
entryProgram.name('powersync-runner').description('CLI to initiate a PowerSync service runner');
|
|
15
15
|
registerTearDownAction(entryProgram);
|
|
16
16
|
registerMigrationAction(entryProgram);
|
|
17
|
+
registerCompactAction(entryProgram);
|
|
17
18
|
if (startHandlers) {
|
|
18
19
|
registerStartAction(entryProgram, startHandlers);
|
|
19
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-entry.js","sourceRoot":"","sources":["../../src/entry/cli-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli-entry.js","sourceRoot":"","sources":["../../src/entry/cli-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,aAAyD;IAC5F,MAAM,YAAY,GAAG,IAAI,OAAO,EAAE,CAAC;IACnC,YAAY,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC,WAAW,CAAC,4CAA4C,CAAC,CAAC;IAEhG,sBAAsB,CAAC,YAAY,CAAC,CAAC;IACrC,uBAAuB,CAAC,YAAY,CAAC,CAAC;IACtC,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAEpC,IAAI,aAAa,EAAE;QACjB,mBAAmB,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC;KAClD;IAED,OAAO;QACL,OAAO,EAAE,YAAY;QACrB;;WAEG;QACH,OAAO,EAAE,KAAK,UAAU,UAAU;YAChC,IAAI;gBACF,MAAM,YAAY,CAAC,UAAU,EAAE,CAAC;aACjC;YAAC,OAAO,CAAC,EAAE;gBACV,MAAM,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;gBAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;aACjB;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import * as v8 from 'v8';
|
|
3
|
+
import { mongo } from '../../db/db-index.js';
|
|
4
|
+
import { MongoBucketStorage, PowerSyncMongo } from '../../storage/storage-index.js';
|
|
5
|
+
import { loadConfig } from '../../util/config.js';
|
|
6
|
+
import { extractRunnerOptions, wrapConfigCommand } from './config-command.js';
|
|
7
|
+
const COMMAND_NAME = 'compact';
|
|
8
|
+
/**
|
|
9
|
+
* Approximately max-old-space-size + 64MB.
|
|
10
|
+
*/
|
|
11
|
+
const HEAP_LIMIT = v8.getHeapStatistics().heap_size_limit;
|
|
12
|
+
/**
|
|
13
|
+
* Subtract 128MB for process overhead.
|
|
14
|
+
*
|
|
15
|
+
* Limit to 1024MB overall.
|
|
16
|
+
*/
|
|
17
|
+
const COMPACT_MEMORY_LIMIT_MB = Math.min(HEAP_LIMIT / 1024 / 1024 - 128, 1024);
|
|
18
|
+
export function registerCompactAction(program) {
|
|
19
|
+
const compactCommand = program.command(COMMAND_NAME);
|
|
20
|
+
wrapConfigCommand(compactCommand);
|
|
21
|
+
return compactCommand.description('Compact storage').action(async (options) => {
|
|
22
|
+
const runnerConfig = extractRunnerOptions(options);
|
|
23
|
+
const config = await loadConfig(runnerConfig);
|
|
24
|
+
const { storage } = config;
|
|
25
|
+
const client = mongo.createMongoClient(storage);
|
|
26
|
+
await client.connect();
|
|
27
|
+
try {
|
|
28
|
+
const psdb = new PowerSyncMongo(client);
|
|
29
|
+
const bucketStorage = new MongoBucketStorage(psdb, { slot_name_prefix: config.slot_name_prefix });
|
|
30
|
+
const active = await bucketStorage.getActiveSyncRules();
|
|
31
|
+
if (active == null) {
|
|
32
|
+
logger.info('No active instance to compact');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const p = bucketStorage.getInstance(active);
|
|
36
|
+
await p.compact({ memoryLimitMB: COMPACT_MEMORY_LIMIT_MB });
|
|
37
|
+
logger.info('done');
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
logger.error(`Failed to compact: ${e.toString()}`);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
await client.close();
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=compact-action.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact-action.js","sourceRoot":"","sources":["../../../src/entry/commands/compact-action.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACpF,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE9E,MAAM,YAAY,GAAG,SAAS,CAAC;AAE/B;;GAEG;AACH,MAAM,UAAU,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC,eAAe,CAAC;AAE1D;;;;GAIG;AACH,MAAM,uBAAuB,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC;AAE/E,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAErD,iBAAiB,CAAC,cAAc,CAAC,CAAC;IAElC,OAAO,cAAc,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5E,MAAM,YAAY,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAEnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC;QAC9C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;QAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAChD,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QACvB,IAAI;YACF,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;YACxC,MAAM,aAAa,GAAG,IAAI,kBAAkB,CAAC,IAAI,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;YAClG,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,kBAAkB,EAAE,CAAC;YACxD,IAAI,MAAM,IAAI,IAAI,EAAE;gBAClB,MAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;gBAC7C,OAAO;aACR;YACD,MAAM,CAAC,GAAG,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAC5D,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACrB;QAAC,OAAO,CAAC,EAAE;YACV,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACjB;gBAAS;YACR,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SACjB;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -3,4 +3,5 @@ export * from './commands/config-command.js';
|
|
|
3
3
|
export * from './commands/migrate-action.js';
|
|
4
4
|
export * from './commands/start-action.js';
|
|
5
5
|
export * from './commands/teardown-action.js';
|
|
6
|
+
export * from './commands/compact-action.js';
|
|
6
7
|
//# sourceMappingURL=entry-index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry-index.js","sourceRoot":"","sources":["../../src/entry/entry-index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC"}
|
|
1
|
+
{"version":3,"file":"entry-index.js","sourceRoot":"","sources":["../../src/entry/entry-index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC"}
|
|
@@ -5,6 +5,7 @@ import { Metrics } from '../../metrics/Metrics.js';
|
|
|
5
5
|
import { streamResponse } from '../../sync/sync.js';
|
|
6
6
|
import * as util from '../../util/util-index.js';
|
|
7
7
|
import { SyncRoutes } from './sync-stream.js';
|
|
8
|
+
import { RequestTracker } from '../../sync/RequestTracker.js';
|
|
8
9
|
export const syncStreamReactive = (router) => router.reactiveStream(SyncRoutes.STREAM, {
|
|
9
10
|
authorize: ({ context }) => {
|
|
10
11
|
return {
|
|
@@ -51,6 +52,7 @@ export const syncStreamReactive = (router) => router.reactiveStream(SyncRoutes.S
|
|
|
51
52
|
observer.triggerCancel();
|
|
52
53
|
});
|
|
53
54
|
Metrics.getInstance().concurrent_connections.add(1);
|
|
55
|
+
const tracker = new RequestTracker();
|
|
54
56
|
try {
|
|
55
57
|
for await (const data of streamResponse({
|
|
56
58
|
storage,
|
|
@@ -64,6 +66,7 @@ export const syncStreamReactive = (router) => router.reactiveStream(SyncRoutes.S
|
|
|
64
66
|
// RSocket handles keepalive events by default
|
|
65
67
|
keep_alive: false
|
|
66
68
|
},
|
|
69
|
+
tracker,
|
|
67
70
|
signal: controller.signal
|
|
68
71
|
})) {
|
|
69
72
|
if (data == null) {
|
|
@@ -79,7 +82,7 @@ export const syncStreamReactive = (router) => router.reactiveStream(SyncRoutes.S
|
|
|
79
82
|
const serialized = serialize(data);
|
|
80
83
|
responder.onNext({ data: serialized }, false);
|
|
81
84
|
requestedN--;
|
|
82
|
-
|
|
85
|
+
tracker.addDataSynced(serialized.length);
|
|
83
86
|
}
|
|
84
87
|
if (requestedN <= 0) {
|
|
85
88
|
await new Promise((resolve) => {
|
|
@@ -112,6 +115,11 @@ export const syncStreamReactive = (router) => router.reactiveStream(SyncRoutes.S
|
|
|
112
115
|
responder.onComplete();
|
|
113
116
|
removeStopHandler();
|
|
114
117
|
disposer();
|
|
118
|
+
logger.info(`Sync stream complete`, {
|
|
119
|
+
user_id: syncParams.user_id,
|
|
120
|
+
operations_synced: tracker.operationsSynced,
|
|
121
|
+
data_synced_bytes: tracker.dataSyncedBytes
|
|
122
|
+
});
|
|
115
123
|
Metrics.getInstance().concurrent_connections.add(-1);
|
|
116
124
|
}
|
|
117
125
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-route.js","sourceRoot":"","sources":["../../../src/routes/endpoints/socket-route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AAEjC,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,KAAK,IAAI,MAAM,0BAA0B,CAAC;AAEjD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"socket-route.js","sourceRoot":"","sources":["../../../src/routes/endpoints/socket-route.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import * as util from '../../util/util-index.js';
|
|
|
6
6
|
import { Metrics } from '../../metrics/Metrics.js';
|
|
7
7
|
import { authUser } from '../auth.js';
|
|
8
8
|
import { routeDefinition } from '../router.js';
|
|
9
|
+
import { RequestTracker } from '../../sync/RequestTracker.js';
|
|
9
10
|
export var SyncRoutes;
|
|
10
11
|
(function (SyncRoutes) {
|
|
11
12
|
SyncRoutes["STREAM"] = "/sync/stream";
|
|
@@ -37,6 +38,7 @@ export const syncStreamed = routeDefinition({
|
|
|
37
38
|
});
|
|
38
39
|
}
|
|
39
40
|
const controller = new AbortController();
|
|
41
|
+
const tracker = new RequestTracker();
|
|
40
42
|
try {
|
|
41
43
|
Metrics.getInstance().concurrent_connections.add(1);
|
|
42
44
|
const stream = Readable.from(sync.transformToBytesTracked(sync.ndjson(sync.streamResponse({
|
|
@@ -44,8 +46,9 @@ export const syncStreamed = routeDefinition({
|
|
|
44
46
|
params,
|
|
45
47
|
syncParams,
|
|
46
48
|
token: payload.context.token_payload,
|
|
49
|
+
tracker,
|
|
47
50
|
signal: controller.signal
|
|
48
|
-
}))), { objectMode: false, highWaterMark: 16 * 1024 });
|
|
51
|
+
})), tracker), { objectMode: false, highWaterMark: 16 * 1024 });
|
|
49
52
|
const deregister = system.addStopHandler(() => {
|
|
50
53
|
// This error is not currently propagated to the client
|
|
51
54
|
controller.abort();
|
|
@@ -70,6 +73,11 @@ export const syncStreamed = routeDefinition({
|
|
|
70
73
|
afterSend: async () => {
|
|
71
74
|
controller.abort();
|
|
72
75
|
Metrics.getInstance().concurrent_connections.add(-1);
|
|
76
|
+
logger.info(`Sync stream complete`, {
|
|
77
|
+
user_id: syncParams.user_id,
|
|
78
|
+
operations_synced: tracker.operationsSynced,
|
|
79
|
+
data_synced_bytes: tracker.dataSyncedBytes
|
|
80
|
+
});
|
|
73
81
|
}
|
|
74
82
|
});
|
|
75
83
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-stream.js","sourceRoot":"","sources":["../../../src/routes/endpoints/sync-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,KAAK,IAAI,MAAM,0BAA0B,CAAC;AACjD,OAAO,KAAK,IAAI,MAAM,0BAA0B,CAAC;AAEjD,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"sync-stream.js","sourceRoot":"","sources":["../../../src/routes/endpoints/sync-stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,OAAO,KAAK,IAAI,MAAM,0BAA0B,CAAC;AACjD,OAAO,KAAK,IAAI,MAAM,0BAA0B,CAAC;AAEjD,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,CAAN,IAAY,UAEX;AAFD,WAAY,UAAU;IACpB,qCAAuB,CAAA;AACzB,CAAC,EAFW,UAAU,KAAV,UAAU,QAErB;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,eAAe,CAAC;IAC1C,IAAI,EAAE,UAAU,CAAC,MAAM;IACvB,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI;IAC9B,SAAS,EAAE,QAAQ;IACnB,SAAS,EAAE,MAAM,CAAC,sBAAsB,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;IAC9F,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;QAEtC,IAAI,MAAM,CAAC,MAAM,EAAE;YACjB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC;gBAC5B,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE,qBAAqB;gBAC3B,WAAW,EAAE,iCAAiC;aAC/C,CAAC,CAAC;SACJ;QAED,MAAM,MAAM,GAA8B,OAAO,CAAC,MAAM,CAAC;QACzD,MAAM,UAAU,GAAG,IAAI,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,aAAc,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QAE1G,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC/B,0CAA0C;QAC1C,MAAM,EAAE,GAAG,MAAM,OAAO,CAAC,mBAAmB,EAAE,CAAC;QAC/C,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,EAAE;YACtB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC;gBAC5B,MAAM,EAAE,GAAG;gBACX,IAAI,EAAE,eAAe;gBACrB,WAAW,EAAE,yBAAyB;aACvC,CAAC,CAAC;SACJ;QACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;QACrC,IAAI;YACF,OAAO,CAAC,WAAW,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAC1B,IAAI,CAAC,uBAAuB,CAC1B,IAAI,CAAC,MAAM,CACT,IAAI,CAAC,cAAc,CAAC;gBAClB,OAAO;gBACP,MAAM;gBACN,UAAU;gBACV,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,aAAc;gBACrC,OAAO;gBACP,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CACH,EACD,OAAO,CACR,EACD,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,GAAG,IAAI,EAAE,CAChD,CAAC;YAEF,MAAM,UAAU,GAAG,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE;gBAC5C,uDAAuD;gBACvD,UAAU,CAAC,KAAK,EAAE,CAAC;gBACnB,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;gBACtB,UAAU,EAAE,CAAC;YACf,CAAC,CAAC,CAAC;YAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC3B,UAAU,CAAC,KAAK,EAAE,CAAC;gBACnB,oDAAoD;gBACpD,IAAI,KAAK,CAAC,OAAO,IAAI,sBAAsB,EAAE;oBAC3C,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,CAAC,CAAC;iBACtD;YACH,CAAC,CAAC,CAAC;YAEH,OAAO,IAAI,MAAM,CAAC,cAAc,CAAC;gBAC/B,MAAM,EAAE,GAAG;gBACX,OAAO,EAAE;oBACP,cAAc,EAAE,sBAAsB;iBACvC;gBACD,IAAI,EAAE,MAAM;gBACZ,SAAS,EAAE,KAAK,IAAI,EAAE;oBACpB,UAAU,CAAC,KAAK,EAAE,CAAC;oBACnB,OAAO,CAAC,WAAW,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;oBACrD,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE;wBAClC,OAAO,EAAE,UAAU,CAAC,OAAO;wBAC3B,iBAAiB,EAAE,OAAO,CAAC,gBAAgB;wBAC3C,iBAAiB,EAAE,OAAO,CAAC,eAAe;qBAC3C,CAAC,CAAC;gBACL,CAAC;aACF,CAAC,CAAC;SACJ;QAAC,OAAO,EAAE,EAAE;YACX,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,OAAO,CAAC,WAAW,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SACtD;IACH,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,YAAY,CAAC,CAAC"}
|
|
@@ -175,7 +175,7 @@ export interface SyncRulesBucketStorage {
|
|
|
175
175
|
* @param dataBuckets current bucket states
|
|
176
176
|
* @param options batch size options
|
|
177
177
|
*/
|
|
178
|
-
getBucketDataBatch(checkpoint: util.OpId, dataBuckets: Map<string, string>, options?: BucketDataBatchOptions): AsyncIterable<
|
|
178
|
+
getBucketDataBatch(checkpoint: util.OpId, dataBuckets: Map<string, string>, options?: BucketDataBatchOptions): AsyncIterable<SyncBucketDataBatch>;
|
|
179
179
|
/**
|
|
180
180
|
* Compute checksums for a given list of buckets.
|
|
181
181
|
*
|
|
@@ -206,6 +206,7 @@ export interface SyncRulesBucketStorage {
|
|
|
206
206
|
* Errors are cleared on commit.
|
|
207
207
|
*/
|
|
208
208
|
reportError(e: any): Promise<void>;
|
|
209
|
+
compact(options?: CompactOptions): Promise<void>;
|
|
209
210
|
}
|
|
210
211
|
export interface SyncRuleStatus {
|
|
211
212
|
checkpoint_lsn: string | null;
|
|
@@ -307,4 +308,27 @@ export interface SaveDelete {
|
|
|
307
308
|
before: SqliteRow;
|
|
308
309
|
after?: undefined;
|
|
309
310
|
}
|
|
311
|
+
export interface SyncBucketDataBatch {
|
|
312
|
+
batch: util.SyncBucketData;
|
|
313
|
+
targetOp: bigint | null;
|
|
314
|
+
}
|
|
310
315
|
export declare function mergeToast(record: ToastableSqliteRow, persisted: ToastableSqliteRow): ToastableSqliteRow;
|
|
316
|
+
export interface CompactOptions {
|
|
317
|
+
/**
|
|
318
|
+
* Heap memory limit for the compact process.
|
|
319
|
+
*
|
|
320
|
+
* Add around 64MB to this to determine the "--max-old-space-size" argument.
|
|
321
|
+
* Add another 80MB to get RSS usage / memory limits.
|
|
322
|
+
*/
|
|
323
|
+
memoryLimitMB?: number;
|
|
324
|
+
/**
|
|
325
|
+
* If specified, ignore any operations newer than this when compacting.
|
|
326
|
+
*
|
|
327
|
+
* This is primarily for tests, where we want to test compacting at a specific
|
|
328
|
+
* point.
|
|
329
|
+
*
|
|
330
|
+
* This can also be used to create a "safe buffer" of recent operations that should
|
|
331
|
+
* not be compacted, to avoid invalidating checkpoints in use.
|
|
332
|
+
*/
|
|
333
|
+
maxOpId?: bigint;
|
|
334
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BucketStorage.js","sourceRoot":"","sources":["../../src/storage/BucketStorage.ts"],"names":[],"mappings":"AA+JA,MAAM,OAAO,yBAAyB;IAGpC,YAA4B,EAAU,EAAkB,UAAwB,EAAE,cAA6B;QAAnF,OAAE,GAAF,EAAE,CAAQ;QAAkB,eAAU,GAAV,UAAU,CAAc;QAC9E,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED,IAAI,SAAS;QACX,OAAO,aAAa,IAAI,CAAC,EAAE,EAAE,CAAC;IAChC,CAAC;CACF;AAYD,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"BucketStorage.js","sourceRoot":"","sources":["../../src/storage/BucketStorage.ts"],"names":[],"mappings":"AA+JA,MAAM,OAAO,yBAAyB;IAGpC,YAA4B,EAAU,EAAkB,UAAwB,EAAE,cAA6B;QAAnF,OAAE,GAAF,EAAE,CAAQ;QAAkB,eAAU,GAAV,UAAU,CAAc;QAC9E,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;IACvC,CAAC;IAED,IAAI,SAAS;QACX,OAAO,aAAa,IAAI,CAAC,EAAE,EAAE,CAAC;IAChC,CAAC;CACF;AAYD,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,kCAAkC,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAuNlE,MAAM,UAAU,UAAU,CAAC,MAA0B,EAAE,SAA6B;IAClF,MAAM,SAAS,GAAuB,EAAE,CAAC;IACzC,KAAK,IAAI,GAAG,IAAI,MAAM,EAAE;QACtB,IAAI,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,WAAW,EAAE;YACrC,SAAS,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;SACjC;aAAM;YACL,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;SAC9B;KACF;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PowerSyncMongo } from './db.js';
|
|
2
|
+
import { CompactOptions } from '../BucketStorage.js';
|
|
3
|
+
/**
|
|
4
|
+
* Additional options, primarily for testing.
|
|
5
|
+
*/
|
|
6
|
+
export interface MongoCompactOptions extends CompactOptions {
|
|
7
|
+
/** Minimum of 2 */
|
|
8
|
+
clearBatchLimit?: number;
|
|
9
|
+
/** Minimum of 1 */
|
|
10
|
+
moveBatchLimit?: number;
|
|
11
|
+
/** Minimum of 1 */
|
|
12
|
+
moveBatchQueryLimit?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare class MongoCompactor {
|
|
15
|
+
private db;
|
|
16
|
+
private group_id;
|
|
17
|
+
private updates;
|
|
18
|
+
private idLimitBytes;
|
|
19
|
+
private moveBatchLimit;
|
|
20
|
+
private moveBatchQueryLimit;
|
|
21
|
+
private clearBatchLimit;
|
|
22
|
+
private maxOpId;
|
|
23
|
+
constructor(db: PowerSyncMongo, group_id: number, options?: MongoCompactOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Compact buckets by converting operations into MOVE and/or CLEAR operations.
|
|
26
|
+
*
|
|
27
|
+
* See /docs/compacting-operations.md for details.
|
|
28
|
+
*/
|
|
29
|
+
compact(): Promise<void>;
|
|
30
|
+
private flush;
|
|
31
|
+
/**
|
|
32
|
+
* Perform a CLEAR compact for a bucket.
|
|
33
|
+
*
|
|
34
|
+
* @param bucket bucket name
|
|
35
|
+
* @param op op_id of the last non-PUT operation, which will be converted to CLEAR.
|
|
36
|
+
*/
|
|
37
|
+
private clearBucket;
|
|
38
|
+
}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { logger } from '@powersync/lib-services-framework';
|
|
2
|
+
import { MaxKey, MinKey } from 'mongodb';
|
|
3
|
+
import { addChecksums } from '../../util/utils.js';
|
|
4
|
+
const DEFAULT_CLEAR_BATCH_LIMIT = 5000;
|
|
5
|
+
const DEFAULT_MOVE_BATCH_LIMIT = 2000;
|
|
6
|
+
const DEFAULT_MOVE_BATCH_QUERY_LIMIT = 10000;
|
|
7
|
+
/** This default is primarily for tests. */
|
|
8
|
+
const DEFAULT_MEMORY_LIMIT_MB = 64;
|
|
9
|
+
export class MongoCompactor {
|
|
10
|
+
constructor(db, group_id, options) {
|
|
11
|
+
this.db = db;
|
|
12
|
+
this.group_id = group_id;
|
|
13
|
+
this.updates = [];
|
|
14
|
+
this.idLimitBytes = (options?.memoryLimitMB ?? DEFAULT_MEMORY_LIMIT_MB) * 1024 * 1024;
|
|
15
|
+
this.moveBatchLimit = options?.moveBatchLimit ?? DEFAULT_MOVE_BATCH_LIMIT;
|
|
16
|
+
this.moveBatchQueryLimit = options?.moveBatchQueryLimit ?? DEFAULT_MOVE_BATCH_QUERY_LIMIT;
|
|
17
|
+
this.clearBatchLimit = options?.clearBatchLimit ?? DEFAULT_CLEAR_BATCH_LIMIT;
|
|
18
|
+
this.maxOpId = options?.maxOpId;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compact buckets by converting operations into MOVE and/or CLEAR operations.
|
|
22
|
+
*
|
|
23
|
+
* See /docs/compacting-operations.md for details.
|
|
24
|
+
*/
|
|
25
|
+
async compact() {
|
|
26
|
+
const idLimitBytes = this.idLimitBytes;
|
|
27
|
+
let currentState = null;
|
|
28
|
+
// Constant lower bound
|
|
29
|
+
const lowerBound = {
|
|
30
|
+
g: this.group_id,
|
|
31
|
+
b: new MinKey(),
|
|
32
|
+
o: new MinKey()
|
|
33
|
+
};
|
|
34
|
+
// Upper bound is adjusted for each batch
|
|
35
|
+
let upperBound = {
|
|
36
|
+
g: this.group_id,
|
|
37
|
+
b: new MaxKey(),
|
|
38
|
+
o: new MaxKey()
|
|
39
|
+
};
|
|
40
|
+
while (true) {
|
|
41
|
+
// Query one batch at a time, to avoid cursor timeouts
|
|
42
|
+
const batch = await this.db.bucket_data
|
|
43
|
+
.find({
|
|
44
|
+
_id: {
|
|
45
|
+
$gte: lowerBound,
|
|
46
|
+
$lt: upperBound
|
|
47
|
+
}
|
|
48
|
+
}, {
|
|
49
|
+
projection: {
|
|
50
|
+
_id: 1,
|
|
51
|
+
op: 1,
|
|
52
|
+
table: 1,
|
|
53
|
+
row_id: 1,
|
|
54
|
+
source_table: 1,
|
|
55
|
+
source_key: 1
|
|
56
|
+
},
|
|
57
|
+
limit: this.moveBatchQueryLimit,
|
|
58
|
+
sort: { _id: -1 },
|
|
59
|
+
singleBatch: true
|
|
60
|
+
})
|
|
61
|
+
.toArray();
|
|
62
|
+
if (batch.length == 0) {
|
|
63
|
+
// We've reached the end
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
// Set upperBound for the next batch
|
|
67
|
+
upperBound = batch[batch.length - 1]._id;
|
|
68
|
+
for (let doc of batch) {
|
|
69
|
+
if (currentState == null || doc._id.b != currentState.bucket) {
|
|
70
|
+
if (currentState != null && currentState.lastNotPut != null && currentState.opsSincePut >= 1) {
|
|
71
|
+
// Important to flush before clearBucket()
|
|
72
|
+
await this.flush();
|
|
73
|
+
logger.info(`Inserting CLEAR at ${this.group_id}:${currentState.bucket}:${currentState.lastNotPut} to remove ${currentState.opsSincePut} operations`);
|
|
74
|
+
const bucket = currentState.bucket;
|
|
75
|
+
const clearOp = currentState.lastNotPut;
|
|
76
|
+
// Free memory before clearing bucket
|
|
77
|
+
currentState = null;
|
|
78
|
+
await this.clearBucket(bucket, clearOp);
|
|
79
|
+
}
|
|
80
|
+
currentState = {
|
|
81
|
+
bucket: doc._id.b,
|
|
82
|
+
seen: new Map(),
|
|
83
|
+
trackingSize: 0,
|
|
84
|
+
lastNotPut: null,
|
|
85
|
+
opsSincePut: 0
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (this.maxOpId != null && doc._id.o > this.maxOpId) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
let isPersistentPut = doc.op == 'PUT';
|
|
92
|
+
if (doc.op == 'REMOVE' || doc.op == 'PUT') {
|
|
93
|
+
const key = `${doc.table}/${doc.row_id}/${doc.source_table}/${doc.source_key?.toHexString()}`;
|
|
94
|
+
const targetOp = currentState.seen.get(key);
|
|
95
|
+
if (targetOp) {
|
|
96
|
+
// Will convert to MOVE, so don't count as PUT
|
|
97
|
+
isPersistentPut = false;
|
|
98
|
+
this.updates.push({
|
|
99
|
+
updateOne: {
|
|
100
|
+
filter: {
|
|
101
|
+
_id: doc._id
|
|
102
|
+
},
|
|
103
|
+
update: {
|
|
104
|
+
$set: {
|
|
105
|
+
op: 'MOVE',
|
|
106
|
+
target_op: targetOp
|
|
107
|
+
},
|
|
108
|
+
$unset: {
|
|
109
|
+
source_table: 1,
|
|
110
|
+
source_key: 1,
|
|
111
|
+
table: 1,
|
|
112
|
+
row_id: 1,
|
|
113
|
+
data: 1
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
if (currentState.trackingSize >= idLimitBytes) {
|
|
121
|
+
// Reached memory limit.
|
|
122
|
+
// Keep the highest seen values in this case.
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// flatstr reduces the memory usage by flattening the string
|
|
126
|
+
currentState.seen.set(flatstr(key), doc._id.o);
|
|
127
|
+
// length + 16 for the string
|
|
128
|
+
// 24 for the bigint
|
|
129
|
+
// 50 for map overhead
|
|
130
|
+
// 50 for additional overhead
|
|
131
|
+
currentState.trackingSize += key.length + 140;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (isPersistentPut) {
|
|
136
|
+
currentState.lastNotPut = null;
|
|
137
|
+
currentState.opsSincePut = 0;
|
|
138
|
+
}
|
|
139
|
+
else if (doc.op != 'CLEAR') {
|
|
140
|
+
if (currentState.lastNotPut == null) {
|
|
141
|
+
currentState.lastNotPut = doc._id.o;
|
|
142
|
+
}
|
|
143
|
+
currentState.opsSincePut += 1;
|
|
144
|
+
}
|
|
145
|
+
if (this.updates.length >= this.moveBatchLimit) {
|
|
146
|
+
await this.flush();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
await this.flush();
|
|
151
|
+
currentState?.seen.clear();
|
|
152
|
+
if (currentState?.lastNotPut != null && currentState?.opsSincePut > 1) {
|
|
153
|
+
logger.info(`Inserting CLEAR at ${this.group_id}:${currentState.bucket}:${currentState.lastNotPut} to remove ${currentState.opsSincePut} operations`);
|
|
154
|
+
const bucket = currentState.bucket;
|
|
155
|
+
const clearOp = currentState.lastNotPut;
|
|
156
|
+
// Free memory before clearing bucket
|
|
157
|
+
currentState = null;
|
|
158
|
+
await this.clearBucket(bucket, clearOp);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async flush() {
|
|
162
|
+
if (this.updates.length > 0) {
|
|
163
|
+
logger.info(`Compacting ${this.updates.length} ops`);
|
|
164
|
+
await this.db.bucket_data.bulkWrite(this.updates, {
|
|
165
|
+
// Order is not important.
|
|
166
|
+
// Since checksums are not affected, these operations can happen in any order,
|
|
167
|
+
// and it's fine if the operations are partially applied.
|
|
168
|
+
// Each individual operation is atomic.
|
|
169
|
+
ordered: false
|
|
170
|
+
});
|
|
171
|
+
this.updates = [];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Perform a CLEAR compact for a bucket.
|
|
176
|
+
*
|
|
177
|
+
* @param bucket bucket name
|
|
178
|
+
* @param op op_id of the last non-PUT operation, which will be converted to CLEAR.
|
|
179
|
+
*/
|
|
180
|
+
async clearBucket(bucket, op) {
|
|
181
|
+
const opFilter = {
|
|
182
|
+
_id: {
|
|
183
|
+
$gte: {
|
|
184
|
+
g: this.group_id,
|
|
185
|
+
b: bucket,
|
|
186
|
+
o: new MinKey()
|
|
187
|
+
},
|
|
188
|
+
$lte: {
|
|
189
|
+
g: this.group_id,
|
|
190
|
+
b: bucket,
|
|
191
|
+
o: op
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
const session = this.db.client.startSession();
|
|
196
|
+
try {
|
|
197
|
+
let done = false;
|
|
198
|
+
while (!done) {
|
|
199
|
+
// Do the CLEAR operation in batches, with each batch a separate transaction.
|
|
200
|
+
// The state after each batch is fully consistent.
|
|
201
|
+
// We need a transaction per batch to make sure checksums stay consistent.
|
|
202
|
+
await session.withTransaction(async () => {
|
|
203
|
+
const query = this.db.bucket_data.find(opFilter, {
|
|
204
|
+
session,
|
|
205
|
+
sort: { _id: 1 },
|
|
206
|
+
projection: {
|
|
207
|
+
_id: 1,
|
|
208
|
+
op: 1,
|
|
209
|
+
checksum: 1,
|
|
210
|
+
target_op: 1
|
|
211
|
+
},
|
|
212
|
+
limit: this.clearBatchLimit
|
|
213
|
+
});
|
|
214
|
+
let checksum = 0;
|
|
215
|
+
let lastOpId = null;
|
|
216
|
+
let targetOp = null;
|
|
217
|
+
let gotAnOp = false;
|
|
218
|
+
for await (let op of query.stream()) {
|
|
219
|
+
if (op.op == 'MOVE' || op.op == 'REMOVE' || op.op == 'CLEAR') {
|
|
220
|
+
checksum = addChecksums(checksum, op.checksum);
|
|
221
|
+
lastOpId = op._id;
|
|
222
|
+
if (op.op != 'CLEAR') {
|
|
223
|
+
gotAnOp = true;
|
|
224
|
+
}
|
|
225
|
+
if (op.target_op != null) {
|
|
226
|
+
if (targetOp == null || op.target_op > targetOp) {
|
|
227
|
+
targetOp = op.target_op;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
throw new Error(`Unexpected ${op.op} operation at ${op._id.g}:${op._id.b}:${op._id.o}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!gotAnOp) {
|
|
236
|
+
done = true;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
logger.info(`Flushing CLEAR at ${lastOpId?.o}`);
|
|
240
|
+
await this.db.bucket_data.deleteMany({
|
|
241
|
+
_id: {
|
|
242
|
+
$gte: {
|
|
243
|
+
g: this.group_id,
|
|
244
|
+
b: bucket,
|
|
245
|
+
o: new MinKey()
|
|
246
|
+
},
|
|
247
|
+
$lte: lastOpId
|
|
248
|
+
}
|
|
249
|
+
}, { session });
|
|
250
|
+
await this.db.bucket_data.insertOne({
|
|
251
|
+
_id: lastOpId,
|
|
252
|
+
op: 'CLEAR',
|
|
253
|
+
checksum: checksum,
|
|
254
|
+
data: null,
|
|
255
|
+
target_op: targetOp
|
|
256
|
+
}, { session });
|
|
257
|
+
}, {
|
|
258
|
+
writeConcern: { w: 'majority' },
|
|
259
|
+
readConcern: { level: 'snapshot' }
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
await session.endSession();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Flattens string to reduce memory usage (around 320 bytes -> 120 bytes),
|
|
270
|
+
* at the cost of some upfront CPU usage.
|
|
271
|
+
*
|
|
272
|
+
* From: https://github.com/davidmarkclements/flatstr/issues/8
|
|
273
|
+
*/
|
|
274
|
+
function flatstr(s) {
|
|
275
|
+
s.match(/\n/g);
|
|
276
|
+
return s;
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=MongoCompactor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MongoCompactor.js","sourceRoot":"","sources":["../../../src/storage/mongo/MongoCompactor.ts"],"names":[],"mappings":"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"}
|