@powersync/service-core 0.0.0-dev-20250507151436 → 0.0.0-dev-20250611110033
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 +47 -7
- package/dist/api/RouteAPI.d.ts +1 -5
- package/dist/api/diagnostics.js +1 -1
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/auth/CachedKeyCollector.js +2 -7
- package/dist/auth/CachedKeyCollector.js.map +1 -1
- package/dist/auth/CompoundKeyCollector.js.map +1 -1
- package/dist/auth/KeyCollector.d.ts +2 -2
- package/dist/auth/KeyStore.js +32 -14
- package/dist/auth/KeyStore.js.map +1 -1
- package/dist/auth/RemoteJWKSCollector.d.ts +1 -0
- package/dist/auth/RemoteJWKSCollector.js +39 -16
- package/dist/auth/RemoteJWKSCollector.js.map +1 -1
- package/dist/auth/auth-index.d.ts +1 -0
- package/dist/auth/auth-index.js +1 -0
- package/dist/auth/auth-index.js.map +1 -1
- package/dist/auth/utils.d.ts +6 -0
- package/dist/auth/utils.js +97 -0
- package/dist/auth/utils.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.d.ts +1 -1
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js.map +1 -1
- package/dist/replication/AbstractReplicationJob.d.ts +4 -0
- package/dist/replication/AbstractReplicationJob.js.map +1 -1
- package/dist/replication/AbstractReplicator.d.ts +23 -0
- package/dist/replication/AbstractReplicator.js +45 -0
- package/dist/replication/AbstractReplicator.js.map +1 -1
- package/dist/replication/RelationCache.d.ts +9 -0
- package/dist/replication/RelationCache.js +20 -0
- package/dist/replication/RelationCache.js.map +1 -0
- package/dist/replication/replication-index.d.ts +1 -0
- package/dist/replication/replication-index.js +1 -0
- package/dist/replication/replication-index.js.map +1 -1
- package/dist/replication/replication-metrics.js +6 -0
- package/dist/replication/replication-metrics.js.map +1 -1
- package/dist/routes/RouterEngine.js +1 -1
- package/dist/routes/RouterEngine.js.map +1 -1
- package/dist/routes/auth.d.ts +5 -16
- package/dist/routes/auth.js +6 -4
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +3 -21
- package/dist/routes/configure-fastify.js +3 -2
- package/dist/routes/configure-fastify.js.map +1 -1
- package/dist/routes/configure-rsocket.js +28 -11
- package/dist/routes/configure-rsocket.js.map +1 -1
- package/dist/routes/endpoints/admin.js +2 -0
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/checkpointing.d.ts +4 -28
- package/dist/routes/endpoints/socket-route.js +22 -8
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js +6 -6
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.d.ts +2 -14
- package/dist/routes/endpoints/sync-stream.js +28 -9
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/routes/route-register.js +10 -6
- package/dist/routes/route-register.js.map +1 -1
- package/dist/routes/router.d.ts +7 -3
- package/dist/routes/router.js.map +1 -1
- package/dist/storage/BucketStorageBatch.d.ts +17 -1
- package/dist/storage/BucketStorageBatch.js +2 -1
- package/dist/storage/BucketStorageBatch.js.map +1 -1
- package/dist/storage/PersistedSyncRulesContent.d.ts +5 -0
- package/dist/storage/SourceTable.d.ts +17 -1
- package/dist/storage/SourceTable.js +28 -0
- package/dist/storage/SourceTable.js.map +1 -1
- package/dist/storage/SyncRulesBucketStorage.d.ts +11 -2
- package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
- package/dist/storage/bson.js +4 -1
- package/dist/storage/bson.js.map +1 -1
- package/dist/sync/BucketChecksumState.d.ts +40 -18
- package/dist/sync/BucketChecksumState.js +122 -74
- package/dist/sync/BucketChecksumState.js.map +1 -1
- package/dist/sync/RequestTracker.d.ts +22 -1
- package/dist/sync/RequestTracker.js +51 -2
- package/dist/sync/RequestTracker.js.map +1 -1
- package/dist/sync/sync.d.ts +3 -5
- package/dist/sync/sync.js +49 -34
- package/dist/sync/sync.js.map +1 -1
- package/dist/util/config/collectors/config-collector.js +2 -5
- package/dist/util/config/collectors/config-collector.js.map +1 -1
- package/dist/util/protocol-types.d.ts +9 -9
- package/dist/util/protocol-types.js.map +1 -1
- package/dist/util/utils.d.ts +1 -1
- package/package.json +6 -7
- package/src/api/RouteAPI.ts +1 -6
- package/src/api/diagnostics.ts +1 -1
- package/src/auth/CachedKeyCollector.ts +4 -6
- package/src/auth/CompoundKeyCollector.ts +2 -1
- package/src/auth/KeyCollector.ts +2 -2
- package/src/auth/KeyStore.ts +45 -20
- package/src/auth/RemoteJWKSCollector.ts +39 -16
- package/src/auth/auth-index.ts +1 -0
- package/src/auth/utils.ts +102 -0
- package/src/index.ts +2 -0
- package/src/metrics/open-telemetry/OpenTelemetryMetricsFactory.ts +3 -3
- package/src/replication/AbstractReplicationJob.ts +5 -0
- package/src/replication/AbstractReplicator.ts +47 -0
- package/src/replication/RelationCache.ts +25 -0
- package/src/replication/replication-index.ts +1 -0
- package/src/replication/replication-metrics.ts +7 -0
- package/src/routes/RouterEngine.ts +1 -1
- package/src/routes/auth.ts +7 -6
- package/src/routes/configure-fastify.ts +6 -3
- package/src/routes/configure-rsocket.ts +33 -14
- package/src/routes/endpoints/admin.ts +2 -0
- package/src/routes/endpoints/socket-route.ts +24 -8
- package/src/routes/endpoints/sync-rules.ts +6 -6
- package/src/routes/endpoints/sync-stream.ts +31 -8
- package/src/routes/route-register.ts +10 -7
- package/src/routes/router.ts +9 -3
- package/src/storage/BucketStorageBatch.ts +22 -2
- package/src/storage/PersistedSyncRulesContent.ts +6 -0
- package/src/storage/SourceTable.ts +44 -1
- package/src/storage/SyncRulesBucketStorage.ts +14 -2
- package/src/storage/bson.ts +4 -1
- package/src/sync/BucketChecksumState.ts +162 -77
- package/src/sync/RequestTracker.ts +70 -3
- package/src/sync/sync.ts +72 -49
- package/src/util/config/collectors/config-collector.ts +3 -7
- package/src/util/protocol-types.ts +15 -10
- package/test/src/auth.test.ts +29 -11
- package/test/src/sync/BucketChecksumState.test.ts +32 -18
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -20,11 +20,19 @@ export const syncStreamed = routeDefinition({
|
|
|
20
20
|
authorize: authUser,
|
|
21
21
|
validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
|
|
22
22
|
handler: async (payload) => {
|
|
23
|
-
const { service_context } = payload.context;
|
|
23
|
+
const { service_context, logger } = payload.context;
|
|
24
24
|
const { routerEngine, storageEngine, metricsEngine, syncContext } = service_context;
|
|
25
25
|
const headers = payload.request.headers;
|
|
26
26
|
const userAgent = headers['x-user-agent'] ?? headers['user-agent'];
|
|
27
27
|
const clientId = payload.params.client_id;
|
|
28
|
+
const streamStart = Date.now();
|
|
29
|
+
|
|
30
|
+
logger.defaultMeta = {
|
|
31
|
+
...logger.defaultMeta,
|
|
32
|
+
user_agent: userAgent,
|
|
33
|
+
client_id: clientId,
|
|
34
|
+
user_id: payload.context.user_id
|
|
35
|
+
};
|
|
28
36
|
|
|
29
37
|
if (routerEngine.closed) {
|
|
30
38
|
throw new errors.ServiceError({
|
|
@@ -64,7 +72,8 @@ export const syncStreamed = routeDefinition({
|
|
|
64
72
|
syncParams,
|
|
65
73
|
token: payload.context.token_payload!,
|
|
66
74
|
tracker,
|
|
67
|
-
signal: controller.signal
|
|
75
|
+
signal: controller.signal,
|
|
76
|
+
logger
|
|
68
77
|
})
|
|
69
78
|
),
|
|
70
79
|
tracker
|
|
@@ -72,16 +81,29 @@ export const syncStreamed = routeDefinition({
|
|
|
72
81
|
{ objectMode: false, highWaterMark: 16 * 1024 }
|
|
73
82
|
);
|
|
74
83
|
|
|
84
|
+
// Best effort guess on why the stream was closed.
|
|
85
|
+
// We use the `??=` operator everywhere, so that we catch the first relevant
|
|
86
|
+
// event, which is usually the most specific.
|
|
87
|
+
let closeReason: string | undefined = undefined;
|
|
88
|
+
|
|
75
89
|
const deregister = routerEngine.addStopHandler(() => {
|
|
76
90
|
// This error is not currently propagated to the client
|
|
77
91
|
controller.abort();
|
|
92
|
+
closeReason ??= 'process shutdown';
|
|
78
93
|
stream.destroy(new Error('Shutting down system'));
|
|
79
94
|
});
|
|
95
|
+
|
|
96
|
+
stream.on('end', () => {
|
|
97
|
+
// Auth failure or switch to new sync rules
|
|
98
|
+
closeReason ??= 'service closing stream';
|
|
99
|
+
});
|
|
100
|
+
|
|
80
101
|
stream.on('close', () => {
|
|
81
102
|
deregister();
|
|
82
103
|
});
|
|
83
104
|
|
|
84
105
|
stream.on('error', (error) => {
|
|
106
|
+
closeReason ??= 'stream error';
|
|
85
107
|
controller.abort();
|
|
86
108
|
// Note: This appears as a 200 response in the logs.
|
|
87
109
|
if (error.message != 'Shutting down system') {
|
|
@@ -95,15 +117,16 @@ export const syncStreamed = routeDefinition({
|
|
|
95
117
|
'Content-Type': 'application/x-ndjson'
|
|
96
118
|
},
|
|
97
119
|
data: stream,
|
|
98
|
-
afterSend: async () => {
|
|
120
|
+
afterSend: async (details) => {
|
|
121
|
+
if (details.clientClosed) {
|
|
122
|
+
closeReason ??= 'client closing stream';
|
|
123
|
+
}
|
|
99
124
|
controller.abort();
|
|
100
125
|
metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
|
|
101
126
|
logger.info(`Sync stream complete`, {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
operations_synced: tracker.operationsSynced,
|
|
106
|
-
data_synced_bytes: tracker.dataSyncedBytes
|
|
127
|
+
...tracker.getLogMeta(),
|
|
128
|
+
stream_ms: Date.now() - streamStart,
|
|
129
|
+
close_reason: closeReason ?? 'unknown'
|
|
107
130
|
});
|
|
108
131
|
}
|
|
109
132
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type fastify from 'fastify';
|
|
2
|
+
import * as uuid from 'uuid';
|
|
2
3
|
|
|
3
4
|
import { errors, HTTPMethod, logger, router } from '@powersync/lib-services-framework';
|
|
4
5
|
import { Context, ContextProvider, RequestEndpoint, RequestEndpointHandlerPayload } from './router.js';
|
|
@@ -25,9 +26,12 @@ export function registerFastifyRoutes(
|
|
|
25
26
|
handler: async (request, reply) => {
|
|
26
27
|
const startTime = new Date();
|
|
27
28
|
let response: router.RouterResponse;
|
|
29
|
+
const requestLogger = logger.child({
|
|
30
|
+
route: e.path,
|
|
31
|
+
rid: `h/${uuid.v7()}`
|
|
32
|
+
});
|
|
28
33
|
try {
|
|
29
|
-
const context = await contextProvider(request);
|
|
30
|
-
|
|
34
|
+
const context = await contextProvider(request, { logger: requestLogger });
|
|
31
35
|
let combined = {
|
|
32
36
|
...(request.params as any),
|
|
33
37
|
...(request.query as any)
|
|
@@ -63,7 +67,7 @@ export function registerFastifyRoutes(
|
|
|
63
67
|
}
|
|
64
68
|
} catch (ex) {
|
|
65
69
|
const serviceError = errors.asServiceError(ex);
|
|
66
|
-
|
|
70
|
+
requestLogger.error(`Request failed`, serviceError);
|
|
67
71
|
|
|
68
72
|
response = new router.RouterResponse({
|
|
69
73
|
status: serviceError.errorData.status || 500,
|
|
@@ -83,13 +87,12 @@ export function registerFastifyRoutes(
|
|
|
83
87
|
try {
|
|
84
88
|
await reply.send(response.data);
|
|
85
89
|
} finally {
|
|
86
|
-
await response.afterSend?.();
|
|
87
|
-
|
|
90
|
+
await response.afterSend?.({ clientClosed: request.socket.closed });
|
|
91
|
+
requestLogger.info(`${e.method} ${request.url}`, {
|
|
88
92
|
duration_ms: Math.round(new Date().valueOf() - startTime.valueOf() + Number.EPSILON),
|
|
89
93
|
status: response.status,
|
|
90
94
|
method: e.method,
|
|
91
|
-
path: request.url
|
|
92
|
-
route: e.path
|
|
95
|
+
path: request.url
|
|
93
96
|
});
|
|
94
97
|
}
|
|
95
98
|
}
|
package/src/routes/router.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { router } from '@powersync/lib-services-framework';
|
|
1
|
+
import { router, ServiceError, Logger } from '@powersync/lib-services-framework';
|
|
2
2
|
import type { JwtPayload } from '../auth/auth-index.js';
|
|
3
3
|
import { ServiceContext } from '../system/ServiceContext.js';
|
|
4
4
|
import { RouterEngine } from './RouterEngine.js';
|
|
@@ -17,11 +17,13 @@ export type Context = {
|
|
|
17
17
|
service_context: RouterServiceContext;
|
|
18
18
|
|
|
19
19
|
token_payload?: JwtPayload;
|
|
20
|
-
|
|
20
|
+
token_error?: ServiceError;
|
|
21
21
|
/**
|
|
22
22
|
* Only on websocket endpoints.
|
|
23
23
|
*/
|
|
24
24
|
user_agent?: string;
|
|
25
|
+
|
|
26
|
+
logger: Logger;
|
|
25
27
|
};
|
|
26
28
|
|
|
27
29
|
export type BasicRouterRequest = {
|
|
@@ -30,7 +32,11 @@ export type BasicRouterRequest = {
|
|
|
30
32
|
hostname: string;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
export type
|
|
35
|
+
export type ConextProviderOptions = {
|
|
36
|
+
logger: Logger;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type ContextProvider = (request: BasicRouterRequest, options: ConextProviderOptions) => Promise<Context>;
|
|
34
40
|
|
|
35
41
|
export type RequestEndpoint<
|
|
36
42
|
I,
|
|
@@ -2,12 +2,13 @@ import { ObserverClient } from '@powersync/lib-services-framework';
|
|
|
2
2
|
import { EvaluatedParameters, EvaluatedRow, SqliteRow, ToastableSqliteRow } from '@powersync/service-sync-rules';
|
|
3
3
|
import { BSON } from 'bson';
|
|
4
4
|
import { ReplicationEventPayload } from './ReplicationEventPayload.js';
|
|
5
|
-
import { SourceTable } from './SourceTable.js';
|
|
5
|
+
import { SourceTable, TableSnapshotStatus } from './SourceTable.js';
|
|
6
6
|
import { BatchedCustomWriteCheckpointOptions } from './storage-index.js';
|
|
7
7
|
import { InternalOpId } from '../util/utils.js';
|
|
8
8
|
|
|
9
9
|
export const DEFAULT_BUCKET_BATCH_COMMIT_OPTIONS: ResolvedBucketBatchCommitOptions = {
|
|
10
|
-
createEmptyCheckpoints: true
|
|
10
|
+
createEmptyCheckpoints: true,
|
|
11
|
+
oldestUncommittedChange: null
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
export interface BucketStorageBatch extends ObserverClient<BucketBatchStorageListener>, AsyncDisposable {
|
|
@@ -44,6 +45,8 @@ export interface BucketStorageBatch extends ObserverClient<BucketBatchStorageLis
|
|
|
44
45
|
* Flush and commit any saved ops. This creates a new checkpoint by default.
|
|
45
46
|
*
|
|
46
47
|
* Only call this after a transaction.
|
|
48
|
+
*
|
|
49
|
+
* Returns true if either (1) a new checkpoint was created, or (2) there are no changes to commit.
|
|
47
50
|
*/
|
|
48
51
|
commit(lsn: string, options?: BucketBatchCommitOptions): Promise<boolean>;
|
|
49
52
|
|
|
@@ -56,6 +59,14 @@ export interface BucketStorageBatch extends ObserverClient<BucketBatchStorageLis
|
|
|
56
59
|
*/
|
|
57
60
|
keepalive(lsn: string): Promise<boolean>;
|
|
58
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Set the LSN for a snapshot, before starting replication.
|
|
64
|
+
*
|
|
65
|
+
* Not required if the source database keeps track of this, for example with
|
|
66
|
+
* PostgreSQL logical replication slots.
|
|
67
|
+
*/
|
|
68
|
+
setSnapshotLsn(lsn: string): Promise<void>;
|
|
69
|
+
|
|
59
70
|
/**
|
|
60
71
|
* Get the last checkpoint LSN, from either commit or keepalive.
|
|
61
72
|
*/
|
|
@@ -63,6 +74,8 @@ export interface BucketStorageBatch extends ObserverClient<BucketBatchStorageLis
|
|
|
63
74
|
|
|
64
75
|
markSnapshotDone(tables: SourceTable[], no_checkpoint_before_lsn: string): Promise<SourceTable[]>;
|
|
65
76
|
|
|
77
|
+
updateTableProgress(table: SourceTable, progress: Partial<TableSnapshotStatus>): Promise<SourceTable>;
|
|
78
|
+
|
|
66
79
|
/**
|
|
67
80
|
* Queues the creation of a custom Write Checkpoint. This will be persisted after operations are flushed.
|
|
68
81
|
*/
|
|
@@ -154,6 +167,13 @@ export interface BucketBatchCommitOptions {
|
|
|
154
167
|
* Defaults to true.
|
|
155
168
|
*/
|
|
156
169
|
createEmptyCheckpoints?: boolean;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The timestamp of the first change in this batch, according to the source database.
|
|
173
|
+
*
|
|
174
|
+
* Used to estimate replication lag.
|
|
175
|
+
*/
|
|
176
|
+
oldestUncommittedChange?: Date | null;
|
|
157
177
|
}
|
|
158
178
|
|
|
159
179
|
export type ResolvedBucketBatchCommitOptions = Required<BucketBatchCommitOptions>;
|
|
@@ -9,6 +9,12 @@ export interface PersistedSyncRulesContent {
|
|
|
9
9
|
readonly id: number;
|
|
10
10
|
readonly sync_rules_content: string;
|
|
11
11
|
readonly slot_name: string;
|
|
12
|
+
/**
|
|
13
|
+
* True if this is the "active" copy of the sync rules.
|
|
14
|
+
*/
|
|
15
|
+
readonly active: boolean;
|
|
16
|
+
|
|
17
|
+
readonly last_checkpoint_lsn: string | null;
|
|
12
18
|
|
|
13
19
|
readonly last_fatal_error?: string | null;
|
|
14
20
|
readonly last_keepalive_ts?: Date | null;
|
|
@@ -2,6 +2,12 @@ import { DEFAULT_TAG } from '@powersync/service-sync-rules';
|
|
|
2
2
|
import * as util from '../util/util-index.js';
|
|
3
3
|
import { ColumnDescriptor } from './SourceEntity.js';
|
|
4
4
|
|
|
5
|
+
export interface TableSnapshotStatus {
|
|
6
|
+
totalEstimatedCount: number;
|
|
7
|
+
replicatedCount: number;
|
|
8
|
+
lastKey: Uint8Array | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
export class SourceTable {
|
|
6
12
|
static readonly DEFAULT_TAG = DEFAULT_TAG;
|
|
7
13
|
|
|
@@ -32,6 +38,13 @@ export class SourceTable {
|
|
|
32
38
|
*/
|
|
33
39
|
public syncEvent = true;
|
|
34
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Always undefined if snapshotComplete = true.
|
|
43
|
+
*
|
|
44
|
+
* May be set if snapshotComplete = false.
|
|
45
|
+
*/
|
|
46
|
+
public snapshotStatus: TableSnapshotStatus | undefined = undefined;
|
|
47
|
+
|
|
35
48
|
constructor(
|
|
36
49
|
public readonly id: any,
|
|
37
50
|
public readonly connectionTag: string,
|
|
@@ -40,7 +53,7 @@ export class SourceTable {
|
|
|
40
53
|
public readonly table: string,
|
|
41
54
|
|
|
42
55
|
public readonly replicaIdColumns: ColumnDescriptor[],
|
|
43
|
-
public
|
|
56
|
+
public snapshotComplete: boolean
|
|
44
57
|
) {}
|
|
45
58
|
|
|
46
59
|
get hasReplicaIdentity() {
|
|
@@ -68,4 +81,34 @@ export class SourceTable {
|
|
|
68
81
|
get syncAny() {
|
|
69
82
|
return this.syncData || this.syncParameters || this.syncEvent;
|
|
70
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* In-memory clone of the table status.
|
|
87
|
+
*/
|
|
88
|
+
clone() {
|
|
89
|
+
const copy = new SourceTable(
|
|
90
|
+
this.id,
|
|
91
|
+
this.connectionTag,
|
|
92
|
+
this.objectId,
|
|
93
|
+
this.schema,
|
|
94
|
+
this.table,
|
|
95
|
+
this.replicaIdColumns,
|
|
96
|
+
this.snapshotComplete
|
|
97
|
+
);
|
|
98
|
+
copy.syncData = this.syncData;
|
|
99
|
+
copy.syncParameters = this.syncParameters;
|
|
100
|
+
copy.snapshotStatus = this.snapshotStatus;
|
|
101
|
+
return copy;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
formatSnapshotProgress() {
|
|
105
|
+
if (this.snapshotComplete || this.snapshotStatus == null) {
|
|
106
|
+
// Should not happen
|
|
107
|
+
return '-';
|
|
108
|
+
} else if (this.snapshotStatus.totalEstimatedCount < 0) {
|
|
109
|
+
return `${this.snapshotStatus.replicatedCount}/?`;
|
|
110
|
+
} else {
|
|
111
|
+
return `${this.snapshotStatus.replicatedCount}/~${this.snapshotStatus.totalEstimatedCount}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
71
114
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ObserverClient } from '@powersync/lib-services-framework';
|
|
1
|
+
import { Logger, ObserverClient } from '@powersync/lib-services-framework';
|
|
2
2
|
import { ParameterLookup, SqlSyncRules, SqliteJsonRow } from '@powersync/service-sync-rules';
|
|
3
3
|
import * as util from '../util/util-index.js';
|
|
4
|
-
import { BucketStorageBatch, FlushedResult } from './BucketStorageBatch.js';
|
|
4
|
+
import { BucketStorageBatch, FlushedResult, SaveUpdate } from './BucketStorageBatch.js';
|
|
5
5
|
import { BucketStorageFactory } from './BucketStorageFactory.js';
|
|
6
6
|
import { ParseSyncRulesOptions } from './PersistedSyncRulesContent.js';
|
|
7
7
|
import { SourceEntityDescriptor } from './SourceEntity.js';
|
|
@@ -125,6 +125,7 @@ export interface SyncRuleStatus {
|
|
|
125
125
|
checkpoint_lsn: string | null;
|
|
126
126
|
active: boolean;
|
|
127
127
|
snapshot_done: boolean;
|
|
128
|
+
snapshot_lsn: string | null;
|
|
128
129
|
}
|
|
129
130
|
export interface ResolveTableOptions {
|
|
130
131
|
group_id: number;
|
|
@@ -159,6 +160,15 @@ export interface StartBatchOptions extends ParseSyncRulesOptions {
|
|
|
159
160
|
* This will avoid creating new operations for rows previously replicated.
|
|
160
161
|
*/
|
|
161
162
|
skipExistingRows?: boolean;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Callback called if we streamed an update to a record that we don't have yet.
|
|
166
|
+
*
|
|
167
|
+
* This is expected to happen in some initial replication edge cases, only if storeCurrentData = true.
|
|
168
|
+
*/
|
|
169
|
+
markRecordUnavailable?: BucketStorageMarkRecordUnavailable;
|
|
170
|
+
|
|
171
|
+
logger?: Logger;
|
|
162
172
|
}
|
|
163
173
|
|
|
164
174
|
export interface CompactOptions {
|
|
@@ -274,3 +284,5 @@ export const CHECKPOINT_INVALIDATE_ALL: CheckpointChanges = {
|
|
|
274
284
|
updatedParameterLookups: new Set<string>(),
|
|
275
285
|
invalidateParameterBuckets: true
|
|
276
286
|
};
|
|
287
|
+
|
|
288
|
+
export type BucketStorageMarkRecordUnavailable = (record: SaveUpdate) => void;
|
package/src/storage/bson.ts
CHANGED
|
@@ -10,7 +10,10 @@ type NodeBuffer = Buffer<ArrayBuffer>;
|
|
|
10
10
|
*/
|
|
11
11
|
export const BSON_DESERIALIZE_INTERNAL_OPTIONS: bson.DeserializeOptions = {
|
|
12
12
|
// use bigint instead of Long
|
|
13
|
-
useBigInt64: true
|
|
13
|
+
useBigInt64: true,
|
|
14
|
+
// We cannot use promoteBuffers: true, since that also converst UUID to Buffer
|
|
15
|
+
// Instead, we need to handle bson.Binary when reading data
|
|
16
|
+
promoteBuffers: false
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
/**
|