Skip to content

Commit 82385bd

Browse files
committed
Added creation and initialization helper methods for the existing replication, storage and API metrics
Added the MetricsEngine to the service context
1 parent 6a16c3d commit 82385bd

File tree

8 files changed

+187
-6
lines changed

8 files changed

+187
-6
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from './diagnostics.js';
22
export * from './RouteAPI.js';
33
export * from './schema.js';
4+
export * from './api-metrics.js';
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { MetricsEngine } from '../metrics/MetricsEngine.js';
2+
3+
export enum APIMetricType {
4+
// Uncompressed size of synced data from PowerSync to Clients
5+
DATA_SYNCED_BYTES = 'powersync_data_synced_bytes_total',
6+
// Number of operations synced
7+
OPERATIONS_SYNCED_TOTAL = 'powersync_operations_synced_total',
8+
// Number of concurrent sync connections
9+
CONCURRENT_CONNECTIONS = 'powersync_concurrent_connections'
10+
}
11+
12+
/**
13+
* Create and register the core API metrics.
14+
* @param engine
15+
*/
16+
export function createCoreAPIMetrics(engine: MetricsEngine): void {
17+
engine.createCounter({
18+
name: APIMetricType.DATA_SYNCED_BYTES,
19+
description: 'Uncompressed size of synced data',
20+
unit: 'bytes'
21+
});
22+
23+
engine.createCounter({
24+
name: APIMetricType.OPERATIONS_SYNCED_TOTAL,
25+
description: 'Number of operations synced'
26+
});
27+
28+
engine.createUpDownCounter({
29+
name: APIMetricType.CONCURRENT_CONNECTIONS,
30+
description: 'Number of concurrent sync connections'
31+
});
32+
}
33+
34+
/**
35+
* Initialise the core API metrics. This should be called after the metrics have been created.
36+
* @param engine
37+
*/
38+
export function initializeCoreAPIMetrics(engine: MetricsEngine): void {
39+
const concurrent_connections = engine.getUpDownCounter(APIMetricType.CONCURRENT_CONNECTIONS);
40+
41+
// Initialize the metric, so that it reports a value before connections have been opened.
42+
concurrent_connections.add(0);
43+
}

packages/service-core/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ export * as entry from './entry/entry-index.js';
1212
// Re-export framework for easy use of Container API
1313
export * as framework from '@powersync/lib-services-framework';
1414

15-
export * from './metrics/Metrics.js';
16-
export * as metrics from './metrics/Metrics.js';
15+
export * from './metrics/metrics-index.js';
16+
export * as metrics from './metrics/metrics-index.js';
1717

1818
export * from './migrations/migrations-index.js';
1919
export * as migrations from './migrations/migrations-index.js';

packages/service-core/src/replication/replication-index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './AbstractReplicator.js';
33
export * from './ErrorRateLimiter.js';
44
export * from './ReplicationEngine.js';
55
export * from './ReplicationModule.js';
6+
export * from './replication-metrics.js';
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { MetricsEngine } from '../metrics/metrics-index.js';
2+
3+
export enum ReplicationMetricType {
4+
// Uncompressed size of replicated data from data source to PowerSync
5+
DATA_REPLICATED_BYTES = 'powersync_data_replicated_bytes_total',
6+
// Total number of replicated rows. Not used for pricing.
7+
ROWS_REPLICATED_TOTAL = 'powersync_rows_replicated_total',
8+
// Total number of replicated transactions. Not used for pricing.
9+
TRANSACTIONS_REPLICATED_TOTAL = 'powersync_transactions_replicated_total',
10+
// Total number of replication chunks. Not used for pricing.
11+
CHUNKS_REPLICATED_TOTAL = 'powersync_chunks_replicated_total'
12+
}
13+
14+
/**
15+
* Create and register the core replication metrics.
16+
* @param engine
17+
*/
18+
export function createCoreReplicationMetrics(engine: MetricsEngine): void {
19+
engine.createCounter({
20+
name: ReplicationMetricType.DATA_REPLICATED_BYTES,
21+
description: 'Uncompressed size of replicated data',
22+
unit: 'bytes'
23+
});
24+
25+
engine.createCounter({
26+
name: ReplicationMetricType.ROWS_REPLICATED_TOTAL,
27+
description: 'Total number of replicated rows'
28+
});
29+
30+
engine.createCounter({
31+
name: ReplicationMetricType.TRANSACTIONS_REPLICATED_TOTAL,
32+
description: 'Total number of replicated transactions'
33+
});
34+
35+
engine.createCounter({
36+
name: ReplicationMetricType.CHUNKS_REPLICATED_TOTAL,
37+
description: 'Total number of replication chunks'
38+
});
39+
}
40+
41+
/**
42+
* Initialise the core replication metrics. This should be called after the metrics have been created.
43+
* @param engine
44+
*/
45+
export function initializeCoreReplicationMetrics(engine: MetricsEngine): void {
46+
const data_replicated_bytes = engine.getCounter(ReplicationMetricType.DATA_REPLICATED_BYTES);
47+
const rows_replicated_total = engine.getCounter(ReplicationMetricType.ROWS_REPLICATED_TOTAL);
48+
const transactions_replicated_total = engine.getCounter(ReplicationMetricType.TRANSACTIONS_REPLICATED_TOTAL);
49+
const chunks_replicated_total = engine.getCounter(ReplicationMetricType.CHUNKS_REPLICATED_TOTAL);
50+
51+
data_replicated_bytes.add(0);
52+
rows_replicated_total.add(0);
53+
transactions_replicated_total.add(0);
54+
chunks_replicated_total.add(0);
55+
}

packages/service-core/src/storage/storage-index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export * from './SourceEntity.js';
66
export * from './SourceTable.js';
77
export * from './StorageEngine.js';
88
export * from './StorageProvider.js';
9+
export * from './storage-metrics.js';
910
export * from './WriteCheckpointAPI.js';
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { MetricsEngine } from '../metrics/MetricsEngine.js';
2+
import { BucketStorageFactory, StorageMetrics } from './BucketStorage.js';
3+
import { logger } from '@powersync/lib-services-framework';
4+
5+
export enum StorageMetricType {
6+
// Size of current replication data stored in PowerSync
7+
REPLICATION_SIZE_BYTES = 'powersync_replication_storage_size_bytes',
8+
// Size of operations data stored in PowerSync
9+
OPERATION_SIZE_BYTES = 'powersync_operation_storage_size_bytes',
10+
// Size of parameter data stored in PowerSync
11+
PARAMETER_SIZE_BYTES = 'powersync_parameter_storage_size_bytes'
12+
}
13+
14+
export function createCoreStorageMetrics(engine: MetricsEngine): void {
15+
engine.createObservableGauge({
16+
name: StorageMetricType.REPLICATION_SIZE_BYTES,
17+
description: 'Size of current data stored in PowerSync',
18+
unit: 'bytes'
19+
});
20+
21+
engine.createObservableGauge({
22+
name: StorageMetricType.OPERATION_SIZE_BYTES,
23+
description: 'Size of operations stored in PowerSync',
24+
unit: 'bytes'
25+
});
26+
27+
engine.createObservableGauge({
28+
name: StorageMetricType.PARAMETER_SIZE_BYTES,
29+
description: 'Size of parameter data stored in PowerSync',
30+
unit: 'bytes'
31+
});
32+
}
33+
34+
export function initializeCoreStorageMetrics(engine: MetricsEngine, storage: BucketStorageFactory): void {
35+
const replication_storage_size_bytes = engine.getObservableGauge(StorageMetricType.REPLICATION_SIZE_BYTES);
36+
const operation_storage_size_bytes = engine.getObservableGauge(StorageMetricType.OPERATION_SIZE_BYTES);
37+
const parameter_storage_size_bytes = engine.getObservableGauge(StorageMetricType.PARAMETER_SIZE_BYTES);
38+
39+
const MINIMUM_INTERVAL = 60_000;
40+
41+
let cachedRequest: Promise<StorageMetrics | null> | undefined = undefined;
42+
let cacheTimestamp = 0;
43+
44+
const getMetrics = () => {
45+
if (cachedRequest == null || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) {
46+
cachedRequest = storage.getStorageMetrics().catch((e) => {
47+
logger.error(`Failed to get storage metrics`, e);
48+
return null;
49+
});
50+
cacheTimestamp = Date.now();
51+
}
52+
return cachedRequest;
53+
};
54+
55+
replication_storage_size_bytes.setValueProvider(async () => {
56+
const metrics = await getMetrics();
57+
if (metrics) {
58+
return metrics.replication_size_bytes;
59+
}
60+
});
61+
62+
operation_storage_size_bytes.setValueProvider(async () => {
63+
const metrics = await getMetrics();
64+
if (metrics) {
65+
return metrics.operations_size_bytes;
66+
}
67+
});
68+
69+
parameter_storage_size_bytes.setValueProvider(async () => {
70+
const metrics = await getMetrics();
71+
if (metrics) {
72+
return metrics.parameters_size_bytes;
73+
}
74+
});
75+
}

packages/service-core/src/system/ServiceContext.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LifeCycledSystem, MigrationManager, ServiceIdentifier, container } from '@powersync/lib-services-framework';
22

33
import { framework } from '../index.js';
4-
import * as metrics from '../metrics/Metrics.js';
4+
import * as metrics from '../metrics/MetricsEngine.js';
55
import { PowerSyncMigrationManager } from '../migrations/PowerSyncMigrationManager.js';
66
import * as replication from '../replication/replication-index.js';
77
import * as routes from '../routes/routes-index.js';
@@ -11,7 +11,7 @@ import * as utils from '../util/util-index.js';
1111
export interface ServiceContext {
1212
configuration: utils.ResolvedPowerSyncConfig;
1313
lifeCycleEngine: LifeCycledSystem;
14-
metrics: metrics.Metrics | null;
14+
metricsEngine: metrics.MetricsEngine;
1515
replicationEngine: replication.ReplicationEngine | null;
1616
routerEngine: routes.RouterEngine | null;
1717
storageEngine: storage.StorageEngine;
@@ -34,6 +34,11 @@ export class ServiceContextContainer implements ServiceContext {
3434
configuration
3535
});
3636

37+
this.lifeCycleEngine.withLifecycle(this.storageEngine, {
38+
start: (storageEngine) => storageEngine.start(),
39+
stop: (storageEngine) => storageEngine.shutDown()
40+
});
41+
3742
const migrationManager = new MigrationManager();
3843
container.register(framework.ContainerImplementation.MIGRATION_MANAGER, migrationManager);
3944

@@ -56,8 +61,8 @@ export class ServiceContextContainer implements ServiceContext {
5661
return container.getOptional(routes.RouterEngine);
5762
}
5863

59-
get metrics(): metrics.Metrics | null {
60-
return container.getOptional(metrics.Metrics);
64+
get metricsEngine(): metrics.MetricsEngine {
65+
return container.getImplementation(metrics.MetricsEngine);
6166
}
6267

6368
get migrations(): PowerSyncMigrationManager {

0 commit comments

Comments
 (0)