Skip to content

Commit 335ed1a

Browse files
authored
Move StageTrace to services-core and add UTs (#25929)
## Description There are several locations that we need/want to use `StageTrace`. It should live in a shared location to facilitate that. This takes the move from #25643 into its own PR. ## Breaking Changes Not technically breaking: moves internal `StageTrace` API from Nexus and R11s-base utils into `services-core` ## Reviewer Guidance I'm open to moving this to `services-client`, but I think this is fine.
1 parent f59f711 commit 335ed1a

File tree

8 files changed

+90
-49
lines changed

8 files changed

+90
-49
lines changed

server/routerlicious/packages/lambdas/src/nexus/connect.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
type ICollaborationSessionClient,
2828
clusterDrainingRetryTimeInMs,
2929
type IDenyList,
30+
StageTrace,
3031
} from "@fluidframework/server-services-core";
3132
import {
3233
CommonProperties,
@@ -47,7 +48,7 @@ import type {
4748
} from "./interfaces";
4849
import { ProtocolVersions, checkProtocolVersion } from "./protocol";
4950
import { checkThrottleAndUsage, getSocketConnectThrottleId } from "./throttleAndUsage";
50-
import { StageTrace, sampleMessages } from "./trace";
51+
import { sampleMessages } from "./trace";
5152
import {
5253
getMessageMetadata,
5354
handleServerErrorAndConvertToNetworkError,

server/routerlicious/packages/lambdas/src/nexus/trace.ts

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* Licensed under the MIT License.
44
*/
55

6-
import { performance } from "@fluidframework/common-utils";
76
import type { IDocumentMessage } from "@fluidframework/protocol-definitions";
87
import { getRandomInt } from "@fluidframework/server-services-client";
98
import { DefaultServiceConfiguration } from "@fluidframework/server-services-core";
@@ -52,31 +51,3 @@ export function addNexusMessageTrace(
5251

5352
return message;
5453
}
55-
56-
interface IStageTrace {
57-
/**
58-
* Name of the Stage.
59-
*/
60-
stage: string;
61-
/**
62-
* Start time of the stage relative to the previous stage's start time.
63-
*/
64-
ts: number;
65-
}
66-
export class StageTrace<T extends { toString(): string }> {
67-
private readonly traces: IStageTrace[] = [];
68-
private lastStampedTraceTime: number = performance.now();
69-
constructor(initialStage?: T) {
70-
if (initialStage) {
71-
this.traces.push({ stage: initialStage.toString(), ts: 0 });
72-
}
73-
}
74-
public get trace(): IStageTrace[] {
75-
return this.traces;
76-
}
77-
public stampStage(stage: T): void {
78-
const now = performance.now();
79-
this.traces.push({ stage: stage.toString(), ts: now - this.lastStampedTraceTime });
80-
this.lastStampedTraceTime = now;
81-
}
82-
}

server/routerlicious/packages/routerlicious-base/src/alfred/routes/api/documents.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,18 @@ import {
2323
InternalErrorCode,
2424
getNetworkInformationFromIP,
2525
} from "@fluidframework/server-services-client";
26-
import type {
27-
IDocumentStorage,
28-
IThrottler,
29-
ITenantManager,
30-
ICache,
31-
IDocumentRepository,
32-
ITokenRevocationManager,
33-
IRevokeTokenOptions,
34-
IRevokedTokenChecker,
35-
IClusterDrainingChecker,
36-
IDenyList,
26+
import {
27+
type IDocumentStorage,
28+
type IThrottler,
29+
type ITenantManager,
30+
type ICache,
31+
type IDocumentRepository,
32+
type ITokenRevocationManager,
33+
type IRevokeTokenOptions,
34+
type IRevokedTokenChecker,
35+
type IClusterDrainingChecker,
36+
type IDenyList,
37+
StageTrace,
3738
} from "@fluidframework/server-services-core";
3839
import {
3940
getLumberBaseProperties,
@@ -63,7 +64,6 @@ import {
6364
generateCacheKey,
6465
getSession,
6566
setGetSessionResultInCache,
66-
StageTrace,
6767
} from "../../../utils";
6868
import type { IDocumentDeleteService } from "../../services";
6969

server/routerlicious/packages/routerlicious-base/src/utils/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ export { catch404, handleError } from "./middleware";
99
export { getIdFromRequest, getTenantIdFromRequest } from "./params";
1010
export { getSession, generateCacheKey, setGetSessionResultInCache } from "./sessionHelper";
1111
export { configureThrottler } from "./throttling";
12-
export { StageTrace } from "./trace";

server/routerlicious/packages/routerlicious-base/src/utils/sessionHelper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@ import {
1515
type IDocumentRepository,
1616
type IClusterDrainingChecker,
1717
type ICache,
18+
type StageTrace,
1819
} from "@fluidframework/server-services-core";
1920
import { getLumberBaseProperties, Lumberjack } from "@fluidframework/server-services-telemetry";
2021

21-
import type { StageTrace } from "./trace";
22-
2322
const defaultSessionStickinessDurationMs = 60 * 60 * 1000; // 60 minutes
2423
const defaultGetSessionCacheTtlInSeconds = 5 * 60; // 5 mins
2524

server/routerlicious/packages/services-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ export {
169169
ThrottlingError,
170170
} from "./throttler";
171171
export type { TokenGenerator } from "./token";
172+
export { StageTrace, IStageTrace } from "./trace";
172173
export {
173174
clientConnectivityStorageId,
174175
type IUsageData,
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
import { strict as assert } from "assert";
7+
import sinon from "sinon";
8+
import { StageTrace } from "../trace";
9+
import { performance } from "@fluidframework/common-utils";
10+
11+
describe("StageTrace", () => {
12+
let clock: sinon.SinonFakeTimers;
13+
beforeEach(() => {
14+
// Stub the `globalThis.performance.now` method
15+
clock = sinon.useFakeTimers({ now: 0 });
16+
sinon.stub(performance, "now").callsFake(() => Date.now());
17+
});
18+
19+
afterEach(() => {
20+
sinon.restore();
21+
});
22+
it("should initialize with initial stage", () => {
23+
const trace = new StageTrace("initial");
24+
assert.strictEqual(trace.trace[0]?.stage, "initial");
25+
});
26+
it("should initialize without initial stage", () => {
27+
const trace = new StageTrace();
28+
assert.strictEqual(trace.trace.length, 0);
29+
});
30+
it("should stamp stages with relative timestamps", () => {
31+
const start = performance.now();
32+
const trace = new StageTrace("start");
33+
clock.tick(50);
34+
trace.stampStage("middle");
35+
clock.tick(50);
36+
trace.stampStage("end");
37+
38+
const traces = trace.trace;
39+
assert.strictEqual(traces.length, 3);
40+
assert.strictEqual(traces[0].stage, "start");
41+
assert.strictEqual(traces[1].stage, "middle");
42+
assert.strictEqual(traces[2].stage, "end");
43+
// 0 for initial stage
44+
assert.strictEqual(traces[0].ts, start);
45+
// 50ms between start and middle
46+
assert.strictEqual(traces[1].ts, 50);
47+
// 50ms between middle and end
48+
assert.strictEqual(traces[2].ts, 50);
49+
});
50+
});

server/routerlicious/packages/routerlicious-base/src/utils/trace.ts renamed to server/routerlicious/packages/services-core/src/trace.ts

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
import { performance } from "@fluidframework/common-utils";
77

8-
interface IStageTrace {
8+
/**
9+
* @internal
10+
*/
11+
export interface IStageTrace {
912
/**
1013
* Name of the Stage.
1114
*/
@@ -15,20 +18,37 @@ interface IStageTrace {
1518
*/
1619
ts: number;
1720
}
21+
22+
/**
23+
* Utility class to trace different stages of an operation with timestamps relative to each other.
24+
* @internal
25+
*/
1826
export class StageTrace<T extends { toString(): string }> {
1927
private readonly traces: IStageTrace[] = [];
20-
private lastStampedTraceTime: number = performance.now();
28+
#lastStampedTraceTime: number = performance.now();
29+
2130
constructor(initialStage?: T) {
2231
if (initialStage) {
2332
this.traces.push({ stage: initialStage.toString(), ts: 0 });
2433
}
2534
}
35+
/**
36+
* Get the collected trace information.
37+
*/
2638
public get trace(): IStageTrace[] {
2739
return this.traces;
2840
}
41+
/**
42+
* Stamp a new stage with the time elapsed since the last stage stamp.
43+
* @remarks
44+
* If this is the first stage being stamped after construction, the time elapsed
45+
* will be since construction.
46+
*
47+
* @param stage - stage to be stringified for trace stage name.
48+
*/
2949
public stampStage(stage: T): void {
3050
const now = performance.now();
31-
this.traces.push({ stage: stage.toString(), ts: now - this.lastStampedTraceTime });
32-
this.lastStampedTraceTime = now;
51+
this.traces.push({ stage: stage.toString(), ts: now - this.#lastStampedTraceTime });
52+
this.#lastStampedTraceTime = now;
3353
}
3454
}

0 commit comments

Comments
 (0)