From 3827f4d0973d6596aaf9167dafefd7dfa2c0be4c Mon Sep 17 00:00:00 2001 From: okxint Date: Fri, 9 Oct 2026 15:21:24 +0530 Subject: [PATCH] fix: use BigInt arithmetic for nanosecond timestamp conversion Multiplying Date.getTime() by 1_000_000 as a float before converting to BigInt causes IEEE 754 precision loss once the product exceeds Number.MAX_SAFE_INTEGER (~9007T, which ms * 1e6 hits around 2255 AD). The correct pattern already exists in convertDateToNanoseconds(): BigInt(date.getTime()) * BigInt(1_000_000) Apply it consistently to the three call sites that still use the float-multiply pattern (getNowInNanoseconds, calculateDurationFromStart, and the runEngineHandlers retry-event recordEvent call). --- .server-changes/fix-otlp-nanosecond-bigint-precision.md | 6 ++++++ 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 +- 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .server-changes/fix-otlp-nanosecond-bigint-precision.md diff --git a/.server-changes/fix-otlp-nanosecond-bigint-precision.md b/.server-changes/fix-otlp-nanosecond-bigint-precision.md new file mode 100644 index 00000000000..c3fc806cfa6 --- /dev/null +++ b/.server-changes/fix-otlp-nanosecond-bigint-precision.md @@ -0,0 +1,6 @@ +--- +area: webapp +type: fix +--- + +Fix OTLP trace timestamps losing precision for runs after approximately 2255 AD. Timestamps are now computed with full 64-bit integer arithmetic. diff --git a/apps/webapp/app/v3/eventRepository/common.server.ts b/apps/webapp/app/v3/eventRepository/common.server.ts index 346e2ab6419..3dce9202eca 100644 --- a/apps/webapp/app/v3/eventRepository/common.server.ts +++ b/apps/webapp/app/v3/eventRepository/common.server.ts @@ -26,7 +26,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 { @@ -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; diff --git a/apps/webapp/app/v3/eventRepository/index.server.ts b/apps/webapp/app/v3/eventRepository/index.server.ts index c599f4e6b96..670ec60f014 100644 --- a/apps/webapp/app/v3/eventRepository/index.server.ts +++ b/apps/webapp/app/v3/eventRepository/index.server.ts @@ -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, }); diff --git a/apps/webapp/app/v3/runEngineHandlers.server.ts b/apps/webapp/app/v3/runEngineHandlers.server.ts index ee4ef2e278d..08e3194bfc7 100644 --- a/apps/webapp/app/v3/runEngineHandlers.server.ts +++ b/apps/webapp/app/v3/runEngineHandlers.server.ts @@ -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: {