Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions apps/webapp/app/v3/eventRepository/common.server.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, it, expect } from "vitest";
import { getNowInNanoseconds, calculateDurationFromStart } from "./common.server";

describe("OTLP nanosecond precision", () => {
it("guarantees exact integer arithmetic without float truncation", () => {
const sampleMs = 1729200000123;
const sampleDate = new Date(sampleMs);
const expectedNano = BigInt(sampleMs) * 1_000_000n;

// Buggy float math produces 1729200000123000064n (+64ns error)
const buggyNano = BigInt(sampleMs * 1_000_000);
expect(buggyNano).not.toBe(expectedNano);

// Fixed calculation produces exact 1729200000123000000n
const fixedNano = BigInt(sampleDate.getTime()) * BigInt(1_000_000);
expect(fixedNano).toBe(expectedNano);
});

it("preserves exact duration calculation", () => {
const start = 1729200000000n * 1_000_000n;
const end = new Date(1729200000500); // 500ms later
const duration = calculateDurationFromStart(end, start);
expect(duration).toBe(500_000_000);
Comment on lines +19 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Precision tests are absent from the default suite

The Vitest include list excludes this test directory. Its duration test also reverses calculateDurationFromStart arguments, so including it alone will not validate the fix.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

});
});
4 changes: 2 additions & 2 deletions apps/webapp/app/v3/eventRepository/common.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function extractContextFromCarrier(carrier: Record<string, unknown>) {
}

export function getNowInNanoseconds(): bigint {
return BigInt(new Date().getTime() * 1_000_000);
return BigInt(new Date().getTime()) * BigInt(1_000_000);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Span precision fix absent from release notes

When this fix changes recorded span times, getNowInNanoseconds gains no server change note. Customers miss the fix in release notes.

Learn more

A server change note is the user-facing release entry for a server-only fix. This PR changes the nanosecond timestamp used for recorded spans and their durations, but adds no entry under .server-changes/. The repository requires a note when a server-only bug fix affects behavior users could encounter; these files feed release notes for customers. See the server change rules.

Example: A customer sees span ordering drift before the upgrade and accurate ordering afterward, but the release notes omit the correction.

Recommended fix: Add a .server-changes/ Markdown file with area: webapp, type: fix, and a single user-facing sentence about more accurate span timing.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

}

export function getDateFromNanoseconds(nanoseconds: bigint): Date {
Expand All @@ -40,7 +40,7 @@ export function calculateDurationFromStart(
) {
const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime;

const duration = Number(BigInt($endtime.getTime() * 1_000_000) - startTime);
const duration = Number(BigInt($endtime.getTime()) * BigInt(1_000_000) - startTime);

if (minimumDuration && duration < minimumDuration) {
return minimumDuration;
Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/v3/eventRepository/index.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ async function recordRunEvent(
runId: foundRun.friendlyId,
...attributes,
},
startTime: BigInt((startTime?.getTime() ?? Date.now()) * 1_000_000),
startTime: BigInt(startTime?.getTime() ?? Date.now()) * BigInt(1_000_000),
...optionsRest,
});

Expand Down
2 changes: 1 addition & 1 deletion apps/webapp/app/v3/runEngineHandlers.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ export function registerRunEngineEventBusHandlers() {
);

await eventRepository.recordEvent(retryMessage, {
startTime: BigInt(time.getTime() * 1000000),
startTime: BigInt(time.getTime()) * BigInt(1_000_000),
taskSlug: run.taskIdentifier,
environment,
attributes: {
Expand Down