Skip to content

Commit 9ccdb5b

Browse files
committed
fix(grouper): use monotonic time and exclusive timings in slow handle diagnostics
1 parent a276b9c commit 9ccdb5b

2 files changed

Lines changed: 43 additions & 9 deletions

File tree

workers/grouper/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ export default class GrouperWorker extends Worker {
427427

428428
session.logIfSlow({
429429
projectId: task.projectId,
430-
title: task.payload?.title,
430+
title: task.payload.title,
431431
type: eventType,
432432
payloadSize: taskPayloadSize,
433433
deltaSize,

workers/grouper/src/metrics/slowHandleDiagnostics.ts

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,22 @@ import type { GrouperDiagnosticsConfig } from './config';
22
import type GrouperMetrics from './grouperMetrics';
33
import type { GrouperStep } from './grouperMetrics';
44

5+
const NS_PER_MS = 1_000_000n;
6+
57
interface LoggerLike {
68
warn(message: string): void;
79
}
810

11+
/**
12+
* Frame on the active measurement stack. childTimeNs accumulates the duration
13+
* of nested measureStep() calls so the parent step records exclusive time.
14+
*/
15+
interface StepFrame {
16+
step: GrouperStep;
17+
startedAt: bigint;
18+
childTimeNs: bigint;
19+
}
20+
921
/**
1022
* Context describing the handled task for slow handle log line.
1123
*/
@@ -22,8 +34,9 @@ export interface SlowHandleContext {
2234
* measures Grouper handle steps and emits a slow handle warning on demand.
2335
*/
2436
export class SlowHandleSession {
25-
private readonly startedAt: number = Date.now();
37+
private readonly startedAt: bigint = process.hrtime.bigint();
2638
private readonly timings: Map<GrouperStep, number> = new Map();
39+
private readonly stack: StepFrame[] = [];
2740
private readonly logger: LoggerLike;
2841
private readonly metrics: GrouperMetrics;
2942
private readonly config: GrouperDiagnosticsConfig;
@@ -40,21 +53,38 @@ export class SlowHandleSession {
4053
}
4154

4255
/**
43-
* Measure a Grouper handle step and accumulate its duration in the session.
56+
* Measure a Grouper handle step and accumulate its exclusive duration
57+
* (time not spent inside nested measureStep calls) in the session.
4458
*
4559
* @param step - step name.
4660
* @param callback - measured callback.
4761
*/
4862
public async measureStep<T>(step: GrouperStep, callback: () => Promise<T> | T): Promise<T> {
49-
const stepStartedAt = Date.now();
63+
const frame: StepFrame = {
64+
step,
65+
startedAt: process.hrtime.bigint(),
66+
childTimeNs: 0n,
67+
};
68+
69+
this.stack.push(frame);
5070

5171
try {
5272
return await this.metrics.observeStepDuration(step, callback);
5373
} finally {
54-
const durationMs = Date.now() - stepStartedAt;
55-
const previousDurationMs = this.timings.get(step) || 0;
74+
const elapsedNs = process.hrtime.bigint() - frame.startedAt;
5675

57-
this.timings.set(step, previousDurationMs + durationMs);
76+
this.stack.pop();
77+
78+
const parent = this.stack[this.stack.length - 1];
79+
80+
if (parent) {
81+
parent.childTimeNs += elapsedNs;
82+
}
83+
84+
const exclusiveMs = Number((elapsedNs - frame.childTimeNs) / NS_PER_MS);
85+
const previousMs = this.timings.get(step) || 0;
86+
87+
this.timings.set(step, previousMs + exclusiveMs);
5888
}
5989
}
6090

@@ -64,7 +94,7 @@ export class SlowHandleSession {
6494
* @param context - handled task context.
6595
*/
6696
public logIfSlow(context: SlowHandleContext): void {
67-
const durationMs = Date.now() - this.startedAt;
97+
const durationMs = Number((process.hrtime.bigint() - this.startedAt) / NS_PER_MS);
6898

6999
if (durationMs < this.config.slowHandleWarnMs) {
70100
return;
@@ -75,9 +105,13 @@ export class SlowHandleSession {
75105
.map(([step, stepDurationMs]) => `${step}=${stepDurationMs}ms`)
76106
.join(' ');
77107

108+
const titleField = context.title !== undefined
109+
? ` title=${JSON.stringify(context.title)}`
110+
: '';
111+
78112
this.logger.warn(
79113
`[slowHandle] duration=${durationMs}ms project=${context.projectId} type=${context.type} ` +
80-
`payloadSize=${context.payloadSize}b deltaSize=${context.deltaSize}b title="${context.title}" steps="${steps}"`
114+
`payloadSize=${context.payloadSize}b deltaSize=${context.deltaSize}b${titleField} steps="${steps}"`
81115
);
82116
}
83117
}

0 commit comments

Comments
 (0)