@powersync/service-core 0.0.0-dev-20240718134716 → 0.0.0-dev-20240725112650
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 +11 -6
- 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 +48 -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/metrics/Metrics.d.ts +4 -3
- package/dist/metrics/Metrics.js +51 -0
- package/dist/metrics/Metrics.js.map +1 -1
- package/dist/replication/WalStream.js +6 -8
- package/dist/replication/WalStream.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +883 -0
- package/dist/routes/configure-fastify.js +58 -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 +46 -0
- package/dist/routes/configure-rsocket.js.map +1 -0
- package/dist/routes/endpoints/socket-route.js +6 -14
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +4 -5
- 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 +4 -4
- package/dist/routes/router-socket.js.map +1 -1
- package/dist/routes/router.d.ts +1 -0
- package/dist/routes/router.js.map +1 -1
- package/dist/routes/routes-index.d.ts +2 -0
- package/dist/routes/routes-index.js +2 -0
- package/dist/routes/routes-index.js.map +1 -1
- package/dist/storage/BucketStorage.d.ts +31 -1
- package/dist/storage/BucketStorage.js.map +1 -1
- package/dist/storage/mongo/MongoCompactor.d.ts +40 -0
- package/dist/storage/mongo/MongoCompactor.js +292 -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.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.js +20 -7
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/util.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 +3 -29
- package/dist/util/config/compound-config-collector.js +22 -69
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/package.json +6 -4
- package/src/entry/cli-entry.ts +2 -1
- package/src/entry/commands/compact-action.ts +54 -0
- package/src/entry/entry-index.ts +1 -0
- package/src/metrics/Metrics.ts +67 -2
- package/src/replication/WalStream.ts +6 -10
- package/src/routes/configure-fastify.ts +102 -0
- package/src/routes/configure-rsocket.ts +59 -0
- package/src/routes/endpoints/socket-route.ts +6 -15
- package/src/routes/endpoints/sync-stream.ts +4 -5
- package/src/routes/route-register.ts +2 -2
- package/src/routes/router-socket.ts +5 -5
- package/src/routes/router.ts +2 -0
- package/src/routes/routes-index.ts +2 -0
- package/src/storage/BucketStorage.ts +36 -1
- package/src/storage/mongo/MongoCompactor.ts +371 -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 +3 -3
- package/src/sync/sync-index.ts +1 -0
- package/src/sync/sync.ts +21 -7
- package/src/sync/util.ts +1 -0
- package/src/util/config/collectors/config-collector.ts +48 -0
- package/src/util/config/compound-config-collector.ts +23 -87
- 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 +169 -29
- package/test/src/util.ts +71 -13
- package/test/src/wal_stream.test.ts +21 -16
- package/test/src/wal_stream_utils.ts +13 -4
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -406,7 +406,7 @@ WHERE oid = $1::regclass`,
|
|
|
406
406
|
await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: record });
|
|
407
407
|
}
|
|
408
408
|
at += rows.length;
|
|
409
|
-
|
|
409
|
+
Metrics.getInstance().rows_replicated_total.add(rows.length);
|
|
410
410
|
|
|
411
411
|
await touch();
|
|
412
412
|
}
|
|
@@ -492,21 +492,19 @@ WHERE oid = $1::regclass`,
|
|
|
492
492
|
return null;
|
|
493
493
|
}
|
|
494
494
|
|
|
495
|
-
const metrics = container.getImplementation(Metrics);
|
|
496
|
-
|
|
497
495
|
if (msg.tag == 'insert') {
|
|
498
|
-
|
|
496
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
499
497
|
const baseRecord = util.constructAfterRecord(msg);
|
|
500
498
|
return await batch.save({ tag: 'insert', sourceTable: table, before: undefined, after: baseRecord });
|
|
501
499
|
} else if (msg.tag == 'update') {
|
|
502
|
-
|
|
500
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
503
501
|
// "before" may be null if the replica id columns are unchanged
|
|
504
502
|
// It's fine to treat that the same as an insert.
|
|
505
503
|
const before = util.constructBeforeRecord(msg);
|
|
506
504
|
const after = util.constructAfterRecord(msg);
|
|
507
505
|
return await batch.save({ tag: 'update', sourceTable: table, before: before, after: after });
|
|
508
506
|
} else if (msg.tag == 'delete') {
|
|
509
|
-
|
|
507
|
+
Metrics.getInstance().rows_replicated_total.add(1);
|
|
510
508
|
const before = util.constructBeforeRecord(msg)!;
|
|
511
509
|
|
|
512
510
|
return await batch.save({ tag: 'delete', sourceTable: table, before: before, after: undefined });
|
|
@@ -557,8 +555,6 @@ WHERE oid = $1::regclass`,
|
|
|
557
555
|
// Auto-activate as soon as initial replication is done
|
|
558
556
|
await this.storage.autoActivate();
|
|
559
557
|
|
|
560
|
-
const metrics = container.getImplementation(Metrics);
|
|
561
|
-
|
|
562
558
|
await this.storage.startBatch({}, async (batch) => {
|
|
563
559
|
// Replication never starts in the middle of a transaction
|
|
564
560
|
let inTx = false;
|
|
@@ -581,7 +577,7 @@ WHERE oid = $1::regclass`,
|
|
|
581
577
|
} else if (msg.tag == 'begin') {
|
|
582
578
|
inTx = true;
|
|
583
579
|
} else if (msg.tag == 'commit') {
|
|
584
|
-
|
|
580
|
+
Metrics.getInstance().transactions_replicated_total.add(1);
|
|
585
581
|
inTx = false;
|
|
586
582
|
await batch.commit(msg.lsn!);
|
|
587
583
|
await this.ack(msg.lsn!, replicationStream);
|
|
@@ -606,7 +602,7 @@ WHERE oid = $1::regclass`,
|
|
|
606
602
|
}
|
|
607
603
|
}
|
|
608
604
|
|
|
609
|
-
|
|
605
|
+
Metrics.getInstance().chunks_replicated_total.add(1);
|
|
610
606
|
}
|
|
611
607
|
});
|
|
612
608
|
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type fastify from 'fastify';
|
|
2
|
+
import { registerFastifyRoutes } from './route-register.js';
|
|
3
|
+
|
|
4
|
+
import * as system from '../system/system-index.js';
|
|
5
|
+
|
|
6
|
+
import { ADMIN_ROUTES } from './endpoints/admin.js';
|
|
7
|
+
import { CHECKPOINT_ROUTES } from './endpoints/checkpointing.js';
|
|
8
|
+
import { DEV_ROUTES } from './endpoints/dev.js';
|
|
9
|
+
import { SYNC_RULES_ROUTES } from './endpoints/sync-rules.js';
|
|
10
|
+
import { SYNC_STREAM_ROUTES } from './endpoints/sync-stream.js';
|
|
11
|
+
import { createRequestQueueHook, CreateRequestQueueParams } from './hooks.js';
|
|
12
|
+
import { RouteDefinition } from './router.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A list of route definitions to be registered as endpoints.
|
|
16
|
+
* Supplied concurrency limits will be applied to the grouped routes.
|
|
17
|
+
*/
|
|
18
|
+
export type RouteRegistrationOptions = {
|
|
19
|
+
routes: RouteDefinition[];
|
|
20
|
+
queueOptions: CreateRequestQueueParams;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* HTTP routes separated by API and Sync stream categories.
|
|
25
|
+
* This allows for separate concurrency limits.
|
|
26
|
+
*/
|
|
27
|
+
export type RouteDefinitions = {
|
|
28
|
+
api?: Partial<RouteRegistrationOptions>;
|
|
29
|
+
syncStream?: Partial<RouteRegistrationOptions>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type FastifyServerConfig = {
|
|
33
|
+
system: system.CorePowerSyncSystem;
|
|
34
|
+
routes?: RouteDefinitions;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const DEFAULT_ROUTE_OPTIONS = {
|
|
38
|
+
api: {
|
|
39
|
+
routes: [...ADMIN_ROUTES, ...CHECKPOINT_ROUTES, ...DEV_ROUTES, ...SYNC_RULES_ROUTES],
|
|
40
|
+
queueOptions: {
|
|
41
|
+
concurrency: 10,
|
|
42
|
+
max_queue_depth: 20
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
syncStream: {
|
|
46
|
+
routes: [...SYNC_STREAM_ROUTES],
|
|
47
|
+
queueOptions: {
|
|
48
|
+
concurrency: 200,
|
|
49
|
+
max_queue_depth: 0
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Registers default routes on a Fastify server. Consumers can optionally configure
|
|
56
|
+
* concurrency queue limits or override routes.
|
|
57
|
+
*/
|
|
58
|
+
export function configureFastifyServer(server: fastify.FastifyInstance, options: FastifyServerConfig) {
|
|
59
|
+
const { system, routes = DEFAULT_ROUTE_OPTIONS } = options;
|
|
60
|
+
/**
|
|
61
|
+
* Fastify creates an encapsulated context for each `.register` call.
|
|
62
|
+
* Creating a separate context here to separate the concurrency limits for Admin APIs
|
|
63
|
+
* and Sync Streaming routes.
|
|
64
|
+
* https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md
|
|
65
|
+
*/
|
|
66
|
+
server.register(async function (childContext) {
|
|
67
|
+
registerFastifyRoutes(
|
|
68
|
+
childContext,
|
|
69
|
+
async () => {
|
|
70
|
+
return {
|
|
71
|
+
user_id: undefined,
|
|
72
|
+
system: system
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
routes.api?.routes ?? DEFAULT_ROUTE_OPTIONS.api.routes
|
|
76
|
+
);
|
|
77
|
+
// Limit the active concurrent requests
|
|
78
|
+
childContext.addHook(
|
|
79
|
+
'onRequest',
|
|
80
|
+
createRequestQueueHook(routes.api?.queueOptions ?? DEFAULT_ROUTE_OPTIONS.api.queueOptions)
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Create a separate context for concurrency queueing
|
|
85
|
+
server.register(async function (childContext) {
|
|
86
|
+
registerFastifyRoutes(
|
|
87
|
+
childContext,
|
|
88
|
+
async () => {
|
|
89
|
+
return {
|
|
90
|
+
user_id: undefined,
|
|
91
|
+
system: system
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
routes.syncStream?.routes ?? DEFAULT_ROUTE_OPTIONS.syncStream.routes
|
|
95
|
+
);
|
|
96
|
+
// Limit the active concurrent requests
|
|
97
|
+
childContext.addHook(
|
|
98
|
+
'onRequest',
|
|
99
|
+
createRequestQueueHook(routes.syncStream?.queueOptions ?? DEFAULT_ROUTE_OPTIONS.syncStream.queueOptions)
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { deserialize } from 'bson';
|
|
2
|
+
import * as http from 'http';
|
|
3
|
+
|
|
4
|
+
import { errors, logger } from '@powersync/lib-services-framework';
|
|
5
|
+
import { ReactiveSocketRouter, RSocketRequestMeta } from '@powersync/service-rsocket-router';
|
|
6
|
+
|
|
7
|
+
import { CorePowerSyncSystem } from '../system/CorePowerSyncSystem.js';
|
|
8
|
+
import { generateContext, getTokenFromHeader } from './auth.js';
|
|
9
|
+
import { syncStreamReactive } from './endpoints/socket-route.js';
|
|
10
|
+
import { RSocketContextMeta, SocketRouteGenerator } from './router-socket.js';
|
|
11
|
+
import { Context } from './router.js';
|
|
12
|
+
|
|
13
|
+
export type RSockerRouterConfig = {
|
|
14
|
+
system: CorePowerSyncSystem;
|
|
15
|
+
server: http.Server;
|
|
16
|
+
routeGenerators?: SocketRouteGenerator[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_SOCKET_ROUTES = [syncStreamReactive];
|
|
20
|
+
|
|
21
|
+
export function configureRSocket(router: ReactiveSocketRouter<Context>, options: RSockerRouterConfig) {
|
|
22
|
+
const { routeGenerators = DEFAULT_SOCKET_ROUTES, server, system } = options;
|
|
23
|
+
|
|
24
|
+
router.applyWebSocketEndpoints(server, {
|
|
25
|
+
contextProvider: async (data: Buffer) => {
|
|
26
|
+
const { token } = RSocketContextMeta.decode(deserialize(data) as any);
|
|
27
|
+
|
|
28
|
+
if (!token) {
|
|
29
|
+
throw new errors.AuthorizationError('No token provided');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const extracted_token = getTokenFromHeader(token);
|
|
34
|
+
if (extracted_token != null) {
|
|
35
|
+
const { context, errors: token_errors } = await generateContext(system, extracted_token);
|
|
36
|
+
if (context?.token_payload == null) {
|
|
37
|
+
throw new errors.AuthorizationError(token_errors ?? 'Authentication required');
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
token,
|
|
41
|
+
...context,
|
|
42
|
+
token_errors: token_errors,
|
|
43
|
+
system
|
|
44
|
+
};
|
|
45
|
+
} else {
|
|
46
|
+
throw new errors.AuthorizationError('No token provided');
|
|
47
|
+
}
|
|
48
|
+
} catch (ex) {
|
|
49
|
+
logger.error(ex);
|
|
50
|
+
throw ex;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
endpoints: routeGenerators.map((generator) => generator(router)),
|
|
54
|
+
metaDecoder: async (meta: Buffer) => {
|
|
55
|
+
return RSocketRequestMeta.decode(deserialize(meta) as any);
|
|
56
|
+
},
|
|
57
|
+
payloadDecoder: async (rawData?: Buffer) => rawData && deserialize(rawData)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { errors, logger, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import { RequestParameters } from '@powersync/service-sync-rules';
|
|
3
3
|
import { serialize } from 'bson';
|
|
4
4
|
|
|
5
5
|
import { Metrics } from '../../metrics/Metrics.js';
|
|
6
|
-
import
|
|
6
|
+
import * as sync from '../../sync/sync-index.js';
|
|
7
7
|
import * as util from '../../util/util-index.js';
|
|
8
8
|
import { SocketRouteGenerator } from '../router-socket.js';
|
|
9
9
|
import { SyncRoutes } from './sync-stream.js';
|
|
10
|
-
import { RequestTracker } from '../../sync/RequestTracker.js';
|
|
11
10
|
|
|
12
11
|
export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
13
12
|
router.reactiveStream<util.StreamingSyncRequest, any>(SyncRoutes.STREAM, {
|
|
14
|
-
authorize: ({ context }) => {
|
|
15
|
-
return {
|
|
16
|
-
authorized: !!context.token_payload,
|
|
17
|
-
errors: ['Authentication required'].concat(context.token_errors ?? [])
|
|
18
|
-
};
|
|
19
|
-
},
|
|
20
13
|
validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
|
|
21
14
|
handler: async ({ context, params, responder, observer, initialN }) => {
|
|
22
15
|
const { system } = context;
|
|
@@ -66,12 +59,10 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
66
59
|
observer.triggerCancel();
|
|
67
60
|
});
|
|
68
61
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
metrics.concurrent_connections.add(1);
|
|
72
|
-
const tracker = new RequestTracker();
|
|
62
|
+
Metrics.getInstance().concurrent_connections.add(1);
|
|
63
|
+
const tracker = new sync.RequestTracker();
|
|
73
64
|
try {
|
|
74
|
-
for await (const data of streamResponse({
|
|
65
|
+
for await (const data of sync.streamResponse({
|
|
75
66
|
storage,
|
|
76
67
|
params: {
|
|
77
68
|
...params,
|
|
@@ -136,7 +127,7 @@ export const syncStreamReactive: SocketRouteGenerator = (router) =>
|
|
|
136
127
|
operations_synced: tracker.operationsSynced,
|
|
137
128
|
data_synced_bytes: tracker.dataSyncedBytes
|
|
138
129
|
});
|
|
139
|
-
|
|
130
|
+
Metrics.getInstance().concurrent_connections.add(-1);
|
|
140
131
|
}
|
|
141
132
|
}
|
|
142
133
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { errors, logger, router, schema } from '@powersync/lib-services-framework';
|
|
2
2
|
import { RequestParameters } from '@powersync/service-sync-rules';
|
|
3
3
|
import { Readable } from 'stream';
|
|
4
4
|
|
|
@@ -43,11 +43,10 @@ export const syncStreamed = routeDefinition({
|
|
|
43
43
|
description: 'No sync rules available'
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
const metrics = container.getImplementation(Metrics);
|
|
47
46
|
const controller = new AbortController();
|
|
48
47
|
const tracker = new RequestTracker();
|
|
49
48
|
try {
|
|
50
|
-
|
|
49
|
+
Metrics.getInstance().concurrent_connections.add(1);
|
|
51
50
|
const stream = Readable.from(
|
|
52
51
|
sync.transformToBytesTracked(
|
|
53
52
|
sync.ndjson(
|
|
@@ -90,7 +89,7 @@ export const syncStreamed = routeDefinition({
|
|
|
90
89
|
data: stream,
|
|
91
90
|
afterSend: async () => {
|
|
92
91
|
controller.abort();
|
|
93
|
-
|
|
92
|
+
Metrics.getInstance().concurrent_connections.add(-1);
|
|
94
93
|
logger.info(`Sync stream complete`, {
|
|
95
94
|
user_id: syncParams.user_id,
|
|
96
95
|
operations_synced: tracker.operationsSynced,
|
|
@@ -100,7 +99,7 @@ export const syncStreamed = routeDefinition({
|
|
|
100
99
|
});
|
|
101
100
|
} catch (ex) {
|
|
102
101
|
controller.abort();
|
|
103
|
-
|
|
102
|
+
Metrics.getInstance().concurrent_connections.add(-1);
|
|
104
103
|
}
|
|
105
104
|
}
|
|
106
105
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import fastify from 'fastify';
|
|
1
|
+
import type fastify from 'fastify';
|
|
2
2
|
|
|
3
|
-
import { errors,
|
|
3
|
+
import { errors, HTTPMethod, logger, router } from '@powersync/lib-services-framework';
|
|
4
4
|
import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
|
|
5
5
|
|
|
6
6
|
export type FastifyEndpoint<I, O, C> = RequestEndpoint<I, O, C> & {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { IReactiveStream, ReactiveSocketRouter } from '@powersync/service-rsocket-router';
|
|
1
2
|
import * as t from 'ts-codec';
|
|
2
|
-
import { ReactiveSocketRouter, IReactiveStream } from '@powersync/service-rsocket-router';
|
|
3
3
|
|
|
4
4
|
import { Context } from './router.js';
|
|
5
5
|
|
|
6
|
-
export const RSocketContextMeta = t.object({
|
|
7
|
-
token: t.string
|
|
8
|
-
});
|
|
9
|
-
|
|
10
6
|
/**
|
|
11
7
|
* Creates a socket route handler given a router instance
|
|
12
8
|
*/
|
|
13
9
|
export type SocketRouteGenerator = (router: ReactiveSocketRouter<Context>) => IReactiveStream;
|
|
10
|
+
|
|
11
|
+
export const RSocketContextMeta = t.object({
|
|
12
|
+
token: t.string
|
|
13
|
+
});
|
package/src/routes/router.ts
CHANGED
|
@@ -228,7 +228,7 @@ export interface SyncRulesBucketStorage {
|
|
|
228
228
|
checkpoint: util.OpId,
|
|
229
229
|
dataBuckets: Map<string, string>,
|
|
230
230
|
options?: BucketDataBatchOptions
|
|
231
|
-
): AsyncIterable<
|
|
231
|
+
): AsyncIterable<SyncBucketDataBatch>;
|
|
232
232
|
|
|
233
233
|
/**
|
|
234
234
|
* Compute checksums for a given list of buckets.
|
|
@@ -266,6 +266,8 @@ export interface SyncRulesBucketStorage {
|
|
|
266
266
|
* Errors are cleared on commit.
|
|
267
267
|
*/
|
|
268
268
|
reportError(e: any): Promise<void>;
|
|
269
|
+
|
|
270
|
+
compact(options?: CompactOptions): Promise<void>;
|
|
269
271
|
}
|
|
270
272
|
|
|
271
273
|
export interface SyncRuleStatus {
|
|
@@ -388,6 +390,11 @@ export interface SaveDelete {
|
|
|
388
390
|
after?: undefined;
|
|
389
391
|
}
|
|
390
392
|
|
|
393
|
+
export interface SyncBucketDataBatch {
|
|
394
|
+
batch: util.SyncBucketData;
|
|
395
|
+
targetOp: bigint | null;
|
|
396
|
+
}
|
|
397
|
+
|
|
391
398
|
export function mergeToast(record: ToastableSqliteRow, persisted: ToastableSqliteRow): ToastableSqliteRow {
|
|
392
399
|
const newRecord: ToastableSqliteRow = {};
|
|
393
400
|
for (let key in record) {
|
|
@@ -399,3 +406,31 @@ export function mergeToast(record: ToastableSqliteRow, persisted: ToastableSqlit
|
|
|
399
406
|
}
|
|
400
407
|
return newRecord;
|
|
401
408
|
}
|
|
409
|
+
|
|
410
|
+
export interface CompactOptions {
|
|
411
|
+
/**
|
|
412
|
+
* Heap memory limit for the compact process.
|
|
413
|
+
*
|
|
414
|
+
* Add around 64MB to this to determine the "--max-old-space-size" argument.
|
|
415
|
+
* Add another 80MB to get RSS usage / memory limits.
|
|
416
|
+
*/
|
|
417
|
+
memoryLimitMB?: number;
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* If specified, ignore any operations newer than this when compacting.
|
|
421
|
+
*
|
|
422
|
+
* This is primarily for tests, where we want to test compacting at a specific
|
|
423
|
+
* point.
|
|
424
|
+
*
|
|
425
|
+
* This can also be used to create a "safe buffer" of recent operations that should
|
|
426
|
+
* not be compacted, to avoid invalidating checkpoints in use.
|
|
427
|
+
*/
|
|
428
|
+
maxOpId?: bigint;
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* If specified, compact only the specific buckets.
|
|
432
|
+
*
|
|
433
|
+
* If not specified, compacts all buckets.
|
|
434
|
+
*/
|
|
435
|
+
compactBuckets?: string[];
|
|
436
|
+
}
|