@powersync/service-core 0.0.0-dev-20260602073133 → 0.0.0-dev-20260909133214
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 +152 -4
- package/dist/api/RouteAPI.d.ts +42 -14
- package/dist/api/diagnostics.d.ts +12 -1
- package/dist/api/diagnostics.js +36 -22
- package/dist/api/diagnostics.js.map +1 -1
- package/dist/entry/commands/compact-action.js +30 -22
- package/dist/entry/commands/compact-action.js.map +1 -1
- package/dist/metrics/metrics-interfaces.d.ts +5 -1
- package/dist/metrics/metrics-interfaces.js.map +1 -1
- package/dist/metrics/open-telemetry/MetricAttributeFilteringExporter.d.ts +36 -0
- package/dist/metrics/open-telemetry/MetricAttributeFilteringExporter.js +70 -0
- package/dist/metrics/open-telemetry/MetricAttributeFilteringExporter.js.map +1 -0
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js +6 -1
- package/dist/metrics/open-telemetry/OpenTelemetryMetricsFactory.js.map +1 -1
- package/dist/metrics/open-telemetry/util.js +7 -3
- package/dist/metrics/open-telemetry/util.js.map +1 -1
- package/dist/modules/AbstractModule.d.ts +6 -2
- package/dist/modules/AbstractModule.js.map +1 -1
- package/dist/replication/AbstractReplicator.d.ts +31 -7
- package/dist/replication/AbstractReplicator.js +168 -74
- package/dist/replication/AbstractReplicator.js.map +1 -1
- package/dist/replication/ReplicationModule.d.ts +3 -3
- package/dist/replication/ReplicationModule.js.map +1 -1
- package/dist/routes/RouterEngine.d.ts +1 -0
- package/dist/routes/RouterEngine.js +6 -1
- package/dist/routes/RouterEngine.js.map +1 -1
- package/dist/routes/configure-fastify.d.ts +104 -56
- package/dist/routes/configure-fastify.js +15 -2
- package/dist/routes/configure-fastify.js.map +1 -1
- package/dist/routes/endpoints/admin.d.ts +42 -24
- package/dist/routes/endpoints/admin.js +49 -36
- package/dist/routes/endpoints/admin.js.map +1 -1
- package/dist/routes/endpoints/checkpointing.d.ts +62 -0
- package/dist/routes/endpoints/checkpointing.js +65 -7
- package/dist/routes/endpoints/checkpointing.js.map +1 -1
- package/dist/routes/endpoints/socket-route.js +3 -2
- package/dist/routes/endpoints/socket-route.js.map +1 -1
- package/dist/routes/endpoints/sync-rules.js +25 -14
- package/dist/routes/endpoints/sync-rules.js.map +1 -1
- package/dist/routes/endpoints/sync-stream.js +1 -1
- package/dist/routes/endpoints/sync-stream.js.map +1 -1
- package/dist/runner/teardown.js +8 -8
- package/dist/runner/teardown.js.map +1 -1
- package/dist/storage/BucketStorageBatch.d.ts +64 -17
- package/dist/storage/BucketStorageBatch.js.map +1 -1
- package/dist/storage/BucketStorageFactory.d.ts +50 -31
- package/dist/storage/BucketStorageFactory.js +10 -20
- package/dist/storage/BucketStorageFactory.js.map +1 -1
- package/dist/storage/CheckpointChecksumInvalidatedError.d.ts +12 -0
- package/dist/storage/CheckpointChecksumInvalidatedError.js +17 -0
- package/dist/storage/CheckpointChecksumInvalidatedError.js.map +1 -0
- package/dist/storage/ChecksumCache.d.ts +11 -6
- package/dist/storage/ChecksumCache.js +5 -4
- package/dist/storage/ChecksumCache.js.map +1 -1
- package/dist/storage/ParsedSyncConfigSet.d.ts +23 -0
- package/dist/storage/ParsedSyncConfigSet.js +2 -0
- package/dist/storage/ParsedSyncConfigSet.js.map +1 -0
- package/dist/storage/PersistedReplicationStream.d.ts +32 -0
- package/dist/storage/PersistedReplicationStream.js +26 -0
- package/dist/storage/PersistedReplicationStream.js.map +1 -0
- package/dist/storage/PersistedSyncConfigContent.d.ts +65 -0
- package/dist/storage/PersistedSyncConfigContent.js +138 -0
- package/dist/storage/PersistedSyncConfigContent.js.map +1 -0
- package/dist/storage/PersistedSyncConfigStatus.d.ts +13 -0
- package/dist/storage/PersistedSyncConfigStatus.js +2 -0
- package/dist/storage/PersistedSyncConfigStatus.js.map +1 -0
- package/dist/storage/ReplicationEventPayload.d.ts +6 -1
- package/dist/storage/SourceEntity.d.ts +13 -2
- package/dist/storage/SourceTable.d.ts +53 -4
- package/dist/storage/SourceTable.js +62 -7
- package/dist/storage/SourceTable.js.map +1 -1
- package/dist/storage/SourceTableReconciler.d.ts +83 -0
- package/dist/storage/SourceTableReconciler.js +95 -0
- package/dist/storage/SourceTableReconciler.js.map +1 -0
- package/dist/storage/StorageVersionConfig.d.ts +4 -0
- package/dist/storage/StorageVersionConfig.js +10 -1
- package/dist/storage/StorageVersionConfig.js.map +1 -1
- package/dist/storage/SyncRulesBucketStorage.d.ts +162 -26
- package/dist/storage/SyncRulesBucketStorage.js +3 -0
- package/dist/storage/SyncRulesBucketStorage.js.map +1 -1
- package/dist/storage/WriteCheckpointAPI.d.ts +85 -6
- package/dist/storage/WriteCheckpointAPI.js +33 -0
- package/dist/storage/WriteCheckpointAPI.js.map +1 -1
- package/dist/storage/implementation/BucketDefinitionMapping.d.ts +133 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js +351 -0
- package/dist/storage/implementation/BucketDefinitionMapping.js.map +1 -0
- package/dist/storage/implementation/IncrementalReprocessingCompatibility.d.ts +6 -0
- package/dist/storage/implementation/IncrementalReprocessingCompatibility.js +31 -0
- package/dist/storage/implementation/IncrementalReprocessingCompatibility.js.map +1 -0
- package/dist/storage/implementation/IncrementalReprocessingSyncConfigLog.d.ts +17 -0
- package/dist/storage/implementation/IncrementalReprocessingSyncConfigLog.js +70 -0
- package/dist/storage/implementation/IncrementalReprocessingSyncConfigLog.js.map +1 -0
- package/dist/storage/implementation/ReplicationStreamStorageIds.d.ts +15 -0
- package/dist/storage/implementation/ReplicationStreamStorageIds.js +20 -0
- package/dist/storage/implementation/ReplicationStreamStorageIds.js.map +1 -0
- package/dist/storage/storage-index.d.ts +10 -1
- package/dist/storage/storage-index.js +10 -1
- package/dist/storage/storage-index.js.map +1 -1
- package/dist/storage/storage-metrics.js +120 -4
- package/dist/storage/storage-metrics.js.map +1 -1
- package/dist/sync/BucketChecksumState.d.ts +12 -2
- package/dist/sync/BucketChecksumState.js +69 -21
- package/dist/sync/BucketChecksumState.js.map +1 -1
- package/dist/sync/RequestTracker.d.ts +7 -0
- package/dist/sync/RequestTracker.js +12 -0
- package/dist/sync/RequestTracker.js.map +1 -1
- package/dist/sync/sync.js +184 -43
- package/dist/sync/sync.js.map +1 -1
- package/dist/sync/util.d.ts +5 -0
- package/dist/sync/util.js +19 -1
- package/dist/sync/util.js.map +1 -1
- package/dist/system/ServiceContext.d.ts +2 -0
- package/dist/system/ServiceContext.js +2 -0
- package/dist/system/ServiceContext.js.map +1 -1
- package/dist/tracing/PerformanceTracer.d.ts +23 -1
- package/dist/tracing/PerformanceTracer.js +11 -0
- package/dist/tracing/PerformanceTracer.js.map +1 -1
- package/dist/util/checkpointing.d.ts +4 -8
- package/dist/util/checkpointing.js +1 -16
- package/dist/util/checkpointing.js.map +1 -1
- package/dist/util/config/compound-config-collector.js +23 -2
- package/dist/util/config/compound-config-collector.js.map +1 -1
- package/dist/util/config/defaults.d.ts +2 -0
- package/dist/util/config/defaults.js +2 -0
- package/dist/util/config/defaults.js.map +1 -1
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js +1 -0
- package/dist/util/config/sync-rules/impl/base64-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js +1 -0
- package/dist/util/config/sync-rules/impl/filesystem-sync-rules-collector.js.map +1 -1
- package/dist/util/config/sync-rules/sync-rules-provider.d.ts +2 -0
- package/dist/util/config/sync-rules/sync-rules-provider.js +3 -0
- package/dist/util/config/sync-rules/sync-rules-provider.js.map +1 -1
- package/dist/util/config/types.d.ts +4 -1
- package/dist/util/lsn.d.ts +4 -0
- package/dist/util/lsn.js +10 -0
- package/dist/util/lsn.js.map +1 -1
- package/dist/util/protocol-types.d.ts +4 -4
- package/dist/util/protocol-types.js +3 -2
- package/dist/util/protocol-types.js.map +1 -1
- package/dist/util/util-index.d.ts +1 -0
- package/dist/util/util-index.js +1 -0
- package/dist/util/util-index.js.map +1 -1
- package/dist/util/utils.d.ts +3 -2
- package/dist/util/utils.js +17 -2
- package/dist/util/utils.js.map +1 -1
- package/dist/util/write-checkpoint-batcher.d.ts +19 -0
- package/dist/util/write-checkpoint-batcher.js +100 -0
- package/dist/util/write-checkpoint-batcher.js.map +1 -0
- package/package.json +7 -7
- package/src/api/RouteAPI.ts +43 -14
- package/src/api/diagnostics.ts +45 -26
- package/src/entry/commands/compact-action.ts +37 -23
- package/src/metrics/metrics-interfaces.ts +6 -1
- package/src/metrics/open-telemetry/MetricAttributeFilteringExporter.ts +85 -0
- package/src/metrics/open-telemetry/OpenTelemetryMetricsFactory.ts +7 -2
- package/src/metrics/open-telemetry/util.ts +7 -3
- package/src/modules/AbstractModule.ts +6 -2
- package/src/replication/AbstractReplicator.ts +212 -78
- package/src/replication/ReplicationModule.ts +2 -3
- package/src/routes/RouterEngine.ts +7 -1
- package/src/routes/configure-fastify.ts +25 -2
- package/src/routes/endpoints/admin.ts +75 -51
- package/src/routes/endpoints/checkpointing.ts +79 -8
- package/src/routes/endpoints/socket-route.ts +3 -2
- package/src/routes/endpoints/sync-rules.ts +26 -14
- package/src/routes/endpoints/sync-stream.ts +1 -1
- package/src/runner/teardown.ts +8 -8
- package/src/storage/BucketStorageBatch.ts +65 -18
- package/src/storage/BucketStorageFactory.ts +73 -46
- package/src/storage/CheckpointChecksumInvalidatedError.ts +17 -0
- package/src/storage/ChecksumCache.ts +27 -14
- package/src/storage/ParsedSyncConfigSet.ts +25 -0
- package/src/storage/PersistedReplicationStream.ts +55 -0
- package/src/storage/PersistedSyncConfigContent.ts +207 -0
- package/src/storage/PersistedSyncConfigStatus.ts +14 -0
- package/src/storage/ReplicationEventPayload.ts +6 -1
- package/src/storage/SourceEntity.ts +12 -2
- package/src/storage/SourceTable.ts +96 -8
- package/src/storage/SourceTableReconciler.ts +188 -0
- package/src/storage/StorageVersionConfig.ts +11 -1
- package/src/storage/SyncRulesBucketStorage.ts +189 -27
- package/src/storage/WriteCheckpointAPI.ts +140 -6
- package/src/storage/implementation/BucketDefinitionMapping.ts +539 -0
- package/src/storage/implementation/IncrementalReprocessingCompatibility.ts +41 -0
- package/src/storage/implementation/IncrementalReprocessingSyncConfigLog.ts +126 -0
- package/src/storage/implementation/ReplicationStreamStorageIds.ts +21 -0
- package/src/storage/storage-index.ts +11 -1
- package/src/storage/storage-metrics.ts +154 -8
- package/src/sync/BucketChecksumState.ts +105 -21
- package/src/sync/RequestTracker.ts +20 -0
- package/src/sync/sync.ts +214 -50
- package/src/sync/util.ts +33 -8
- package/src/system/ServiceContext.ts +7 -0
- package/src/tracing/PerformanceTracer.ts +41 -1
- package/src/util/checkpointing.ts +4 -22
- package/src/util/config/compound-config-collector.ts +31 -1
- package/src/util/config/defaults.ts +2 -0
- package/src/util/config/sync-rules/impl/base64-sync-rules-collector.ts +1 -0
- package/src/util/config/sync-rules/impl/filesystem-sync-rules-collector.ts +1 -0
- package/src/util/config/sync-rules/sync-rules-provider.ts +5 -0
- package/src/util/config/types.ts +4 -1
- package/src/util/lsn.ts +9 -0
- package/src/util/protocol-types.ts +3 -2
- package/src/util/util-index.ts +1 -0
- package/src/util/utils.ts +16 -2
- package/src/util/write-checkpoint-batcher.ts +142 -0
- package/test/src/AbstractReplicator.test.ts +177 -0
- package/test/src/MetricAttributeFilteringExporter.test.ts +96 -0
- package/test/src/PersistedSyncConfigContent.test.ts +152 -0
- package/test/src/checksum_cache.test.ts +22 -0
- package/test/src/config.test.ts +142 -0
- package/test/src/diagnostics.test.ts +87 -34
- package/test/src/module-loader.test.ts +33 -1
- package/test/src/routes/admin.test.ts +188 -18
- package/test/src/routes/checkpointing.test.ts +54 -0
- package/test/src/routes/mocks.ts +6 -2
- package/test/src/routes/stream.test.ts +123 -2
- package/test/src/source-table-reconciler.test.ts +244 -0
- package/test/src/storage/SourceTable.test.ts +92 -0
- package/test/src/storage-metrics.test.ts +148 -0
- package/test/src/sync/BucketChecksumState.test.ts +98 -12
- package/test/src/sync/util.test.ts +28 -1
- package/test/src/util/checkpointing.test.ts +249 -0
- package/test/src/util/utils.test.ts +23 -0
- package/tsconfig.json +1 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/storage/PersistedSyncRulesContent.d.ts +0 -59
- package/dist/storage/PersistedSyncRulesContent.js +0 -100
- package/dist/storage/PersistedSyncRulesContent.js.map +0 -1
- package/src/storage/PersistedSyncRulesContent.ts +0 -174
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { BucketDefinitionId, ParameterIndexId } from '@powersync/service-sync-rules';
|
|
2
|
+
import { SingleSyncConfigBucketDefinitionMapping } from './BucketDefinitionMapping.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Persisted storage ids for a replication stream, derived from the rule_mapping documents
|
|
6
|
+
* of all its sync configs.
|
|
7
|
+
*
|
|
8
|
+
* This requires no parsed sync configs, and deliberately does not resolve individual
|
|
9
|
+
* sources - use the parsed-set-bound {@link BucketDefinitionMapping} for that.
|
|
10
|
+
*/
|
|
11
|
+
export class ReplicationStreamStorageIds {
|
|
12
|
+
constructor(private readonly mappings: SingleSyncConfigBucketDefinitionMapping[]) {}
|
|
13
|
+
|
|
14
|
+
get bucketDefinitionIds(): BucketDefinitionId[] {
|
|
15
|
+
return [...new Set(this.mappings.flatMap((mapping) => mapping.allBucketDefinitionIds()))];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get parameterIndexIds(): ParameterIndexId[] {
|
|
19
|
+
return [...new Set(this.mappings.flatMap((mapping) => mapping.allParameterIndexIds()))];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -2,16 +2,26 @@ export * from './bson.js';
|
|
|
2
2
|
export * from './BucketStorage.js';
|
|
3
3
|
export * from './BucketStorageBatch.js';
|
|
4
4
|
export * from './BucketStorageFactory.js';
|
|
5
|
+
export * from './CheckpointChecksumInvalidatedError.js';
|
|
5
6
|
export * from './ChecksumCache.js';
|
|
6
|
-
export * from './
|
|
7
|
+
export * from './ParsedSyncConfigSet.js';
|
|
8
|
+
export * from './PersistedReplicationStream.js';
|
|
9
|
+
export * from './PersistedSyncConfigContent.js';
|
|
10
|
+
export * from './PersistedSyncConfigStatus.js';
|
|
7
11
|
export * from './ReplicationEventPayload.js';
|
|
8
12
|
export * from './ReplicationLock.js';
|
|
9
13
|
export * from './ReportStorage.js';
|
|
10
14
|
export * from './SourceEntity.js';
|
|
11
15
|
export * from './SourceTable.js';
|
|
16
|
+
export * from './SourceTableReconciler.js';
|
|
12
17
|
export * from './storage-metrics.js';
|
|
13
18
|
export * from './StorageEngine.js';
|
|
14
19
|
export * from './StorageProvider.js';
|
|
15
20
|
export * from './StorageVersionConfig.js';
|
|
16
21
|
export * from './SyncRulesBucketStorage.js';
|
|
17
22
|
export * from './WriteCheckpointAPI.js';
|
|
23
|
+
|
|
24
|
+
export * from './implementation/BucketDefinitionMapping.js';
|
|
25
|
+
export * from './implementation/IncrementalReprocessingCompatibility.js';
|
|
26
|
+
export * from './implementation/IncrementalReprocessingSyncConfigLog.js';
|
|
27
|
+
export * from './implementation/ReplicationStreamStorageIds.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { logger } from '@powersync/lib-services-framework';
|
|
2
2
|
import { StorageMetric } from '@powersync/service-types';
|
|
3
3
|
import { MetricsEngine } from '../metrics/MetricsEngine.js';
|
|
4
|
-
import { BucketStorageFactory, StorageMetrics } from './BucketStorageFactory.js';
|
|
4
|
+
import { BucketStorageFactory, StorageMetrics, StorageSyncConfigMetrics } from './BucketStorageFactory.js';
|
|
5
5
|
|
|
6
6
|
export function createCoreStorageMetrics(engine: MetricsEngine): void {
|
|
7
7
|
engine.createObservableGauge({
|
|
@@ -9,59 +9,205 @@ export function createCoreStorageMetrics(engine: MetricsEngine): void {
|
|
|
9
9
|
description: 'Size of current data stored in PowerSync',
|
|
10
10
|
unit: 'bytes'
|
|
11
11
|
});
|
|
12
|
+
engine.createObservableGauge({
|
|
13
|
+
name: StorageMetric.ATTRIBUTED_SOURCE_RECORDS_BYTES,
|
|
14
|
+
description: 'Size of current data stored in PowerSync by sync config',
|
|
15
|
+
unit: 'bytes'
|
|
16
|
+
});
|
|
12
17
|
|
|
13
18
|
engine.createObservableGauge({
|
|
14
19
|
name: StorageMetric.OPERATION_SIZE_BYTES,
|
|
15
20
|
description: 'Size of operations stored in PowerSync',
|
|
16
21
|
unit: 'bytes'
|
|
17
22
|
});
|
|
23
|
+
engine.createObservableGauge({
|
|
24
|
+
name: StorageMetric.ATTRIBUTED_BUCKET_DATA_BYTES,
|
|
25
|
+
description: 'Size of operations stored in PowerSync by sync config',
|
|
26
|
+
unit: 'bytes'
|
|
27
|
+
});
|
|
18
28
|
|
|
19
29
|
engine.createObservableGauge({
|
|
20
30
|
name: StorageMetric.PARAMETER_SIZE_BYTES,
|
|
21
31
|
description: 'Size of parameter data stored in PowerSync',
|
|
22
32
|
unit: 'bytes'
|
|
23
33
|
});
|
|
34
|
+
engine.createObservableGauge({
|
|
35
|
+
name: StorageMetric.ATTRIBUTED_PARAMETER_INDEXES_BYTES,
|
|
36
|
+
description: 'Size of parameter data stored in PowerSync by sync config',
|
|
37
|
+
unit: 'bytes'
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
engine.createObservableGauge({
|
|
41
|
+
name: StorageMetric.OBJECT_STORAGE_SIZE_BYTES,
|
|
42
|
+
description: 'Size of active object-storage references stored in PowerSync',
|
|
43
|
+
unit: 'bytes'
|
|
44
|
+
});
|
|
45
|
+
engine.createObservableGauge({
|
|
46
|
+
name: StorageMetric.ATTRIBUTED_OBJECT_STORAGE_BYTES,
|
|
47
|
+
description: 'Size of active object-storage references by sync config',
|
|
48
|
+
unit: 'bytes'
|
|
49
|
+
});
|
|
24
50
|
}
|
|
25
51
|
|
|
26
52
|
export function initializeCoreStorageMetrics(engine: MetricsEngine, storage: BucketStorageFactory): void {
|
|
27
53
|
const replication_storage_size_bytes = engine.getObservableGauge(StorageMetric.REPLICATION_SIZE_BYTES);
|
|
54
|
+
const attributed_source_records_bytes = engine.getObservableGauge(StorageMetric.ATTRIBUTED_SOURCE_RECORDS_BYTES);
|
|
28
55
|
const operation_storage_size_bytes = engine.getObservableGauge(StorageMetric.OPERATION_SIZE_BYTES);
|
|
56
|
+
const attributed_bucket_data_bytes = engine.getObservableGauge(StorageMetric.ATTRIBUTED_BUCKET_DATA_BYTES);
|
|
29
57
|
const parameter_storage_size_bytes = engine.getObservableGauge(StorageMetric.PARAMETER_SIZE_BYTES);
|
|
58
|
+
const attributed_parameter_indexes_bytes = engine.getObservableGauge(
|
|
59
|
+
StorageMetric.ATTRIBUTED_PARAMETER_INDEXES_BYTES
|
|
60
|
+
);
|
|
61
|
+
const object_storage_size_bytes = engine.getObservableGauge(StorageMetric.OBJECT_STORAGE_SIZE_BYTES);
|
|
62
|
+
const attributed_object_storage_bytes = engine.getObservableGauge(StorageMetric.ATTRIBUTED_OBJECT_STORAGE_BYTES);
|
|
30
63
|
|
|
31
64
|
const MINIMUM_INTERVAL = 60_000;
|
|
32
65
|
|
|
66
|
+
/**
|
|
67
|
+
* How long a sync config that is no longer present keeps being reported as zero.
|
|
68
|
+
*
|
|
69
|
+
* This must comfortably exceed the longest metric export interval, so that every configured
|
|
70
|
+
* reader observes the zero at least once. The OTLP reader exports every 5 minutes.
|
|
71
|
+
*/
|
|
72
|
+
const RETIRED_SYNC_CONFIG_INTERVAL = 15 * 60_000;
|
|
73
|
+
|
|
74
|
+
type SyncConfigAttributes = {
|
|
75
|
+
sync_config_id: string;
|
|
76
|
+
sync_config_state: string;
|
|
77
|
+
version_label?: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Attribute sets reported at least once, along with the last time each was present in the
|
|
82
|
+
* storage metrics.
|
|
83
|
+
*
|
|
84
|
+
* These gauges are exported with cumulative temporality, which means the metrics SDK keeps
|
|
85
|
+
* re-exporting the last reported value for every attribute set it has seen, also after we stop
|
|
86
|
+
* reporting that set. Without this, a sync config that has since been pruned would keep
|
|
87
|
+
* reporting its last non-zero size indefinitely, so sums over these gauges would drift upwards
|
|
88
|
+
* permanently. Reporting zero for a while instead leaves the SDK carrying a zero forward.
|
|
89
|
+
*/
|
|
90
|
+
const reportedSyncConfigs = new Map<string, { attributes: SyncConfigAttributes; lastSeen: number }>();
|
|
91
|
+
|
|
92
|
+
const syncConfigKey = (attributes: SyncConfigAttributes) =>
|
|
93
|
+
JSON.stringify([attributes.sync_config_id, attributes.sync_config_state, attributes.version_label]);
|
|
94
|
+
|
|
95
|
+
const syncConfigAttributes = (syncConfig: StorageSyncConfigMetrics): SyncConfigAttributes => ({
|
|
96
|
+
sync_config_id: syncConfig.sync_config_id,
|
|
97
|
+
sync_config_state: syncConfig.sync_config_state,
|
|
98
|
+
...(syncConfig.version_label == null ? {} : { version_label: syncConfig.version_label })
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const trackSyncConfigs = (metrics: StorageMetrics | null) => {
|
|
102
|
+
if (metrics == null) {
|
|
103
|
+
// Failed to read the metrics - this says nothing about which sync configs still exist.
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
for (const syncConfig of metrics.sync_config_metrics ?? []) {
|
|
108
|
+
const attributes = syncConfigAttributes(syncConfig);
|
|
109
|
+
reportedSyncConfigs.set(syncConfigKey(attributes), { attributes, lastSeen: now });
|
|
110
|
+
}
|
|
111
|
+
for (const [key, entry] of reportedSyncConfigs) {
|
|
112
|
+
if (now - entry.lastSeen > RETIRED_SYNC_CONFIG_INTERVAL) {
|
|
113
|
+
reportedSyncConfigs.delete(key);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
33
118
|
let cachedRequest: Promise<StorageMetrics | null> | undefined = undefined;
|
|
34
119
|
let cacheTimestamp = 0;
|
|
35
120
|
|
|
36
121
|
const getMetrics = () => {
|
|
37
122
|
if (!cachedRequest || Date.now() - cacheTimestamp > MINIMUM_INTERVAL) {
|
|
38
|
-
cachedRequest = storage
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
123
|
+
cachedRequest = storage
|
|
124
|
+
.getStorageMetrics()
|
|
125
|
+
.catch((e) => {
|
|
126
|
+
logger.error(`Failed to get storage metrics`, e);
|
|
127
|
+
return null;
|
|
128
|
+
})
|
|
129
|
+
.then((metrics) => {
|
|
130
|
+
trackSyncConfigs(metrics);
|
|
131
|
+
return metrics;
|
|
132
|
+
});
|
|
42
133
|
cacheTimestamp = Date.now();
|
|
43
134
|
}
|
|
44
135
|
return cachedRequest;
|
|
45
136
|
};
|
|
46
137
|
|
|
138
|
+
type StorageSizeMetricKey =
|
|
139
|
+
| 'attributed_bucket_data_bytes'
|
|
140
|
+
| 'attributed_parameter_indexes_bytes'
|
|
141
|
+
| 'attributed_source_records_bytes'
|
|
142
|
+
| 'attributed_object_storage_bytes';
|
|
143
|
+
|
|
144
|
+
function nonNegative(value: number): number;
|
|
145
|
+
function nonNegative(value: number | undefined): number | undefined;
|
|
146
|
+
function nonNegative(value: number | undefined) {
|
|
147
|
+
return value == null ? value : Math.max(0, value);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const observationsForSyncConfigs = (metrics: StorageMetrics, key: StorageSizeMetricKey) => {
|
|
151
|
+
const observations: { value: number; attributes?: Record<string, string> }[] = [];
|
|
152
|
+
const present = new Set<string>();
|
|
153
|
+
for (const syncConfig of metrics.sync_config_metrics ?? []) {
|
|
154
|
+
const attributes = syncConfigAttributes(syncConfig);
|
|
155
|
+
present.add(syncConfigKey(attributes));
|
|
156
|
+
observations.push({ value: nonNegative(syncConfig[key]), attributes });
|
|
157
|
+
}
|
|
158
|
+
for (const entry of reportedSyncConfigs.values()) {
|
|
159
|
+
if (!present.has(syncConfigKey(entry.attributes))) {
|
|
160
|
+
observations.push({ value: 0, attributes: entry.attributes });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return observations;
|
|
164
|
+
};
|
|
165
|
+
|
|
47
166
|
replication_storage_size_bytes.setValueProvider(async () => {
|
|
167
|
+
const metrics = await getMetrics();
|
|
168
|
+
return nonNegative(metrics?.replication_size_bytes);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
attributed_source_records_bytes.setValueProvider(async () => {
|
|
48
172
|
const metrics = await getMetrics();
|
|
49
173
|
if (metrics) {
|
|
50
|
-
return metrics
|
|
174
|
+
return observationsForSyncConfigs(metrics, 'attributed_source_records_bytes');
|
|
51
175
|
}
|
|
52
176
|
});
|
|
53
177
|
|
|
54
178
|
operation_storage_size_bytes.setValueProvider(async () => {
|
|
179
|
+
const metrics = await getMetrics();
|
|
180
|
+
return nonNegative(metrics?.operations_size_bytes);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
attributed_bucket_data_bytes.setValueProvider(async () => {
|
|
55
184
|
const metrics = await getMetrics();
|
|
56
185
|
if (metrics) {
|
|
57
|
-
return metrics
|
|
186
|
+
return observationsForSyncConfigs(metrics, 'attributed_bucket_data_bytes');
|
|
58
187
|
}
|
|
59
188
|
});
|
|
60
189
|
|
|
61
190
|
parameter_storage_size_bytes.setValueProvider(async () => {
|
|
191
|
+
const metrics = await getMetrics();
|
|
192
|
+
return nonNegative(metrics?.parameters_size_bytes);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
attributed_parameter_indexes_bytes.setValueProvider(async () => {
|
|
196
|
+
const metrics = await getMetrics();
|
|
197
|
+
if (metrics) {
|
|
198
|
+
return observationsForSyncConfigs(metrics, 'attributed_parameter_indexes_bytes');
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
object_storage_size_bytes.setValueProvider(async () => {
|
|
203
|
+
const metrics = await getMetrics();
|
|
204
|
+
return nonNegative(metrics?.object_storage_size_bytes);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
attributed_object_storage_bytes.setValueProvider(async () => {
|
|
62
208
|
const metrics = await getMetrics();
|
|
63
209
|
if (metrics) {
|
|
64
|
-
return metrics
|
|
210
|
+
return observationsForSyncConfigs(metrics, 'attributed_object_storage_bytes');
|
|
65
211
|
}
|
|
66
212
|
});
|
|
67
213
|
}
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
BucketParameterQuerier,
|
|
3
3
|
BucketPriority,
|
|
4
4
|
BucketSource,
|
|
5
|
+
BucketSourceType,
|
|
5
6
|
HydratedSyncConfig,
|
|
6
7
|
mergeBuckets,
|
|
7
8
|
QuerierError,
|
|
@@ -22,9 +23,26 @@ import {
|
|
|
22
23
|
} from '@powersync/lib-services-framework';
|
|
23
24
|
import { JwtPayload } from '../auth/JwtPayload.js';
|
|
24
25
|
import { ParameterSetLimitExceededError } from '../storage/storage-index.js';
|
|
26
|
+
import { PerformanceTracer } from '../tracing/PerformanceTracer.js';
|
|
25
27
|
import { SyncContext } from './SyncContext.js';
|
|
26
28
|
import { getIntersection, hasIntersection } from './util.js';
|
|
27
29
|
|
|
30
|
+
export type SyncCheckpointTraceCategory =
|
|
31
|
+
// buildNextCheckpointLine, excluding parameter computation and checksum lookups
|
|
32
|
+
| 'checkpoint'
|
|
33
|
+
// Parameter lookups
|
|
34
|
+
| 'parameters'
|
|
35
|
+
// Checksum calculations
|
|
36
|
+
| 'checksum'
|
|
37
|
+
// Waiting for a data lock to become available
|
|
38
|
+
| 'acquiring_lock'
|
|
39
|
+
// Data fetches. Holds one data lock for this period.
|
|
40
|
+
| 'bucket_data'
|
|
41
|
+
// Sending data to client, may include serialization overhead. Holds one data lock for this period in most cases.
|
|
42
|
+
| 'sending'
|
|
43
|
+
// Time spent reading concurrent S3 requests, counted once per batch.
|
|
44
|
+
| 's3';
|
|
45
|
+
|
|
28
46
|
export interface BucketChecksumStateOptions {
|
|
29
47
|
syncContext: SyncContext;
|
|
30
48
|
bucketStorage: BucketChecksumStateStorage;
|
|
@@ -60,6 +78,12 @@ export class BucketChecksumState {
|
|
|
60
78
|
*/
|
|
61
79
|
private lastChecksums: util.ChecksumMap | null = null;
|
|
62
80
|
private lastWriteCheckpoint: bigint | null = null;
|
|
81
|
+
/**
|
|
82
|
+
* The next storage checkpoint diff may be relative to a checksum-invalidated
|
|
83
|
+
* checkpoint that was never sent to the client. Re-check every bucket once
|
|
84
|
+
* instead of applying that diff to the last client-visible checksums.
|
|
85
|
+
*/
|
|
86
|
+
private forceFullChecksumForNextCheckpoint = false;
|
|
63
87
|
/**
|
|
64
88
|
* Once we've sent the first full checkpoint line including all {@link util.Checkpoint.streams} that the user is
|
|
65
89
|
* subscribed to, we keep an index of the stream names to their index in that array.
|
|
@@ -101,6 +125,10 @@ export class BucketChecksumState {
|
|
|
101
125
|
}
|
|
102
126
|
}
|
|
103
127
|
|
|
128
|
+
invalidateChecksumBaseline() {
|
|
129
|
+
this.forceFullChecksumForNextCheckpoint = true;
|
|
130
|
+
}
|
|
131
|
+
|
|
104
132
|
/**
|
|
105
133
|
* Build a new checkpoint line for an underlying storage checkpoint update if any buckets have changed.
|
|
106
134
|
*
|
|
@@ -112,14 +140,20 @@ export class BucketChecksumState {
|
|
|
112
140
|
* @param next The updated checkpoint
|
|
113
141
|
* @returns A {@link CheckpointLine} if any of the buckets watched by this connected was updated, or otherwise `null`.
|
|
114
142
|
*/
|
|
115
|
-
async buildNextCheckpointLine(
|
|
143
|
+
async buildNextCheckpointLine(
|
|
144
|
+
next: storage.StorageCheckpointUpdate,
|
|
145
|
+
tracer?: PerformanceTracer<SyncCheckpointTraceCategory>
|
|
146
|
+
): Promise<CheckpointLine | null> {
|
|
147
|
+
tracer ??= new PerformanceTracer<SyncCheckpointTraceCategory>(`sync checkpoint ${next.base.checkpoint}`);
|
|
148
|
+
|
|
116
149
|
const { writeCheckpoint, base } = next;
|
|
117
150
|
const userIdForLogs = this.parameterState.syncParams.userId;
|
|
118
151
|
|
|
119
152
|
const storage = this.bucketStorage;
|
|
120
153
|
|
|
121
|
-
const update = await this.parameterState.getCheckpointUpdate(next);
|
|
154
|
+
const update = await this.parameterState.getCheckpointUpdate(next, tracer);
|
|
122
155
|
const { buckets: allBuckets, updatedBuckets, usedParameterResults } = update;
|
|
156
|
+
const forceFullChecksum = this.forceFullChecksumForNextCheckpoint;
|
|
123
157
|
|
|
124
158
|
/** Set of all buckets in this checkpoint. */
|
|
125
159
|
const bucketDescriptionMap = new Map(allBuckets.map((b) => [b.bucket, b]));
|
|
@@ -144,8 +178,9 @@ export class BucketChecksumState {
|
|
|
144
178
|
const count = bucketsByDefinition.get(definition) ?? 0;
|
|
145
179
|
bucketsByDefinition.set(definition, count + 1);
|
|
146
180
|
}
|
|
147
|
-
|
|
148
|
-
const
|
|
181
|
+
// Only 1 type is allowed per sync config
|
|
182
|
+
const bucketSourceType = this.parameterState.syncRules.bucketSourceDefinitions[0].type;
|
|
183
|
+
const breakdown = formatBucketDefinitionBreakdown(bucketsByDefinition, bucketSourceType);
|
|
149
184
|
errorMessage += breakdown.message;
|
|
150
185
|
logData.buckets_by_definition = breakdown.countsByDefinition;
|
|
151
186
|
|
|
@@ -154,13 +189,14 @@ export class BucketChecksumState {
|
|
|
154
189
|
}
|
|
155
190
|
|
|
156
191
|
let checksumMap: util.ChecksumMap;
|
|
157
|
-
if (updatedBuckets != INVALIDATE_ALL_BUCKETS) {
|
|
192
|
+
if (!forceFullChecksum && updatedBuckets != INVALIDATE_ALL_BUCKETS) {
|
|
158
193
|
if (this.lastChecksums == null) {
|
|
159
194
|
throw new ServiceAssertionError(`Bucket diff received without existing checksums`);
|
|
160
195
|
}
|
|
161
196
|
|
|
162
197
|
// Re-check updated buckets only
|
|
163
198
|
let checksumLookups: storage.BucketChecksumRequest[] = [];
|
|
199
|
+
let hasNewBucket = false;
|
|
164
200
|
|
|
165
201
|
let newChecksums = new Map<string, util.BucketChecksum>();
|
|
166
202
|
for (let desc of bucketDescriptionMap.values()) {
|
|
@@ -174,11 +210,17 @@ export class BucketChecksumState {
|
|
|
174
210
|
newChecksums.set(desc.bucket, existing);
|
|
175
211
|
} else {
|
|
176
212
|
checksumLookups.push({ bucket: desc.bucket, source: desc.source });
|
|
213
|
+
hasNewBucket ||= !this.lastChecksums.has(desc.bucket);
|
|
177
214
|
}
|
|
178
215
|
}
|
|
179
216
|
|
|
180
217
|
if (checksumLookups.length > 0) {
|
|
181
|
-
|
|
218
|
+
const requestHint: storage.BucketRequestHint = hasNewBucket ? 'bulk' : 'incremental';
|
|
219
|
+
let updatedChecksums: util.ChecksumMap;
|
|
220
|
+
|
|
221
|
+
using _ = tracer.span('checksum');
|
|
222
|
+
updatedChecksums = await storage.getChecksums(base, checksumLookups, { requestHint });
|
|
223
|
+
|
|
182
224
|
for (let [bucket, value] of updatedChecksums.entries()) {
|
|
183
225
|
newChecksums.set(bucket, value);
|
|
184
226
|
}
|
|
@@ -186,12 +228,23 @@ export class BucketChecksumState {
|
|
|
186
228
|
checksumMap = newChecksums;
|
|
187
229
|
} else {
|
|
188
230
|
// Re-check all buckets
|
|
189
|
-
const
|
|
190
|
-
|
|
231
|
+
const hasNewBucket =
|
|
232
|
+
this.lastChecksums == null ||
|
|
233
|
+
[...bucketDescriptionMap.values()].some((b) => !this.lastChecksums!.has(b.bucket));
|
|
234
|
+
const requestHint: storage.BucketRequestHint = hasNewBucket ? 'bulk' : 'incremental';
|
|
235
|
+
const bucketList = [...bucketDescriptionMap.values()].map((b) => ({
|
|
236
|
+
bucket: b.bucket,
|
|
237
|
+
source: b.source
|
|
238
|
+
}));
|
|
239
|
+
|
|
240
|
+
using _ = tracer.span('checksum');
|
|
241
|
+
checksumMap = await storage.getChecksums(base, bucketList, { requestHint });
|
|
191
242
|
}
|
|
192
243
|
|
|
193
244
|
// Subset of buckets for which there may be new data in this batch.
|
|
194
245
|
let bucketsToFetch: ResolvedBucket[];
|
|
246
|
+
let bucketDataRequestHint: storage.BucketRequestHint;
|
|
247
|
+
const newBucketDownloads = new Set<string>();
|
|
195
248
|
|
|
196
249
|
let checkpointLine: util.StreamingSyncCheckpointDiff | util.StreamingSyncCheckpoint;
|
|
197
250
|
|
|
@@ -210,6 +263,9 @@ export class BucketChecksumState {
|
|
|
210
263
|
diff.updatedBuckets.length == 0
|
|
211
264
|
) {
|
|
212
265
|
// No changes - don't send anything to the client
|
|
266
|
+
if (forceFullChecksum) {
|
|
267
|
+
this.forceFullChecksumForNextCheckpoint = false;
|
|
268
|
+
}
|
|
213
269
|
return null;
|
|
214
270
|
}
|
|
215
271
|
|
|
@@ -232,6 +288,12 @@ export class BucketChecksumState {
|
|
|
232
288
|
bucketsToFetch = [...generateBucketsToFetch].map((b) => {
|
|
233
289
|
return bucketDescriptionMap.get(b)!;
|
|
234
290
|
});
|
|
291
|
+
for (const bucket of diff.updatedBuckets) {
|
|
292
|
+
if (!this.lastChecksums!.has(bucket.bucket)) {
|
|
293
|
+
newBucketDownloads.add(bucket.bucket);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
bucketDataRequestHint = newBucketDownloads.size > 0 ? 'bulk' : 'incremental';
|
|
235
297
|
|
|
236
298
|
deferredLog = () => {
|
|
237
299
|
let message = `Updated checkpoint: ${base.checkpoint} | `;
|
|
@@ -280,6 +342,10 @@ export class BucketChecksumState {
|
|
|
280
342
|
);
|
|
281
343
|
};
|
|
282
344
|
bucketsToFetch = allBuckets;
|
|
345
|
+
for (const bucket of bucketsToFetch) {
|
|
346
|
+
newBucketDownloads.add(bucket.bucket);
|
|
347
|
+
}
|
|
348
|
+
bucketDataRequestHint = 'bulk';
|
|
283
349
|
|
|
284
350
|
const subscriptions: util.StreamDescription[] = [];
|
|
285
351
|
const streamNameToIndex = new Map<string, number>();
|
|
@@ -321,6 +387,7 @@ export class BucketChecksumState {
|
|
|
321
387
|
return {
|
|
322
388
|
checkpointLine,
|
|
323
389
|
bucketsToFetch,
|
|
390
|
+
bucketDataRequestHint,
|
|
324
391
|
advance: () => {
|
|
325
392
|
hasAdvanced = true;
|
|
326
393
|
// bucketDataPositions must be updated in-place - it represents the current state of
|
|
@@ -353,6 +420,9 @@ export class BucketChecksumState {
|
|
|
353
420
|
this.lastChecksums = checksumMap;
|
|
354
421
|
this.lastWriteCheckpoint = writeCheckpoint;
|
|
355
422
|
this.pendingBucketDownloads = pendingBucketDownloads;
|
|
423
|
+
if (forceFullChecksum) {
|
|
424
|
+
this.forceFullChecksumForNextCheckpoint = false;
|
|
425
|
+
}
|
|
356
426
|
deferredLog();
|
|
357
427
|
},
|
|
358
428
|
|
|
@@ -512,12 +582,15 @@ export class BucketParameterState {
|
|
|
512
582
|
return (desc.subscribedToByDefault && this.includeDefaultStreams) || this.subscribedStreamNames.has(desc.name);
|
|
513
583
|
}
|
|
514
584
|
|
|
515
|
-
async getCheckpointUpdate(
|
|
585
|
+
async getCheckpointUpdate(
|
|
586
|
+
checkpoint: storage.StorageCheckpointUpdate,
|
|
587
|
+
tracer: PerformanceTracer<SyncCheckpointTraceCategory>
|
|
588
|
+
): Promise<CheckpointUpdate> {
|
|
516
589
|
const querier = this.querier;
|
|
517
590
|
let update: CheckpointUpdate;
|
|
518
591
|
if (querier.hasDynamicBuckets) {
|
|
519
592
|
try {
|
|
520
|
-
update = await this.getCheckpointUpdateDynamic(checkpoint);
|
|
593
|
+
update = await this.getCheckpointUpdateDynamic(checkpoint, tracer);
|
|
521
594
|
} catch (e: unknown) {
|
|
522
595
|
if (e instanceof ParameterSetLimitExceededError) {
|
|
523
596
|
// Too many parameter results, create a breakdown of which streams are responsible for the most queries and
|
|
@@ -580,7 +653,10 @@ export class BucketParameterState {
|
|
|
580
653
|
/**
|
|
581
654
|
* For dynamic buckets, we need to re-query the list of buckets every time.
|
|
582
655
|
*/
|
|
583
|
-
private async getCheckpointUpdateDynamic(
|
|
656
|
+
private async getCheckpointUpdateDynamic(
|
|
657
|
+
checkpoint: storage.StorageCheckpointUpdate,
|
|
658
|
+
tracer: PerformanceTracer<SyncCheckpointTraceCategory>
|
|
659
|
+
): Promise<CheckpointUpdate> {
|
|
584
660
|
const querier = this.querier;
|
|
585
661
|
const staticBuckets = this.staticBuckets.values();
|
|
586
662
|
const update = checkpoint.update;
|
|
@@ -636,6 +712,7 @@ export class BucketParameterState {
|
|
|
636
712
|
}
|
|
637
713
|
|
|
638
714
|
try {
|
|
715
|
+
using _ = tracer.span('parameters', definition);
|
|
639
716
|
const results = await checkpoint.base.getParameterSets(lookups, remainingBudget);
|
|
640
717
|
const numRows = results.reduce((a, b) => a + b.rows.length, 0);
|
|
641
718
|
|
|
@@ -692,6 +769,7 @@ export class BucketParameterState {
|
|
|
692
769
|
export interface CheckpointLine {
|
|
693
770
|
checkpointLine: util.StreamingSyncCheckpointDiff | util.StreamingSyncCheckpoint;
|
|
694
771
|
bucketsToFetch: ResolvedBucket[];
|
|
772
|
+
bucketDataRequestHint: storage.BucketRequestHint;
|
|
695
773
|
|
|
696
774
|
/**
|
|
697
775
|
* Call when a checkpoint line is being sent to a client, to update the internal state.
|
|
@@ -738,30 +816,36 @@ function logCheckpoint(
|
|
|
738
816
|
}
|
|
739
817
|
|
|
740
818
|
/**
|
|
741
|
-
* Format a breakdown of dynamic
|
|
819
|
+
* Format a breakdown of dynamic buckets by sync stream or legacy bucket definition.
|
|
742
820
|
*
|
|
743
|
-
* Sorts definitions by count (descending), includes the top
|
|
821
|
+
* Sorts definitions by count (descending), includes the top 100, and returns both the
|
|
744
822
|
* formatted message string and the counts record suitable for structured log data.
|
|
745
823
|
*/
|
|
746
|
-
function formatBucketDefinitionBreakdown(
|
|
824
|
+
function formatBucketDefinitionBreakdown(
|
|
825
|
+
bucketsByDefinition: Map<string, number>,
|
|
826
|
+
bucketSourceType: BucketSourceType
|
|
827
|
+
): {
|
|
747
828
|
message: string;
|
|
748
829
|
countsByDefinition: Record<string, number>;
|
|
749
830
|
} {
|
|
750
|
-
|
|
831
|
+
const maxLoggedDefinitions = 100;
|
|
832
|
+
|
|
833
|
+
// Sort definitions by count (descending) and take the largest entries.
|
|
751
834
|
const allSorted = Array.from(bucketsByDefinition.entries()).sort((a, b) => b[1] - a[1]);
|
|
752
|
-
const sortedDefinitions = allSorted.slice(0,
|
|
835
|
+
const sortedDefinitions = allSorted.slice(0, maxLoggedDefinitions);
|
|
753
836
|
|
|
754
|
-
|
|
837
|
+
const sourceLabel = bucketSourceType == BucketSourceType.SYNC_STREAM ? 'sync stream' : 'bucket definition';
|
|
838
|
+
let message = `\nBuckets by ${sourceLabel}:`;
|
|
755
839
|
const countsByDefinition: Record<string, number> = {};
|
|
756
840
|
for (const [definition, count] of sortedDefinitions) {
|
|
757
841
|
message += `\n ${definition}: ${count}`;
|
|
758
842
|
countsByDefinition[definition] = count;
|
|
759
843
|
}
|
|
760
844
|
|
|
761
|
-
if (allSorted.length >
|
|
762
|
-
const remainingResults = allSorted.slice(
|
|
763
|
-
const remainingDefinitions = allSorted.length -
|
|
764
|
-
message += `\n ... and ${remainingResults} more
|
|
845
|
+
if (allSorted.length > maxLoggedDefinitions) {
|
|
846
|
+
const remainingResults = allSorted.slice(maxLoggedDefinitions).reduce((sum, [, count]) => sum + count, 0);
|
|
847
|
+
const remainingDefinitions = allSorted.length - maxLoggedDefinitions;
|
|
848
|
+
message += `\n ... and ${remainingResults} more buckets from ${remainingDefinitions} ${sourceLabel}s`;
|
|
765
849
|
}
|
|
766
850
|
|
|
767
851
|
return { message, countsByDefinition };
|
|
@@ -15,6 +15,8 @@ export class RequestTracker {
|
|
|
15
15
|
largeBuckets: Record<string, number> = {};
|
|
16
16
|
|
|
17
17
|
private encoding: string | undefined = undefined;
|
|
18
|
+
private lastCheckpointLogOperationsSynced = 0;
|
|
19
|
+
private lastCheckpointLogDataSyncedBytes = 0;
|
|
18
20
|
|
|
19
21
|
constructor(private metrics: MetricsEngine) {
|
|
20
22
|
this.metrics = metrics;
|
|
@@ -68,6 +70,19 @@ export class RequestTracker {
|
|
|
68
70
|
encoding: this.encoding
|
|
69
71
|
};
|
|
70
72
|
}
|
|
73
|
+
|
|
74
|
+
getIncrementalCheckpointStats(): CheckpointLogStats {
|
|
75
|
+
const operationsSynced = this.operationsSynced - this.lastCheckpointLogOperationsSynced;
|
|
76
|
+
const dataSyncedBytes = this.dataSyncedBytes - this.lastCheckpointLogDataSyncedBytes;
|
|
77
|
+
|
|
78
|
+
this.lastCheckpointLogOperationsSynced = this.operationsSynced;
|
|
79
|
+
this.lastCheckpointLogDataSyncedBytes = this.dataSyncedBytes;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
operations_synced: operationsSynced,
|
|
83
|
+
data_synced_bytes: dataSyncedBytes
|
|
84
|
+
};
|
|
85
|
+
}
|
|
71
86
|
}
|
|
72
87
|
|
|
73
88
|
export interface OperationCounts {
|
|
@@ -83,6 +98,11 @@ export interface OperationsSentStats {
|
|
|
83
98
|
total: number;
|
|
84
99
|
}
|
|
85
100
|
|
|
101
|
+
export interface CheckpointLogStats {
|
|
102
|
+
operations_synced: number;
|
|
103
|
+
data_synced_bytes: number;
|
|
104
|
+
}
|
|
105
|
+
|
|
86
106
|
export function statsForBatch(batch: SyncBucketData): OperationsSentStats {
|
|
87
107
|
let put = 0;
|
|
88
108
|
let remove = 0;
|