@powersync/service-core 1.11.3 → 1.12.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.
Files changed (107) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/auth/CachedKeyCollector.js +2 -7
  3. package/dist/auth/CachedKeyCollector.js.map +1 -1
  4. package/dist/auth/CompoundKeyCollector.js.map +1 -1
  5. package/dist/auth/KeyCollector.d.ts +2 -2
  6. package/dist/auth/KeyStore.js +32 -14
  7. package/dist/auth/KeyStore.js.map +1 -1
  8. package/dist/auth/RemoteJWKSCollector.d.ts +1 -0
  9. package/dist/auth/RemoteJWKSCollector.js +39 -16
  10. package/dist/auth/RemoteJWKSCollector.js.map +1 -1
  11. package/dist/auth/auth-index.d.ts +1 -0
  12. package/dist/auth/auth-index.js +1 -0
  13. package/dist/auth/auth-index.js.map +1 -1
  14. package/dist/auth/utils.d.ts +6 -0
  15. package/dist/auth/utils.js +97 -0
  16. package/dist/auth/utils.js.map +1 -0
  17. package/dist/entry/commands/compact-action.js +4 -1
  18. package/dist/entry/commands/compact-action.js.map +1 -1
  19. package/dist/entry/commands/migrate-action.js +4 -1
  20. package/dist/entry/commands/migrate-action.js.map +1 -1
  21. package/dist/entry/commands/test-connection-action.js +4 -1
  22. package/dist/entry/commands/test-connection-action.js.map +1 -1
  23. package/dist/routes/RouterEngine.d.ts +2 -0
  24. package/dist/routes/RouterEngine.js +15 -10
  25. package/dist/routes/RouterEngine.js.map +1 -1
  26. package/dist/routes/auth.d.ts +5 -16
  27. package/dist/routes/auth.js +6 -4
  28. package/dist/routes/auth.js.map +1 -1
  29. package/dist/routes/configure-fastify.d.ts +3 -21
  30. package/dist/routes/configure-fastify.js +3 -6
  31. package/dist/routes/configure-fastify.js.map +1 -1
  32. package/dist/routes/configure-rsocket.js +28 -14
  33. package/dist/routes/configure-rsocket.js.map +1 -1
  34. package/dist/routes/endpoints/admin.js.map +1 -1
  35. package/dist/routes/endpoints/checkpointing.d.ts +4 -28
  36. package/dist/routes/endpoints/checkpointing.js.map +1 -1
  37. package/dist/routes/endpoints/route-endpoints-index.d.ts +1 -0
  38. package/dist/routes/endpoints/route-endpoints-index.js +1 -0
  39. package/dist/routes/endpoints/route-endpoints-index.js.map +1 -1
  40. package/dist/routes/endpoints/socket-route.js +22 -8
  41. package/dist/routes/endpoints/socket-route.js.map +1 -1
  42. package/dist/routes/endpoints/sync-rules.js.map +1 -1
  43. package/dist/routes/endpoints/sync-stream.d.ts +2 -14
  44. package/dist/routes/endpoints/sync-stream.js +28 -9
  45. package/dist/routes/endpoints/sync-stream.js.map +1 -1
  46. package/dist/routes/route-register.js +10 -6
  47. package/dist/routes/route-register.js.map +1 -1
  48. package/dist/routes/router.d.ts +8 -7
  49. package/dist/routes/router.js.map +1 -1
  50. package/dist/runner/teardown.js +4 -1
  51. package/dist/runner/teardown.js.map +1 -1
  52. package/dist/sync/BucketChecksumState.d.ts +40 -18
  53. package/dist/sync/BucketChecksumState.js +120 -72
  54. package/dist/sync/BucketChecksumState.js.map +1 -1
  55. package/dist/sync/RequestTracker.d.ts +22 -1
  56. package/dist/sync/RequestTracker.js +51 -2
  57. package/dist/sync/RequestTracker.js.map +1 -1
  58. package/dist/sync/sync.d.ts +3 -5
  59. package/dist/sync/sync.js +47 -32
  60. package/dist/sync/sync.js.map +1 -1
  61. package/dist/system/ServiceContext.d.ts +19 -4
  62. package/dist/system/ServiceContext.js +20 -8
  63. package/dist/system/ServiceContext.js.map +1 -1
  64. package/dist/util/config/collectors/config-collector.js +4 -33
  65. package/dist/util/config/collectors/config-collector.js.map +1 -1
  66. package/dist/util/config/collectors/impl/yaml-env.d.ts +7 -0
  67. package/dist/util/config/collectors/impl/yaml-env.js +59 -0
  68. package/dist/util/config/collectors/impl/yaml-env.js.map +1 -0
  69. package/dist/util/config/compound-config-collector.js +18 -1
  70. package/dist/util/config/compound-config-collector.js.map +1 -1
  71. package/dist/util/config/types.d.ts +11 -0
  72. package/package.json +6 -7
  73. package/src/auth/CachedKeyCollector.ts +4 -6
  74. package/src/auth/CompoundKeyCollector.ts +2 -1
  75. package/src/auth/KeyCollector.ts +2 -2
  76. package/src/auth/KeyStore.ts +45 -20
  77. package/src/auth/RemoteJWKSCollector.ts +39 -16
  78. package/src/auth/auth-index.ts +1 -0
  79. package/src/auth/utils.ts +102 -0
  80. package/src/entry/commands/compact-action.ts +4 -1
  81. package/src/entry/commands/migrate-action.ts +4 -1
  82. package/src/entry/commands/test-connection-action.ts +4 -1
  83. package/src/routes/RouterEngine.ts +21 -11
  84. package/src/routes/auth.ts +7 -6
  85. package/src/routes/configure-fastify.ts +6 -8
  86. package/src/routes/configure-rsocket.ts +33 -18
  87. package/src/routes/endpoints/admin.ts +5 -5
  88. package/src/routes/endpoints/checkpointing.ts +2 -2
  89. package/src/routes/endpoints/route-endpoints-index.ts +1 -0
  90. package/src/routes/endpoints/socket-route.ts +27 -11
  91. package/src/routes/endpoints/sync-rules.ts +4 -4
  92. package/src/routes/endpoints/sync-stream.ts +34 -11
  93. package/src/routes/route-register.ts +10 -7
  94. package/src/routes/router.ts +11 -4
  95. package/src/runner/teardown.ts +5 -1
  96. package/src/sync/BucketChecksumState.ts +160 -75
  97. package/src/sync/RequestTracker.ts +70 -3
  98. package/src/sync/sync.ts +69 -46
  99. package/src/system/ServiceContext.ts +31 -12
  100. package/src/util/config/collectors/config-collector.ts +4 -40
  101. package/src/util/config/collectors/impl/yaml-env.ts +67 -0
  102. package/src/util/config/compound-config-collector.ts +22 -5
  103. package/src/util/config/types.ts +13 -0
  104. package/test/src/auth.test.ts +29 -11
  105. package/test/src/config.test.ts +72 -0
  106. package/test/src/sync/BucketChecksumState.test.ts +32 -18
  107. package/tsconfig.tsbuildinfo +1 -1
@@ -1,4 +1,4 @@
1
- import { ErrorCode, errors, logger, router, schema } from '@powersync/lib-services-framework';
1
+ import { ErrorCode, errors, router, schema } from '@powersync/lib-services-framework';
2
2
  import { RequestParameters } from '@powersync/service-sync-rules';
3
3
  import { Readable } from 'stream';
4
4
  import * as sync from '../../sync/sync-index.js';
@@ -16,11 +16,18 @@ export const syncStreamed = routeDefinition({
16
16
  authorize: authUser,
17
17
  validator: schema.createTsCodecValidator(util.StreamingSyncRequest, { allowAdditional: true }),
18
18
  handler: async (payload) => {
19
- const { service_context } = payload.context;
19
+ const { service_context, logger } = payload.context;
20
20
  const { routerEngine, storageEngine, metricsEngine, syncContext } = service_context;
21
21
  const headers = payload.request.headers;
22
22
  const userAgent = headers['x-user-agent'] ?? headers['user-agent'];
23
23
  const clientId = payload.params.client_id;
24
+ const streamStart = Date.now();
25
+ logger.defaultMeta = {
26
+ ...logger.defaultMeta,
27
+ user_agent: userAgent,
28
+ client_id: clientId,
29
+ user_id: payload.context.user_id
30
+ };
24
31
  if (routerEngine.closed) {
25
32
  throw new errors.ServiceError({
26
33
  status: 503,
@@ -51,17 +58,28 @@ export const syncStreamed = routeDefinition({
51
58
  syncParams,
52
59
  token: payload.context.token_payload,
53
60
  tracker,
54
- signal: controller.signal
61
+ signal: controller.signal,
62
+ logger
55
63
  })), tracker), { objectMode: false, highWaterMark: 16 * 1024 });
64
+ // Best effort guess on why the stream was closed.
65
+ // We use the `??=` operator everywhere, so that we catch the first relevant
66
+ // event, which is usually the most specific.
67
+ let closeReason = undefined;
56
68
  const deregister = routerEngine.addStopHandler(() => {
57
69
  // This error is not currently propagated to the client
58
70
  controller.abort();
71
+ closeReason ??= 'process shutdown';
59
72
  stream.destroy(new Error('Shutting down system'));
60
73
  });
74
+ stream.on('end', () => {
75
+ // Auth failure or switch to new sync rules
76
+ closeReason ??= 'service closing stream';
77
+ });
61
78
  stream.on('close', () => {
62
79
  deregister();
63
80
  });
64
81
  stream.on('error', (error) => {
82
+ closeReason ??= 'stream error';
65
83
  controller.abort();
66
84
  // Note: This appears as a 200 response in the logs.
67
85
  if (error.message != 'Shutting down system') {
@@ -74,15 +92,16 @@ export const syncStreamed = routeDefinition({
74
92
  'Content-Type': 'application/x-ndjson'
75
93
  },
76
94
  data: stream,
77
- afterSend: async () => {
95
+ afterSend: async (details) => {
96
+ if (details.clientClosed) {
97
+ closeReason ??= 'client closing stream';
98
+ }
78
99
  controller.abort();
79
100
  metricsEngine.getUpDownCounter(APIMetric.CONCURRENT_CONNECTIONS).add(-1);
80
101
  logger.info(`Sync stream complete`, {
81
- user_id: syncParams.user_id,
82
- client_id: clientId,
83
- user_agent: userAgent,
84
- operations_synced: tracker.operationsSynced,
85
- data_synced_bytes: tracker.dataSyncedBytes
102
+ ...tracker.getLogMeta(),
103
+ stream_ms: Date.now() - streamStart,
104
+ close_reason: closeReason ?? 'unknown'
86
105
  });
87
106
  }
88
107
  });
@@ -1 +1 @@
1
- {"version":3,"file":"sync-stream.js","sourceRoot":"","sources":["../../../src/routes/endpoints/sync-stream.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-stream.js","sourceRoot":"","sources":["../../../src/routes/endpoints/sync-stream.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import * as uuid from 'uuid';
1
2
  import { errors, logger, router } from '@powersync/lib-services-framework';
2
3
  /**
3
4
  * Registers endpoint definitions as routes on a Fastify app instance.
@@ -12,8 +13,12 @@ export function registerFastifyRoutes(app, contextProvider, endpoints) {
12
13
  handler: async (request, reply) => {
13
14
  const startTime = new Date();
14
15
  let response;
16
+ const requestLogger = logger.child({
17
+ route: e.path,
18
+ rid: `h/${uuid.v7()}`
19
+ });
15
20
  try {
16
- const context = await contextProvider(request);
21
+ const context = await contextProvider(request, { logger: requestLogger });
17
22
  let combined = {
18
23
  ...request.params,
19
24
  ...request.query
@@ -48,7 +53,7 @@ export function registerFastifyRoutes(app, contextProvider, endpoints) {
48
53
  }
49
54
  catch (ex) {
50
55
  const serviceError = errors.asServiceError(ex);
51
- logger.error(`Request failed`, serviceError);
56
+ requestLogger.error(`Request failed`, serviceError);
52
57
  response = new router.RouterResponse({
53
58
  status: serviceError.errorData.status || 500,
54
59
  headers: {
@@ -67,13 +72,12 @@ export function registerFastifyRoutes(app, contextProvider, endpoints) {
67
72
  await reply.send(response.data);
68
73
  }
69
74
  finally {
70
- await response.afterSend?.();
71
- logger.info(`${e.method} ${request.url}`, {
75
+ await response.afterSend?.({ clientClosed: request.socket.closed });
76
+ requestLogger.info(`${e.method} ${request.url}`, {
72
77
  duration_ms: Math.round(new Date().valueOf() - startTime.valueOf() + Number.EPSILON),
73
78
  status: response.status,
74
79
  method: e.method,
75
- path: request.url,
76
- route: e.path
80
+ path: request.url
77
81
  });
78
82
  }
79
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"route-register.js","sourceRoot":"","sources":["../../src/routes/route-register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAc,MAAM,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAQvF;;GAEG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAA4B,EAC5B,eAAgC,EAChC,SAA+C;IAE/C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,sCAAsC;QACtC,GAAG,CAAC,QAAQ,CAAC,KAAK,WAAW,OAAO;YAClC,OAAO,CAAC,KAAK,CAAC;gBACZ,GAAG,EAAE,CAAC,CAAC,IAAI;gBACX,MAAM,EAAE,CAAC,CAAC,MAAoB;gBAC9B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;oBAChC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC;oBAC7B,IAAI,QAA+B,CAAC;oBACpC,IAAI,CAAC;wBACH,MAAM,OAAO,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;wBAE/C,IAAI,QAAQ,GAAG;4BACb,GAAI,OAAO,CAAC,MAAc;4BAC1B,GAAI,OAAO,CAAC,KAAa;yBAC1B,CAAC;wBAEF,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;4BACvG,QAAQ,GAAG;gCACT,GAAG,QAAQ;gCACX,GAAG,OAAO,CAAC,IAAI;6BAChB,CAAC;wBACJ,CAAC;wBAED,MAAM,OAAO,GAAkC;4BAC7C,OAAO,EAAE,OAAO;4BAChB,MAAM,EAAE,QAAQ;4BAChB,OAAO;yBACR,CAAC;wBAEF,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;wBAElE,IAAI,MAAM,CAAC,cAAc,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,EAAE,CAAC;4BAC7D,QAAQ,GAAG,gBAAgB,CAAC;wBAC9B,CAAC;6BAAM,IAAI,MAAM,CAAC,eAAe,CAAC,gBAAgB,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;4BACzF,QAAQ,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC;gCACnC,MAAM,EAAE,GAAG;gCACX,IAAI,EAAE,gBAAgB;6BACvB,CAAC,CAAC;wBACL,CAAC;6BAAM,CAAC;4BACN,QAAQ,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC;gCACnC,MAAM,EAAE,GAAG;gCACX,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE;6BACjC,CAAC,CAAC;wBACL,CAAC;oBACH,CAAC;oBAAC,OAAO,EAAE,EAAE,CAAC;wBACZ,MAAM,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;wBAC/C,MAAM,CAAC,KAAK,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;wBAE7C,QAAQ,GAAG,IAAI,MAAM,CAAC,cAAc,CAAC;4BACnC,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,MAAM,IAAI,GAAG;4BAC5C,OAAO,EAAE;gCACP,cAAc,EAAE,kBAAkB;6BACnC;4BACD,IAAI,EAAE;gCACJ,KAAK,EAAE,YAAY,CAAC,SAAS;6BAC9B;yBACF,CAAC,CAAC;oBACL,CAAC;oBAED,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;wBAC5C,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;oBAC3C,CAAC,CAAC,CAAC;oBACH,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;oBAC9B,IAAI,CAAC;wBACH,MAAM,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;oBAClC,CAAC;4BAAS,CAAC;wBACT,MAAM,QAAQ,CAAC,SAAS,EAAE,EAAE,CAAC;wBAC7B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE;4BACxC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;4BACpF,MAAM,EAAE,QAAQ,CAAC,MAAM;4BACvB,MAAM,EAAE,CAAC,CAAC,MAAM;4BAChB,IAAI,EAAE,OAAO,CAAC,GAAG;4BACjB,KAAK,EAAE,CAAC,CAAC,IAAI;yBACd,CAAC,CAAC;oBACL,CAAC;gBACH,CAAC;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,EAAE,CAAC;gBACvB,OAAO,CAAC,2BAA2B,EAAE,CAAC;YACxC,CAAC;YAED,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"route-register.js","sourceRoot":"","sources":["../../src/routes/route-register.ts"],"names":[],"mappings":"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"}
@@ -1,13 +1,10 @@
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
- import { RouterEngine } from './RouterEngine.js';
5
4
  /**
6
5
  * The {@link RouterEngine} must be provided for these routes
7
6
  */
8
- export type RouterServiceContext = ServiceContext & {
9
- routerEngine: RouterEngine;
10
- };
7
+ export type RouterServiceContext = ServiceContext;
11
8
  /**
12
9
  * Common context for routes
13
10
  */
@@ -15,18 +12,22 @@ export type Context = {
15
12
  user_id?: string;
16
13
  service_context: RouterServiceContext;
17
14
  token_payload?: JwtPayload;
18
- token_errors?: string[];
15
+ token_error?: ServiceError;
19
16
  /**
20
17
  * Only on websocket endpoints.
21
18
  */
22
19
  user_agent?: string;
20
+ logger: Logger;
23
21
  };
24
22
  export type BasicRouterRequest = {
25
23
  headers: Record<string, string | string[] | undefined>;
26
24
  protocol: string;
27
25
  hostname: string;
28
26
  };
29
- export type ContextProvider = (request: BasicRouterRequest) => Promise<Context>;
27
+ export type ConextProviderOptions = {
28
+ logger: Logger;
29
+ };
30
+ export type ContextProvider = (request: BasicRouterRequest, options: ConextProviderOptions) => Promise<Context>;
30
31
  export type RequestEndpoint<I, O, C = Context, Payload = RequestEndpointHandlerPayload<I, C, BasicRouterRequest>> = router.Endpoint<I, O, C, Payload> & {};
31
32
  export type RequestEndpointHandlerPayload<I = any, C = Context, Request = BasicRouterRequest> = router.EndpointHandlerPayload<I, C> & {
32
33
  request: Request;
@@ -1 +1 @@
1
- {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/routes/router.ts"],"names":[],"mappings":"AAiDA;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA4C;IAE5C,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/routes/router.ts"],"names":[],"mappings":"AAwDA;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,MAA4C;IAE5C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -11,7 +11,10 @@ export async function teardown(runnerConfig) {
11
11
  try {
12
12
  logger.info(`Tearing down PowerSync instance...`);
13
13
  const config = await utils.loadConfig(runnerConfig);
14
- const serviceContext = new system.ServiceContextContainer(config);
14
+ const serviceContext = new system.ServiceContextContainer({
15
+ serviceMode: system.ServiceContextMode.TEARDOWN,
16
+ configuration: config
17
+ });
15
18
  const moduleManager = container.getImplementation(modules.ModuleManager);
16
19
  await moduleManager.initialize(serviceContext);
17
20
  // This is mostly done to ensure that the storage is ready
@@ -1 +1 @@
1
- {"version":3,"file":"teardown.js","sourceRoot":"","sources":["../../src/runner/teardown.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"teardown.js","sourceRoot":"","sources":["../../src/runner/teardown.ts"],"names":[],"mappings":"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"}
@@ -1,18 +1,22 @@
1
1
  import { BucketDescription, RequestParameters, SqlSyncRules } from '@powersync/service-sync-rules';
2
2
  import * as storage from '../storage/storage-index.js';
3
3
  import * as util from '../util/util-index.js';
4
- import { BucketSyncState } from './sync.js';
4
+ import { Logger } from '@powersync/lib-services-framework';
5
5
  import { SyncContext } from './SyncContext.js';
6
6
  export interface BucketChecksumStateOptions {
7
7
  syncContext: SyncContext;
8
8
  bucketStorage: BucketChecksumStateStorage;
9
9
  syncRules: SqlSyncRules;
10
10
  syncParams: RequestParameters;
11
+ logger?: Logger;
11
12
  initialBucketPositions?: {
12
13
  name: string;
13
14
  after: util.InternalOpId;
14
15
  }[];
15
16
  }
17
+ type BucketSyncState = {
18
+ start_op_id: util.InternalOpId;
19
+ };
16
20
  /**
17
21
  * Represents the state of the checksums and data for a specific connection.
18
22
  *
@@ -24,8 +28,10 @@ export declare class BucketChecksumState {
24
28
  /**
25
29
  * Bucket state of bucket id -> op_id.
26
30
  * This starts with the state from the client. May contain buckets that the user do not have access to (anymore).
31
+ *
32
+ * This is always updated in-place.
27
33
  */
28
- bucketDataPositions: Map<string, BucketSyncState>;
34
+ readonly bucketDataPositions: Map<string, BucketSyncState>;
29
35
  /**
30
36
  * Last checksums sent to the client. We keep this to calculate checkpoint diffs.
31
37
  */
@@ -37,26 +43,20 @@ export declare class BucketChecksumState {
37
43
  * partial checkpoints are sent.
38
44
  */
39
45
  private pendingBucketDownloads;
46
+ private readonly logger;
40
47
  constructor(options: BucketChecksumStateOptions);
41
- buildNextCheckpointLine(next: storage.StorageCheckpointUpdate): Promise<CheckpointLine | null>;
42
48
  /**
43
- * Get bucket positions to sync, given the list of buckets.
49
+ * Build a new checkpoint line for an underlying storage checkpoint update if any buckets have changed.
44
50
  *
45
- * @param bucketsToFetch List of buckets to fetch, typically from buildNextCheckpointLine, or a subset of that
46
- * @returns
47
- */
48
- getFilteredBucketPositions(bucketsToFetch: BucketDescription[]): Map<string, util.InternalOpId>;
49
- /**
50
- * Update the position of bucket data the client has.
51
+ * This call is idempotent - no internal state is updated directly when this is called.
51
52
  *
52
- * @param bucket the bucket name
53
- * @param nextAfter sync operations >= this value in the next batch
53
+ * When the checkpoint line is sent to the client, call `CheckpointLine.advance()` to update the internal state.
54
+ * A line may be safely discarded (not sent to the client) if `advance()` is not called.
55
+ *
56
+ * @param next The updated checkpoint
57
+ * @returns A {@link CheckpointLine} if any of the buckets watched by this connected was updated, or otherwise `null`.
54
58
  */
55
- updateBucketPosition(options: {
56
- bucket: string;
57
- nextAfter: util.InternalOpId;
58
- hasMore: boolean;
59
- }): void;
59
+ buildNextCheckpointLine(next: storage.StorageCheckpointUpdate): Promise<CheckpointLine | null>;
60
60
  }
61
61
  declare const INVALIDATE_ALL_BUCKETS: unique symbol;
62
62
  export interface CheckpointUpdate {
@@ -78,10 +78,11 @@ export declare class BucketParameterState {
78
78
  readonly syncParams: RequestParameters;
79
79
  private readonly querier;
80
80
  private readonly staticBuckets;
81
+ private readonly logger;
81
82
  private cachedDynamicBuckets;
82
83
  private cachedDynamicBucketSet;
83
84
  private readonly lookups;
84
- constructor(context: SyncContext, bucketStorage: BucketChecksumStateStorage, syncRules: SqlSyncRules, syncParams: RequestParameters);
85
+ constructor(context: SyncContext, bucketStorage: BucketChecksumStateStorage, syncRules: SqlSyncRules, syncParams: RequestParameters, logger: Logger);
85
86
  getCheckpointUpdate(checkpoint: storage.StorageCheckpointUpdate): Promise<CheckpointUpdate>;
86
87
  /**
87
88
  * For static buckets, we can keep track of which buckets have been updated.
@@ -95,6 +96,27 @@ export declare class BucketParameterState {
95
96
  export interface CheckpointLine {
96
97
  checkpointLine: util.StreamingSyncCheckpointDiff | util.StreamingSyncCheckpoint;
97
98
  bucketsToFetch: BucketDescription[];
99
+ /**
100
+ * Call when a checkpoint line is being sent to a client, to update the internal state.
101
+ */
102
+ advance: () => void;
103
+ /**
104
+ * Get bucket positions to sync, given the list of buckets.
105
+ *
106
+ * @param bucketsToFetch List of buckets to fetch - either this.bucketsToFetch, or a subset of it. Defaults to this.bucketsToFetch.
107
+ */
108
+ getFilteredBucketPositions(bucketsToFetch?: BucketDescription[]): Map<string, util.InternalOpId>;
109
+ /**
110
+ * Update the position of bucket data the client has, after it was sent to the client.
111
+ *
112
+ * @param bucket the bucket name
113
+ * @param nextAfter sync operations >= this value in the next batch
114
+ */
115
+ updateBucketPosition(options: {
116
+ bucket: string;
117
+ nextAfter: util.InternalOpId;
118
+ hasMore: boolean;
119
+ }): void;
98
120
  }
99
121
  export type BucketChecksumStateStorage = Pick<storage.SyncRulesBucketStorage, 'getChecksums' | 'getParameterSets'>;
100
122
  export {};
@@ -1,5 +1,5 @@
1
1
  import * as util from '../util/util-index.js';
2
- import { ErrorCode, logger, ServiceAssertionError, ServiceError } from '@powersync/lib-services-framework';
2
+ import { ErrorCode, ServiceAssertionError, ServiceError, logger as defaultLogger } from '@powersync/lib-services-framework';
3
3
  import { JSONBig } from '@powersync/service-jsonbig';
4
4
  import { getIntersection, hasIntersection } from './util.js';
5
5
  /**
@@ -13,6 +13,8 @@ export class BucketChecksumState {
13
13
  /**
14
14
  * Bucket state of bucket id -> op_id.
15
15
  * This starts with the state from the client. May contain buckets that the user do not have access to (anymore).
16
+ *
17
+ * This is always updated in-place.
16
18
  */
17
19
  bucketDataPositions = new Map();
18
20
  /**
@@ -26,34 +28,38 @@ export class BucketChecksumState {
26
28
  * partial checkpoints are sent.
27
29
  */
28
30
  pendingBucketDownloads = new Set();
31
+ logger;
29
32
  constructor(options) {
30
33
  this.context = options.syncContext;
31
34
  this.bucketStorage = options.bucketStorage;
32
- this.parameterState = new BucketParameterState(options.syncContext, options.bucketStorage, options.syncRules, options.syncParams);
35
+ this.logger = options.logger ?? defaultLogger;
36
+ this.parameterState = new BucketParameterState(options.syncContext, options.bucketStorage, options.syncRules, options.syncParams, this.logger);
33
37
  this.bucketDataPositions = new Map();
34
38
  for (let { name, after: start } of options.initialBucketPositions ?? []) {
35
39
  this.bucketDataPositions.set(name, { start_op_id: start });
36
40
  }
37
41
  }
42
+ /**
43
+ * Build a new checkpoint line for an underlying storage checkpoint update if any buckets have changed.
44
+ *
45
+ * This call is idempotent - no internal state is updated directly when this is called.
46
+ *
47
+ * When the checkpoint line is sent to the client, call `CheckpointLine.advance()` to update the internal state.
48
+ * A line may be safely discarded (not sent to the client) if `advance()` is not called.
49
+ *
50
+ * @param next The updated checkpoint
51
+ * @returns A {@link CheckpointLine} if any of the buckets watched by this connected was updated, or otherwise `null`.
52
+ */
38
53
  async buildNextCheckpointLine(next) {
39
54
  const { writeCheckpoint, base } = next;
40
55
  const user_id = this.parameterState.syncParams.user_id;
41
56
  const storage = this.bucketStorage;
42
57
  const update = await this.parameterState.getCheckpointUpdate(next);
43
- if (update == null && this.lastWriteCheckpoint == writeCheckpoint) {
44
- return null;
45
- }
46
58
  const { buckets: allBuckets, updatedBuckets } = update;
47
- let dataBucketsNew = new Map();
48
- for (let bucket of allBuckets) {
49
- dataBucketsNew.set(bucket.bucket, {
50
- description: bucket,
51
- start_op_id: this.bucketDataPositions.get(bucket.bucket)?.start_op_id ?? 0n
52
- });
53
- }
54
- this.bucketDataPositions = dataBucketsNew;
55
- if (dataBucketsNew.size > this.context.maxBuckets) {
56
- throw new ServiceError(ErrorCode.PSYNC_S2305, `Too many buckets: ${dataBucketsNew.size} (limit of ${this.context.maxBuckets})`);
59
+ /** Set of all buckets in this checkpoint. */
60
+ const bucketDescriptionMap = new Map(allBuckets.map((b) => [b.bucket, b]));
61
+ if (bucketDescriptionMap.size > this.context.maxBuckets) {
62
+ throw new ServiceError(ErrorCode.PSYNC_S2305, `Too many buckets: ${bucketDescriptionMap.size} (limit of ${this.context.maxBuckets})`);
57
63
  }
58
64
  let checksumMap;
59
65
  if (updatedBuckets != INVALIDATE_ALL_BUCKETS) {
@@ -63,7 +69,7 @@ export class BucketChecksumState {
63
69
  // Re-check updated buckets only
64
70
  let checksumLookups = [];
65
71
  let newChecksums = new Map();
66
- for (let bucket of dataBucketsNew.keys()) {
72
+ for (let bucket of bucketDescriptionMap.keys()) {
67
73
  if (!updatedBuckets.has(bucket)) {
68
74
  const existing = this.lastChecksums.get(bucket);
69
75
  if (existing == null) {
@@ -87,12 +93,14 @@ export class BucketChecksumState {
87
93
  }
88
94
  else {
89
95
  // Re-check all buckets
90
- const bucketList = [...dataBucketsNew.keys()];
96
+ const bucketList = [...bucketDescriptionMap.keys()];
91
97
  checksumMap = await storage.getChecksums(base.checkpoint, bucketList);
92
98
  }
93
99
  // Subset of buckets for which there may be new data in this batch.
94
100
  let bucketsToFetch;
95
101
  let checkpointLine;
102
+ // Log function that is deferred until the checkpoint line is sent to the client.
103
+ let deferredLog;
96
104
  if (this.lastChecksums) {
97
105
  // TODO: If updatedBuckets is present, we can use that to more efficiently calculate a diff,
98
106
  // and avoid any unnecessary loops through the entire list of buckets.
@@ -116,26 +124,28 @@ export class BucketChecksumState {
116
124
  }
117
125
  const updatedBucketDescriptions = diff.updatedBuckets.map((e) => ({
118
126
  ...e,
119
- priority: this.bucketDataPositions.get(e.bucket).description.priority
127
+ priority: bucketDescriptionMap.get(e.bucket).priority
120
128
  }));
121
129
  bucketsToFetch = [...generateBucketsToFetch].map((b) => {
122
130
  return {
123
131
  bucket: b,
124
- priority: this.bucketDataPositions.get(b).description.priority
132
+ priority: bucketDescriptionMap.get(b).priority
125
133
  };
126
134
  });
127
- let message = `Updated checkpoint: ${base.checkpoint} | `;
128
- message += `write: ${writeCheckpoint} | `;
129
- message += `buckets: ${allBuckets.length} | `;
130
- message += `updated: ${limitedBuckets(diff.updatedBuckets, 20)} | `;
131
- message += `removed: ${limitedBuckets(diff.removedBuckets, 20)}`;
132
- logger.info(message, {
133
- checkpoint: base.checkpoint,
134
- user_id: user_id,
135
- buckets: allBuckets.length,
136
- updated: diff.updatedBuckets.length,
137
- removed: diff.removedBuckets.length
138
- });
135
+ deferredLog = () => {
136
+ let message = `Updated checkpoint: ${base.checkpoint} | `;
137
+ message += `write: ${writeCheckpoint} | `;
138
+ message += `buckets: ${allBuckets.length} | `;
139
+ message += `updated: ${limitedBuckets(diff.updatedBuckets, 20)} | `;
140
+ message += `removed: ${limitedBuckets(diff.removedBuckets, 20)}`;
141
+ this.logger.info(message, {
142
+ checkpoint: base.checkpoint,
143
+ user_id: user_id,
144
+ buckets: allBuckets.length,
145
+ updated: diff.updatedBuckets.length,
146
+ removed: diff.removedBuckets.length
147
+ });
148
+ };
139
149
  checkpointLine = {
140
150
  checkpoint_diff: {
141
151
  last_op_id: util.internalToExternalOpId(base.checkpoint),
@@ -146,9 +156,11 @@ export class BucketChecksumState {
146
156
  };
147
157
  }
148
158
  else {
149
- let message = `New checkpoint: ${base.checkpoint} | write: ${writeCheckpoint} | `;
150
- message += `buckets: ${allBuckets.length} ${limitedBuckets(allBuckets, 20)}`;
151
- logger.info(message, { checkpoint: base.checkpoint, user_id: user_id, buckets: allBuckets.length });
159
+ deferredLog = () => {
160
+ let message = `New checkpoint: ${base.checkpoint} | write: ${writeCheckpoint} | `;
161
+ message += `buckets: ${allBuckets.length} ${limitedBuckets(allBuckets, 20)}`;
162
+ this.logger.info(message, { checkpoint: base.checkpoint, user_id: user_id, buckets: allBuckets.length });
163
+ };
152
164
  bucketsToFetch = allBuckets;
153
165
  checkpointLine = {
154
166
  checkpoint: {
@@ -156,49 +168,83 @@ export class BucketChecksumState {
156
168
  write_checkpoint: writeCheckpoint ? String(writeCheckpoint) : undefined,
157
169
  buckets: [...checksumMap.values()].map((e) => ({
158
170
  ...e,
159
- priority: this.bucketDataPositions.get(e.bucket).description.priority
171
+ priority: bucketDescriptionMap.get(e.bucket).priority
160
172
  }))
161
173
  }
162
174
  };
163
175
  }
164
- this.lastChecksums = checksumMap;
165
- this.lastWriteCheckpoint = writeCheckpoint;
166
- this.pendingBucketDownloads = new Set(bucketsToFetch.map((b) => b.bucket));
176
+ const pendingBucketDownloads = new Set(bucketsToFetch.map((b) => b.bucket));
177
+ let hasAdvanced = false;
167
178
  return {
168
179
  checkpointLine,
169
- bucketsToFetch
170
- };
171
- }
172
- /**
173
- * Get bucket positions to sync, given the list of buckets.
174
- *
175
- * @param bucketsToFetch List of buckets to fetch, typically from buildNextCheckpointLine, or a subset of that
176
- * @returns
177
- */
178
- getFilteredBucketPositions(bucketsToFetch) {
179
- const filtered = new Map();
180
- for (let bucket of bucketsToFetch) {
181
- const state = this.bucketDataPositions.get(bucket.bucket);
182
- if (state) {
183
- filtered.set(bucket.bucket, state.start_op_id);
180
+ bucketsToFetch,
181
+ advance: () => {
182
+ hasAdvanced = true;
183
+ // bucketDataPositions must be updated in-place - it represents the current state of
184
+ // the connection, not of the checkpoint line.
185
+ // The following could happen:
186
+ // 1. A = buildCheckpointLine()
187
+ // 2. A.advance()
188
+ // 3. B = buildCheckpointLine()
189
+ // 4. A.updateBucketPosition()
190
+ // 5. B.advance()
191
+ // In that case, it is important that the updated bucket position for A takes effect
192
+ // for checkpoint B.
193
+ let bucketsToRemove = [];
194
+ for (let bucket of this.bucketDataPositions.keys()) {
195
+ if (!bucketDescriptionMap.has(bucket)) {
196
+ bucketsToRemove.push(bucket);
197
+ }
198
+ }
199
+ for (let bucket of bucketsToRemove) {
200
+ this.bucketDataPositions.delete(bucket);
201
+ }
202
+ for (let bucket of allBuckets) {
203
+ if (!this.bucketDataPositions.has(bucket.bucket)) {
204
+ // Bucket the client hasn't seen before - initialize with 0.
205
+ this.bucketDataPositions.set(bucket.bucket, { start_op_id: 0n });
206
+ }
207
+ // If the bucket position is already present, we keep the current position.
208
+ }
209
+ this.lastChecksums = checksumMap;
210
+ this.lastWriteCheckpoint = writeCheckpoint;
211
+ this.pendingBucketDownloads = pendingBucketDownloads;
212
+ deferredLog();
213
+ },
214
+ getFilteredBucketPositions: (buckets) => {
215
+ if (!hasAdvanced) {
216
+ throw new ServiceAssertionError('Call line.advance() before getFilteredBucketPositions()');
217
+ }
218
+ buckets ??= bucketsToFetch;
219
+ const filtered = new Map();
220
+ for (let bucket of buckets) {
221
+ const state = this.bucketDataPositions.get(bucket.bucket);
222
+ if (state) {
223
+ filtered.set(bucket.bucket, state.start_op_id);
224
+ }
225
+ }
226
+ return filtered;
227
+ },
228
+ updateBucketPosition: (options) => {
229
+ if (!hasAdvanced) {
230
+ throw new ServiceAssertionError('Call line.advance() before updateBucketPosition()');
231
+ }
232
+ const state = this.bucketDataPositions.get(options.bucket);
233
+ if (state) {
234
+ state.start_op_id = options.nextAfter;
235
+ }
236
+ else {
237
+ // If we hit this, another checkpoint has removed the bucket in the meantime, meaning
238
+ // line.advance() has been called on it. In that case we don't need the bucket state anymore.
239
+ // It is generally not expected to happen, but we still cover the case.
240
+ }
241
+ if (!options.hasMore) {
242
+ // This specifically updates the per-checkpoint line. Completing a download for one line,
243
+ // does not remove it from the next line, since it could have new updates there.
244
+ pendingBucketDownloads.delete(options.bucket);
245
+ }
184
246
  }
185
- }
186
- return filtered;
187
- }
188
- /**
189
- * Update the position of bucket data the client has.
190
- *
191
- * @param bucket the bucket name
192
- * @param nextAfter sync operations >= this value in the next batch
193
- */
194
- updateBucketPosition(options) {
195
- const state = this.bucketDataPositions.get(options.bucket);
196
- if (state) {
197
- state.start_op_id = options.nextAfter;
198
- }
199
- if (!options.hasMore) {
200
- this.pendingBucketDownloads.delete(options.bucket);
201
- }
247
+ };
202
248
  }
203
249
  }
204
250
  const INVALIDATE_ALL_BUCKETS = Symbol('INVALIDATE_ALL_BUCKETS');
@@ -209,14 +255,16 @@ export class BucketParameterState {
209
255
  syncParams;
210
256
  querier;
211
257
  staticBuckets;
258
+ logger;
212
259
  cachedDynamicBuckets = null;
213
260
  cachedDynamicBucketSet = null;
214
261
  lookups;
215
- constructor(context, bucketStorage, syncRules, syncParams) {
262
+ constructor(context, bucketStorage, syncRules, syncParams, logger) {
216
263
  this.context = context;
217
264
  this.bucketStorage = bucketStorage;
218
265
  this.syncRules = syncRules;
219
266
  this.syncParams = syncParams;
267
+ this.logger = logger;
220
268
  this.querier = syncRules.getBucketParameterQuerier(this.syncParams);
221
269
  this.staticBuckets = new Map(this.querier.staticBuckets.map((b) => [b.bucket, b]));
222
270
  this.lookups = new Set(this.querier.parameterQueryLookups.map((l) => JSONBig.stringify(l.values)));
@@ -234,7 +282,7 @@ export class BucketParameterState {
234
282
  // TODO: Limit number of results even before we get to this point
235
283
  // This limit applies _before_ we get the unique set
236
284
  const error = new ServiceError(ErrorCode.PSYNC_S2305, `Too many parameter query results: ${update.buckets.length} (limit of ${this.context.maxParameterQueryResults})`);
237
- logger.error(error.message, {
285
+ this.logger.error(error.message, {
238
286
  checkpoint: checkpoint,
239
287
  user_id: this.syncParams.user_id,
240
288
  buckets: update.buckets.length