From a1253482b4240665bc2afb84fc1fda33537d097d Mon Sep 17 00:00:00 2001 From: kmaclip Date: Fri, 3 Apr 2026 15:54:00 -0400 Subject: [PATCH] fix: convert to BigInt before nanosecond multiplication to avoid precision loss Multiplying epoch milliseconds by 1,000,000 as a Number exceeds Number.MAX_SAFE_INTEGER (~9e15), causing IEEE 754 precision loss of ~256ns in ~0.2% of cases. Convert to BigInt first, then multiply. Fixed in 4 locations: - getNowInNanoseconds() in common.server.ts - calculateDurationFromStart() in common.server.ts - recordRunDebugLog() in index.server.ts - retry event recording in runEngineHandlers.server.ts The correct pattern (BigInt(ms) * BigInt(1_000_000)) already existed in convertDateToNanoseconds() in the same file. Closes #3292 --- apps/webapp/app/v3/eventRepository/common.server.ts | 4 ++-- apps/webapp/app/v3/eventRepository/index.server.ts | 2 +- apps/webapp/app/v3/runEngineHandlers.server.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/webapp/app/v3/eventRepository/common.server.ts b/apps/webapp/app/v3/eventRepository/common.server.ts index 3ba8a50c7f7..5d5d0e201f1 100644 --- a/apps/webapp/app/v3/eventRepository/common.server.ts +++ b/apps/webapp/app/v3/eventRepository/common.server.ts @@ -21,7 +21,7 @@ export function extractContextFromCarrier(carrier: Record) { } export function getNowInNanoseconds(): bigint { - return BigInt(new Date().getTime() * 1_000_000); + return BigInt(new Date().getTime()) * BigInt(1_000_000); } export function getDateFromNanoseconds(nanoseconds: bigint): Date { @@ -35,7 +35,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; diff --git a/apps/webapp/app/v3/eventRepository/index.server.ts b/apps/webapp/app/v3/eventRepository/index.server.ts index 70ea6440321..edc0280baf2 100644 --- a/apps/webapp/app/v3/eventRepository/index.server.ts +++ b/apps/webapp/app/v3/eventRepository/index.server.ts @@ -215,7 +215,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, }); diff --git a/apps/webapp/app/v3/runEngineHandlers.server.ts b/apps/webapp/app/v3/runEngineHandlers.server.ts index 411f91ff75d..aa355cd04dd 100644 --- a/apps/webapp/app/v3/runEngineHandlers.server.ts +++ b/apps/webapp/app/v3/runEngineHandlers.server.ts @@ -428,7 +428,7 @@ export function registerRunEngineEventBusHandlers() { const eventRepository = resolveEventRepositoryForStore(run.taskEventStore); await eventRepository.recordEvent(retryMessage, { - startTime: BigInt(time.getTime() * 1000000), + startTime: BigInt(time.getTime()) * BigInt(1_000_000), taskSlug: run.taskIdentifier, environment, attributes: {