Skip to content

Commit 45c8e00

Browse files
committed
Fix usage by still passing durationMs and costInCents to the execution, just not the run.ctx
1 parent ccd3000 commit 45c8e00

File tree

10 files changed

+114
-11
lines changed

10 files changed

+114
-11
lines changed

packages/cli-v3/src/entryPoints/dev-run-worker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,11 @@ const zodIpc = new ZodIpcConnection({
505505
getNumberEnvVar("TRIGGER_RUN_METADATA_FLUSH_INTERVAL", 1000)
506506
);
507507

508+
devUsageManager.setInitialState({
509+
cpuTime: execution.run.durationMs ?? 0,
510+
costInCents: execution.run.costInCents ?? 0,
511+
});
512+
508513
_executionMeasurement = usage.start();
509514

510515
const timeoutController = timeout.abortAfterTimeout(execution.run.maxDuration);

packages/cli-v3/src/entryPoints/managed-run-worker.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,17 @@ const zodIpc = new ZodIpcConnection({
328328

329329
resetExecutionEnvironment();
330330

331-
initializeUsageManager({
331+
const prodManager = initializeUsageManager({
332332
usageIntervalMs: getEnvVar("USAGE_HEARTBEAT_INTERVAL_MS"),
333333
usageEventUrl: getEnvVar("USAGE_EVENT_URL"),
334334
triggerJWT: getEnvVar("TRIGGER_JWT"),
335335
});
336336

337+
prodManager.setInitialState({
338+
cpuTime: execution.run.durationMs ?? 0,
339+
costInCents: execution.run.costInCents ?? 0,
340+
});
341+
337342
standardRunTimelineMetricsManager.registerMetricsFromExecution(metrics, isWarmStart);
338343

339344
console.log(`[${new Date().toISOString()}] Received EXECUTE_TASK_RUN`, execution);
@@ -690,6 +695,8 @@ function initializeUsageManager({
690695

691696
usage.setGlobalUsageManager(prodUsageManager);
692697
timeout.setGlobalManager(new UsageTimeoutManager(devUsageManager));
698+
699+
return prodUsageManager;
693700
}
694701

695702
_sharedWorkerRuntime = new SharedRuntimeManager(zodIpc, true);

packages/core/src/v3/schemas/common.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,10 @@ export const TaskRun = z.object({
225225

226226
parentTaskRunId: z.string().optional(),
227227
rootTaskRunId: z.string().optional(),
228+
229+
// These are only used during execution, not in run.ctx
230+
durationMs: z.number().optional(),
231+
costInCents: z.number().optional(),
228232
});
229233

230234
export type TaskRun = z.infer<typeof TaskRun>;
@@ -397,6 +401,8 @@ export const TaskRunContext = z.object({
397401
payload: true,
398402
payloadType: true,
399403
metadata: true,
404+
durationMs: true,
405+
costInCents: true,
400406
}),
401407
...StaticTaskRunExecutionShape,
402408
});

packages/core/src/v3/usage/api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const API_NAME = "usage";
22

33
import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js";
4-
import type { UsageManager, UsageMeasurement, UsageSample } from "./types.js";
4+
import type { InitialUsageState, UsageManager, UsageMeasurement, UsageSample } from "./types.js";
55
import { NoopUsageManager } from "./noopUsageManager.js";
66

77
const NOOP_USAGE_MANAGER = new NoopUsageManager();
@@ -53,6 +53,10 @@ export class UsageAPI implements UsageManager {
5353
this.disable();
5454
}
5555

56+
public getInitialState(): InitialUsageState {
57+
return this.#getUsageManager().getInitialState();
58+
}
59+
5660
#getUsageManager(): UsageManager {
5761
return getGlobal(API_NAME) ?? NOOP_USAGE_MANAGER;
5862
}

packages/core/src/v3/usage/devUsageManager.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UsageManager, UsageMeasurement, UsageSample } from "./types.js";
1+
import { InitialUsageState, UsageManager, UsageMeasurement, UsageSample } from "./types.js";
22
import { clock } from "../clock-api.js";
33
import { ClockTime, calculateDurationInMs } from "../clock/clock.js";
44

@@ -45,6 +45,10 @@ export class DevUsageManager implements UsageManager {
4545
private _firstMeasurement?: DevUsageMeasurement;
4646
private _currentMeasurements: Map<string, DevUsageMeasurement> = new Map();
4747
private _pauses: Map<string, { start: ClockTime; end?: ClockTime }> = new Map();
48+
private _initialState: InitialUsageState = {
49+
cpuTime: 0,
50+
costInCents: 0,
51+
};
4852

4953
disable(): void {}
5054

@@ -54,6 +58,18 @@ export class DevUsageManager implements UsageManager {
5458
this._firstMeasurement = undefined;
5559
this._currentMeasurements.clear();
5660
this._pauses.clear();
61+
this._initialState = {
62+
cpuTime: 0,
63+
costInCents: 0,
64+
};
65+
}
66+
67+
setInitialState(state: InitialUsageState) {
68+
this._initialState = state;
69+
}
70+
71+
getInitialState(): InitialUsageState {
72+
return this._initialState;
5773
}
5874

5975
sample(): UsageSample | undefined {

packages/core/src/v3/usage/noopUsageManager.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { UsageManager, UsageMeasurement, UsageSample } from "./types.js";
1+
import { InitialUsageState, UsageManager, UsageMeasurement, UsageSample } from "./types.js";
22

33
export class NoopUsageManager implements UsageManager {
44
disable(): void {
@@ -30,4 +30,11 @@ export class NoopUsageManager implements UsageManager {
3030
reset(): void {
3131
// Noop
3232
}
33+
34+
getInitialState(): InitialUsageState {
35+
return {
36+
cpuTime: 0,
37+
costInCents: 0,
38+
};
39+
}
3340
}

packages/core/src/v3/usage/prodUsageManager.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { setInterval } from "node:timers/promises";
2-
import { UsageManager, UsageMeasurement, UsageSample } from "./types.js";
2+
import { InitialUsageState, UsageManager, UsageMeasurement, UsageSample } from "./types.js";
33
import { UsageClient } from "./usageClient.js";
44

55
export type ProdUsageManagerOptions = {
@@ -13,6 +13,10 @@ export class ProdUsageManager implements UsageManager {
1313
private _abortController: AbortController | undefined;
1414
private _lastSample: UsageSample | undefined;
1515
private _usageClient: UsageClient | undefined;
16+
private _initialState: InitialUsageState = {
17+
cpuTime: 0,
18+
costInCents: 0,
19+
};
1620

1721
constructor(
1822
private readonly delegageUsageManager: UsageManager,
@@ -27,13 +31,25 @@ export class ProdUsageManager implements UsageManager {
2731
return typeof this._usageClient !== "undefined";
2832
}
2933

34+
setInitialState(state: InitialUsageState) {
35+
this._initialState = state;
36+
}
37+
38+
getInitialState(): InitialUsageState {
39+
return this._initialState;
40+
}
41+
3042
reset(): void {
3143
this.delegageUsageManager.reset();
3244
this._abortController?.abort();
3345
this._abortController = new AbortController();
3446
this._usageClient = undefined;
3547
this._measurement = undefined;
3648
this._lastSample = undefined;
49+
this._initialState = {
50+
cpuTime: 0,
51+
costInCents: 0,
52+
};
3753
}
3854

3955
disable(): void {

packages/core/src/v3/usage/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ export interface UsageMeasurement {
77
sample(): UsageSample;
88
}
99

10+
export type InitialUsageState = {
11+
cpuTime: number;
12+
costInCents: number;
13+
};
14+
1015
export interface UsageManager {
1116
disable(): void;
17+
getInitialState(): InitialUsageState;
1218
start(): UsageMeasurement;
1319
stop(measurement: UsageMeasurement): UsageSample;
1420
sample(): UsageSample | undefined;

packages/trigger-sdk/src/v3/usage.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const usage = {
5454
*/
5555
getCurrent: (): CurrentUsage => {
5656
const sample = usageApi.sample();
57+
const initialState = usageApi.getInitialState();
5758
const machine = taskContext.ctx?.machine;
5859
const run = taskContext.ctx?.run;
5960

@@ -65,12 +66,12 @@ export const usage = {
6566
durationMs: 0,
6667
},
6768
total: {
68-
costInCents: 0,
69-
durationMs: 0,
69+
costInCents: initialState.costInCents,
70+
durationMs: initialState.cpuTime,
7071
},
7172
},
7273
baseCostInCents: run?.baseCostInCents ?? 0,
73-
totalCostInCents: run?.baseCostInCents ?? 0,
74+
totalCostInCents: initialState.costInCents + (run?.baseCostInCents ?? 0),
7475
};
7576
}
7677

@@ -83,12 +84,12 @@ export const usage = {
8384
durationMs: sample.cpuTime,
8485
},
8586
total: {
86-
costInCents: currentCostInCents,
87-
durationMs: sample.cpuTime,
87+
costInCents: currentCostInCents + initialState.costInCents,
88+
durationMs: sample.cpuTime + initialState.cpuTime,
8889
},
8990
},
9091
baseCostInCents: run?.baseCostInCents ?? 0,
91-
totalCostInCents: currentCostInCents + (run?.baseCostInCents ?? 0),
92+
totalCostInCents: currentCostInCents + (run?.baseCostInCents ?? 0) + initialState.costInCents,
9293
};
9394
},
9495
/**
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { logger, task, wait, usage } from "@trigger.dev/sdk";
2+
import { setTimeout } from "timers/promises";
3+
4+
export const usageExampleTask = task({
5+
id: "usage-example",
6+
retry: {
7+
maxAttempts: 3,
8+
minTimeoutInMs: 500,
9+
maxTimeoutInMs: 1000,
10+
factor: 1.5,
11+
},
12+
run: async (payload: { throwError: boolean }, { ctx }) => {
13+
logger.info("run.ctx", { ctx });
14+
15+
await setTimeout(1000);
16+
17+
const currentUsage = usage.getCurrent();
18+
19+
logger.info("currentUsage", { currentUsage });
20+
21+
if (payload.throwError && ctx.attempt.number === 1) {
22+
throw new Error("Forced error to cause a retry");
23+
}
24+
25+
await setTimeout(5000);
26+
27+
const currentUsage2 = usage.getCurrent();
28+
29+
logger.info("currentUsage2", { currentUsage2 });
30+
31+
return {
32+
message: "Hello, world!",
33+
};
34+
},
35+
});

0 commit comments

Comments
 (0)