Skip to content

Commit 38fa380

Browse files
authored
chore(zero-cache): export a view-syncer drain-time estimate (#45)
1 parent f5032fd commit 38fa380

3 files changed

Lines changed: 29 additions & 2 deletions

File tree

‎packages/zero-cache/src/services/view-syncer/drain-coordinator.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ type DrainListener = () => void;
55

66
// The target (additional) utilization to impose on the server
77
// that receives the drained connections.
8-
const TARGET_UTILIZATION = 0.6;
8+
export const TARGET_UTILIZATION = 0.6;
99

1010
/**
1111
* There are two types of drains:

‎packages/zero-cache/src/services/view-syncer/view-syncer.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,11 @@ export interface ViewSyncer {
182182

183183
readonly queryCount: number;
184184
readonly rowCount: number;
185+
/**
186+
* The time its queries took to hydrate, which its client group's
187+
* reconnect costs the view-syncer it lands on.
188+
*/
189+
readonly hydrationTimeMs: number;
185190
readonly createdAtMs: number;
186191
readonly servedVersion: string | null;
187192
readonly servingLagEligible: boolean;
@@ -1136,6 +1141,10 @@ export class ViewSyncerService implements ViewSyncer, ActivityBasedService {
11361141
return this.#pipelines.initialized() ? this.#pipelines.queries().size : 0;
11371142
}
11381143

1144+
get hydrationTimeMs(): number {
1145+
return this.#totalHydrationTimeMs();
1146+
}
1147+
11391148
pipelineHashes(): readonly PipelineHashInfo[] {
11401149
if (!this.#pipelines.initialized()) {
11411150
return [];

‎packages/zero-cache/src/workers/syncer.ts‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ import type {
3434
SingletonService,
3535
} from '../services/service.ts';
3636
import type {ConnectionContextManager} from '../services/view-syncer/connection-context-manager.ts';
37-
import {DrainCoordinator} from '../services/view-syncer/drain-coordinator.ts';
37+
import {
38+
DrainCoordinator,
39+
TARGET_UTILIZATION,
40+
} from '../services/view-syncer/drain-coordinator.ts';
3841
import type {ViewSyncer} from '../services/view-syncer/view-syncer.ts';
3942
import type {ClientGroupStatusMessage, Worker} from '../types/processes.ts';
4043
import type {Subscription} from '../types/subscription.ts';
@@ -520,6 +523,21 @@ export class Syncer implements SingletonService {
520523
result.observe(total);
521524
});
522525

526+
getOrCreateGauge('sync', 'drain-time', {
527+
description:
528+
'How long a graceful shutdown would take to release every client ' +
529+
'group, pacing their disconnects by their hydration times as drain() ' +
530+
'does. A pod drains its workers in parallel, so its drain takes the ' +
531+
'longest of theirs.',
532+
unit: 's',
533+
}).addCallback(result => {
534+
let hydrationTimeMs = 0;
535+
for (const vs of this.#viewSyncers.getServices()) {
536+
hydrationTimeMs += vs.hydrationTimeMs;
537+
}
538+
result.observe(hydrationTimeMs / TARGET_UTILIZATION / 1000);
539+
});
540+
523541
getOrCreateGauge(
524542
'sync',
525543
'rows',

0 commit comments

Comments
 (0)