@powersync/service-core 0.0.0-dev-20250317122913 → 0.0.0-dev-20250326092547
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 +18 -4
- package/dist/api/api-index.d.ts +1 -0
- package/dist/api/api-index.js +1 -0
- package/dist/api/api-index.js.map +1 -1
- package/dist/api/api-metrics.d.ts +11 -0
- package/dist/api/api-metrics.js +30 -0
- package/dist/api/api-metrics.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/metrics/MetricsEngine.d.ts +21 -0
- package/dist/metrics/MetricsEngine.js +79 -0
- package/dist/metrics/MetricsEngine.js.map +1 -0
- package/dist/metrics/metrics-index.d.ts +5 -0
- package/dist/metrics/metrics-index.js +6 -0
- package/dist/metrics/metrics-index.js.map +1 -0
- package/dist/metrics/metrics-interfaces.d.ts +36 -0
- package/dist/metrics/metrics-interfaces.js +6 -0
- package/dist/metrics/metrics-interfaces.js.map +1 -0
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.d.ts +10 -0
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js +51 -0
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js.map +1 -0
- package/dist/metrics/open-telemetry/util.d.ts +6 -0
- package/dist/metrics/open-telemetry/util.js +56 -0
- package/dist/metrics/open-telemetry/util.js.map +1 -0
- package/dist/metrics/register-metrics.d.ts +11 -0
- package/dist/metrics/register-metrics.js +44 -0
- package/dist/metrics/register-metrics.js.map +1 -0
- package/dist/replication/AbstractReplicationJob.d.ts +2 -0
- package/dist/replication/AbstractReplicationJob.js.map +1 -1
- package/dist/replication/AbstractReplicator.d.ts +3 -0
- package/dist/replication/AbstractReplicator.js +3 -0
- package/dist/replication/AbstractReplicator.js.map +1 -1
- package/dist/replication/ReplicationModule.d.ts +7 -0
- package/dist/replication/ReplicationModule.js +1 -0
- package/dist/replication/ReplicationModule.js.map +1 -1
- 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.d.ts +11 -0
- package/dist/replication/replication-metrics.js +39 -0
- package/dist/replication/replication-metrics.js.map +1 -0
- package/dist/routes/configure-fastify.d.ts +1 -1
- package/dist/routes/endpoints/probes.d.ts +2 -2
- package/dist/routes/endpoints/probes.js +16 -2
- package/dist/routes/endpoints/probes.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +5 -5
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +6 -6
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/storage/SyncRulesBucketStorage.d.ts +11 -1
- package/dist/storage/SyncRulesBucketStorage.js +1 -1
- package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
- package/dist/storage/WriteCheckpointAPI.d.ts +0 -2
- package/dist/storage/WriteCheckpointAPI.js.map +1 -1
- package/dist/storage/storage-index.d.ts +1 -0
- package/dist/storage/storage-index.js +1 -0
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/storage/storage-metrics.d.ts +4 -0
- package/dist/storage/storage-metrics.js +56 -0
- package/dist/storage/storage-metrics.js.map +1 -0
- package/dist/sync/BucketChecksumState.d.ts +4 -2
- package/dist/sync/BucketChecksumState.js +17 -26
- package/dist/sync/BucketChecksumState.js.map +1 -1
- package/dist/sync/RequestTracker.d.ts +3 -0
- package/dist/sync/RequestTracker.js +8 -3
- package/dist/sync/RequestTracker.js.map +1 -1
- package/dist/sync/util.d.ts +10 -2
- package/dist/sync/util.js +25 -6
- package/dist/sync/util.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +3 -3
- package/dist/system/ServiceContext.js +7 -3
- package/dist/system/ServiceContext.js.map +1 -1
- package/package.json +8 -8
- package/src/api/api-index.ts +1 -0
- package/src/api/api-metrics.ts +35 -0
- package/src/index.ts +2 -2
- package/src/metrics/MetricsEngine.ts +98 -0
- package/src/metrics/metrics-index.ts +5 -0
- package/src/metrics/metrics-interfaces.ts +41 -0
- package/src/metrics/open-telemetry/OpenTelemetryMetricsFactory.ts +66 -0
- package/src/metrics/open-telemetry/util.ts +74 -0
- package/src/metrics/register-metrics.ts +56 -0
- package/src/replication/AbstractReplicationJob.ts +2 -0
- package/src/replication/AbstractReplicator.ts +7 -0
- package/src/replication/ReplicationModule.ts +10 -0
- package/src/replication/replication-index.ts +1 -0
- package/src/replication/replication-metrics.ts +45 -0
- package/src/routes/endpoints/probes.ts +18 -2
- package/src/routes/endpoints/socket-route.ts +6 -5
- package/src/routes/endpoints/sync-stream.ts +7 -6
- package/src/storage/SyncRulesBucketStorage.ts +12 -2
- package/src/storage/WriteCheckpointAPI.ts +0 -2
- package/src/storage/storage-index.ts +1 -0
- package/src/storage/storage-metrics.ts +67 -0
- package/src/sync/BucketChecksumState.ts +25 -41
- package/src/sync/RequestTracker.ts +9 -3
- package/src/sync/util.ts +29 -8
- package/src/system/ServiceContext.ts +9 -4
- package/test/src/routes/probes.integration.test.ts +5 -5
- package/test/src/routes/probes.test.ts +5 -4
- package/test/src/sync/BucketChecksumState.test.ts +5 -5
- package/test/src/util.test.ts +48 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/metrics/Metrics.d.ts +0 -30
- package/dist/metrics/Metrics.js +0 -202
- package/dist/metrics/Metrics.js.map +0 -1
- package/src/metrics/Metrics.ts +0 -255
package/src/sync/util.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as timers from 'timers/promises';
|
|
2
2
|
|
|
3
|
+
import { SemaphoreInterface } from 'async-mutex';
|
|
3
4
|
import * as util from '../util/util-index.js';
|
|
4
5
|
import { RequestTracker } from './RequestTracker.js';
|
|
5
|
-
import { SemaphoreInterface } from 'async-mutex';
|
|
6
6
|
|
|
7
7
|
export type TokenStreamOptions = {
|
|
8
8
|
/**
|
|
@@ -154,14 +154,35 @@ export function settledPromise<T>(promise: Promise<T>): Promise<PromiseSettledRe
|
|
|
154
154
|
);
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
export
|
|
158
|
-
|
|
159
|
-
|
|
157
|
+
export type MapOrSet<T> = Map<T, any> | Set<T>;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if two sets have any element(s) in common.
|
|
161
|
+
*/
|
|
162
|
+
export function hasIntersection<T>(a: MapOrSet<T>, b: MapOrSet<T>) {
|
|
163
|
+
for (let _ of getIntersection(a, b)) {
|
|
164
|
+
return true;
|
|
160
165
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Return the intersection of two sets or maps.
|
|
171
|
+
*/
|
|
172
|
+
export function* getIntersection<T>(a: MapOrSet<T>, b: MapOrSet<T>): IterableIterator<T> {
|
|
173
|
+
// Iterate over the smaller set to reduce the number of lookups
|
|
174
|
+
if (a.size < b.size) {
|
|
175
|
+
for (let key of a.keys()) {
|
|
176
|
+
if (b.has(key)) {
|
|
177
|
+
yield key;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
} else {
|
|
182
|
+
for (let key of b.keys()) {
|
|
183
|
+
if (a.has(key)) {
|
|
184
|
+
yield key;
|
|
185
|
+
}
|
|
165
186
|
}
|
|
166
187
|
}
|
|
167
188
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LifeCycledSystem, MigrationManager, ServiceIdentifier, container } from '@powersync/lib-services-framework';
|
|
2
2
|
|
|
3
3
|
import { framework } from '../index.js';
|
|
4
|
-
import * as metrics from '../metrics/
|
|
4
|
+
import * as metrics from '../metrics/MetricsEngine.js';
|
|
5
5
|
import { PowerSyncMigrationManager } from '../migrations/PowerSyncMigrationManager.js';
|
|
6
6
|
import * as replication from '../replication/replication-index.js';
|
|
7
7
|
import * as routes from '../routes/routes-index.js';
|
|
@@ -12,7 +12,7 @@ import { SyncContext } from '../sync/SyncContext.js';
|
|
|
12
12
|
export interface ServiceContext {
|
|
13
13
|
configuration: utils.ResolvedPowerSyncConfig;
|
|
14
14
|
lifeCycleEngine: LifeCycledSystem;
|
|
15
|
-
|
|
15
|
+
metricsEngine: metrics.MetricsEngine;
|
|
16
16
|
replicationEngine: replication.ReplicationEngine | null;
|
|
17
17
|
routerEngine: routes.RouterEngine | null;
|
|
18
18
|
storageEngine: storage.StorageEngine;
|
|
@@ -37,6 +37,11 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
37
37
|
configuration
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
this.lifeCycleEngine.withLifecycle(this.storageEngine, {
|
|
41
|
+
start: (storageEngine) => storageEngine.start(),
|
|
42
|
+
stop: (storageEngine) => storageEngine.shutDown()
|
|
43
|
+
});
|
|
44
|
+
|
|
40
45
|
this.syncContext = new SyncContext({
|
|
41
46
|
maxDataFetchConcurrency: configuration.api_parameters.max_data_fetch_concurrency,
|
|
42
47
|
maxBuckets: configuration.api_parameters.max_buckets_per_connection,
|
|
@@ -65,8 +70,8 @@ export class ServiceContextContainer implements ServiceContext {
|
|
|
65
70
|
return container.getOptional(routes.RouterEngine);
|
|
66
71
|
}
|
|
67
72
|
|
|
68
|
-
get
|
|
69
|
-
return container.
|
|
73
|
+
get metricsEngine(): metrics.MetricsEngine {
|
|
74
|
+
return container.getImplementation(metrics.MetricsEngine);
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
get migrations(): PowerSyncMigrationManager {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import Fastify, { FastifyInstance } from 'fastify';
|
|
3
1
|
import { container } from '@powersync/lib-services-framework';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
2
|
+
import Fastify, { FastifyInstance } from 'fastify';
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
6
4
|
import { configureFastifyServer } from '../../../src/index.js';
|
|
5
|
+
import * as auth from '../../../src/routes/auth.js';
|
|
7
6
|
import { ProbeRoutes } from '../../../src/routes/endpoints/probes.js';
|
|
7
|
+
import * as system from '../../../src/system/system-index.js';
|
|
8
8
|
|
|
9
9
|
vi.mock('@powersync/lib-services-framework', async () => {
|
|
10
10
|
const actual = (await vi.importActual('@powersync/lib-services-framework')) as any;
|
|
@@ -25,7 +25,7 @@ describe('Probe Routes Integration', () => {
|
|
|
25
25
|
|
|
26
26
|
beforeEach(async () => {
|
|
27
27
|
app = Fastify();
|
|
28
|
-
mockSystem = { routerEngine: {} } as system.ServiceContext;
|
|
28
|
+
mockSystem = { routerEngine: {}, replicationEngine: {} } as system.ServiceContext;
|
|
29
29
|
await configureFastifyServer(app, { service_context: mockSystem });
|
|
30
30
|
await app.ready();
|
|
31
31
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
1
|
import { container } from '@powersync/lib-services-framework';
|
|
3
|
-
import {
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import { livenessCheck, readinessCheck, startupCheck } from '../../../src/routes/endpoints/probes.js';
|
|
4
4
|
|
|
5
5
|
// Mock the container
|
|
6
6
|
vi.mock('@powersync/lib-services-framework', () => ({
|
|
@@ -83,6 +83,7 @@ describe('Probe Routes', () => {
|
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
describe('livenessCheck', () => {
|
|
86
|
+
const mockedContext = { context: { service_context: { replicationEngine: {} } } } as any;
|
|
86
87
|
it('has the correct route definitions', () => {
|
|
87
88
|
expect(livenessCheck.path).toBe('/probes/liveness');
|
|
88
89
|
expect(livenessCheck.method).toBe('GET');
|
|
@@ -97,7 +98,7 @@ describe('Probe Routes', () => {
|
|
|
97
98
|
|
|
98
99
|
vi.mocked(container.probes.state).mockReturnValue(mockState);
|
|
99
100
|
|
|
100
|
-
const response = await livenessCheck.handler();
|
|
101
|
+
const response = await livenessCheck.handler(mockedContext);
|
|
101
102
|
|
|
102
103
|
expect(response.status).toBe(200);
|
|
103
104
|
expect(response.data).toEqual(mockState);
|
|
@@ -112,7 +113,7 @@ describe('Probe Routes', () => {
|
|
|
112
113
|
|
|
113
114
|
vi.mocked(container.probes.state).mockReturnValue(mockState);
|
|
114
115
|
|
|
115
|
-
const response = await livenessCheck.handler();
|
|
116
|
+
const response = await livenessCheck.handler(mockedContext);
|
|
116
117
|
|
|
117
118
|
expect(response.status).toBe(400);
|
|
118
119
|
expect(response.data).toEqual(mockState);
|
|
@@ -98,7 +98,7 @@ bucket_definitions:
|
|
|
98
98
|
base: { checkpoint: 2n, lsn: '2' },
|
|
99
99
|
writeCheckpoint: null,
|
|
100
100
|
update: {
|
|
101
|
-
updatedDataBuckets: ['global[]'],
|
|
101
|
+
updatedDataBuckets: new Set(['global[]']),
|
|
102
102
|
invalidateDataBuckets: false,
|
|
103
103
|
updatedParameterLookups: new Set(),
|
|
104
104
|
invalidateParameterBuckets: false
|
|
@@ -201,7 +201,7 @@ bucket_definitions:
|
|
|
201
201
|
writeCheckpoint: null,
|
|
202
202
|
update: {
|
|
203
203
|
...CHECKPOINT_INVALIDATE_ALL,
|
|
204
|
-
updatedDataBuckets: ['global[1]', 'global[2]'],
|
|
204
|
+
updatedDataBuckets: new Set(['global[1]', 'global[2]']),
|
|
205
205
|
invalidateDataBuckets: false
|
|
206
206
|
}
|
|
207
207
|
}))!;
|
|
@@ -294,7 +294,7 @@ bucket_definitions:
|
|
|
294
294
|
// Invalidate the state for global[1] - will only re-check the single bucket.
|
|
295
295
|
// This is essentially inconsistent state, but is the simplest way to test that
|
|
296
296
|
// the filter is working.
|
|
297
|
-
updatedDataBuckets: ['global[1]'],
|
|
297
|
+
updatedDataBuckets: new Set(['global[1]']),
|
|
298
298
|
invalidateDataBuckets: false
|
|
299
299
|
}
|
|
300
300
|
}))!;
|
|
@@ -421,7 +421,7 @@ bucket_definitions:
|
|
|
421
421
|
update: {
|
|
422
422
|
...CHECKPOINT_INVALIDATE_ALL,
|
|
423
423
|
invalidateDataBuckets: false,
|
|
424
|
-
updatedDataBuckets: ['global[1]']
|
|
424
|
+
updatedDataBuckets: new Set(['global[1]'])
|
|
425
425
|
}
|
|
426
426
|
}))!;
|
|
427
427
|
expect(line2.checkpointLine).toEqual({
|
|
@@ -533,7 +533,7 @@ bucket_definitions:
|
|
|
533
533
|
writeCheckpoint: null,
|
|
534
534
|
update: {
|
|
535
535
|
invalidateDataBuckets: false,
|
|
536
|
-
updatedDataBuckets:
|
|
536
|
+
updatedDataBuckets: new Set(),
|
|
537
537
|
updatedParameterLookups: new Set([JSONBig.stringify(['by_project', '1', 'u1'])]),
|
|
538
538
|
invalidateParameterBuckets: false
|
|
539
539
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getIntersection, hasIntersection } from '@/index.js';
|
|
2
|
+
import { describe, expect, test } from 'vitest';
|
|
3
|
+
|
|
4
|
+
describe('utils', () => {
|
|
5
|
+
function testInstersection(a: Set<any>, b: Set<any>, expected: boolean) {
|
|
6
|
+
expect(hasIntersection(a, b)).toBe(expected);
|
|
7
|
+
expect(hasIntersection(b, a)).toBe(expected);
|
|
8
|
+
const mapA = new Map([...a].map((v) => [v, 1]));
|
|
9
|
+
const mapB = new Map([...b].map((v) => [v, 1]));
|
|
10
|
+
expect(hasIntersection(mapA, b)).toBe(expected);
|
|
11
|
+
expect(hasIntersection(mapB, a)).toBe(expected);
|
|
12
|
+
expect(hasIntersection(mapA, mapB)).toBe(expected);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test('hasIntersection', async () => {
|
|
16
|
+
testInstersection(new Set(['a']), new Set(['a']), true);
|
|
17
|
+
testInstersection(new Set(['a', 'b', 'c']), new Set(['a', 'b', 'c']), true);
|
|
18
|
+
testInstersection(new Set(['a', 'b', 'c']), new Set(['d', 'e']), false);
|
|
19
|
+
testInstersection(new Set(['a', 'b', 'c']), new Set(['d', 'c', 'e']), true);
|
|
20
|
+
testInstersection(new Set(['a', 'b', 'c']), new Set(['c', 'e']), true);
|
|
21
|
+
testInstersection(new Set(['a', 'b', 'c', 2]), new Set([1, 2, 3]), true);
|
|
22
|
+
testInstersection(new Set(['a', 'b', 'c', 4]), new Set([1, 2, 3]), false);
|
|
23
|
+
testInstersection(new Set([]), new Set([1, 2, 3]), false);
|
|
24
|
+
testInstersection(new Set([]), new Set([]), false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
function testGetIntersection(a: Set<any>, b: Set<any>, expected: any[]) {
|
|
28
|
+
expect([...getIntersection(a, b)]).toEqual(expected);
|
|
29
|
+
expect([...getIntersection(b, a)]).toEqual(expected);
|
|
30
|
+
const mapA = new Map([...a].map((v) => [v, 1]));
|
|
31
|
+
const mapB = new Map([...b].map((v) => [v, 1]));
|
|
32
|
+
expect([...getIntersection(mapA, b)]).toEqual(expected);
|
|
33
|
+
expect([...getIntersection(mapB, a)]).toEqual(expected);
|
|
34
|
+
expect([...getIntersection(mapA, mapB)]).toEqual(expected);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test('getIntersection', async () => {
|
|
38
|
+
testGetIntersection(new Set(['a']), new Set(['a']), ['a']);
|
|
39
|
+
testGetIntersection(new Set(['a', 'b', 'c']), new Set(['a', 'b', 'c']), ['a', 'b', 'c']);
|
|
40
|
+
testGetIntersection(new Set(['a', 'b', 'c']), new Set(['d', 'e']), []);
|
|
41
|
+
testGetIntersection(new Set(['a', 'b', 'c']), new Set(['d', 'c', 'e']), ['c']);
|
|
42
|
+
testGetIntersection(new Set(['a', 'b', 'c']), new Set(['c', 'e']), ['c']);
|
|
43
|
+
testGetIntersection(new Set(['a', 'b', 'c', 2]), new Set([1, 2, 3]), [2]);
|
|
44
|
+
testGetIntersection(new Set(['a', 'b', 'c', 4]), new Set([1, 2, 3]), []);
|
|
45
|
+
testGetIntersection(new Set([]), new Set([1, 2, 3]), []);
|
|
46
|
+
testGetIntersection(new Set([]), new Set([]), []);
|
|
47
|
+
});
|
|
48
|
+
});
|