Skip to content

fix(clickhouse): avoid server-tz drift in sandbox/team metrics filters#3304

Open
Piwriw wants to merge 5 commits into
e2b-dev:mainfrom
Piwriw:fix/clickhouse-timezone
Open

fix(clickhouse): avoid server-tz drift in sandbox/team metrics filters#3304
Piwriw wants to merge 5 commits into
e2b-dev:mainfrom
Piwriw:fix/clickhouse-timezone

Conversation

@Piwriw

@Piwriw Piwriw commented Jul 17, 2026

Copy link
Copy Markdown

Summary

  • Replace DateTime64 query parameters with Int64 Unix-nanosecond parameters converted through fromUnixTimestamp64Nano.
  • Apply the fix to:
    • sandbox_metrics_gauge queries in packages/clickhouse/pkg/sandbox.go
    • team_metrics_sum and team_metrics_gauge queries in packages/clickhouse/pkg/team.go
    • Existing sandbox and build log queries
  • Centralize ClickHouse timestamp conversion in packages/clickhouse/pkg/timestamp, including safe clamping for dates outside the int64 nanosecond range.

Why

clickhouse.DateNamed(..., clickhouse.Seconds) serializes a UTC time.Time as a wall-clock string without an offset, such as '2025-02-17 00:00:00'.

When a DateTime64(9) column has no explicit timezone, ClickHouse interprets that value using the server timezone. On a non-UTC server such as Asia/Shanghai, the query window shifts by the timezone offset. This can produce empty results for short ranges and stale or incorrect results for longer ranges.

The issue has existed since the original metrics migrations:

  • 20250717135224_sandbox_metrics.sql
  • 20250801113224_team_metrics.sql

The sandbox logs reader introduced in #3236 already used Unix nanoseconds. This PR applies the same timezone-independent approach to the older metrics readers and moves the shared conversion into a dedicated package.

Affected Endpoints

  • GET /sandboxes/{sandboxID}/metrics
  • GET /teams/{teamID}/metrics
  • GET /teams/{teamID}/metrics/max

Implementation

Unix nanoseconds represent an absolute instant. Passing them as Int64 values and converting them with fromUnixTimestamp64Nano avoids timezone-dependent string parsing:

timestamp >= fromUnixTimestamp64Nano({start_time:Int64})
AND timestamp <= fromUnixTimestamp64Nano({end_time:Int64})

The shared timestamp.UnixNano helper also clamps far-future and far-past values before conversion, preventing time.Time.UnixNano overflow.

Returned timestamps and their JSON serialization are unchanged.

Testing

  • Added focused tests for timestamp conversion, timezone normalization, and int64 range clamping.
  • Verified the ClickHouse timestamp, sandbox logs, and root client packages.

No schema changes or migrations are required.

Replace {start_time:DateTime64}/{end_time:DateTime64} placeholders with
fromUnixTimestamp64Nano({start:Int64})/{end:Int64} and pass Unix nano values
via clickhouse.Named, mirroring the pattern already used in
packages/clickhouse/pkg/sandboxlogs/sandboxlogs.go.

The previous DateNamed(..., clickhouse.Seconds) call serializes a UTC
time.Time to a wall-clock string without offset (e.g. '2025-02-17
00:00:00'). On a non-UTC server the column inherits that tz and the
filter window shifts by the offset (8h on CST), returning empty or stale
data for GET /sandboxes/{sandboxID}/metrics, GET /teams/{teamID}/metrics,
and GET /teams/{teamID}/metrics/max.

Unix nano is an absolute instant and sidesteps literal interpretation
entirely. Returned time.Time values still carry Location via the
DateTime64 column type, so downstream JSON serialization stays RFC3339.

Signed-off-by: joohwan <piwriw@163.com>
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@cla-bot cla-bot Bot added the cla-signed label Jul 17, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 482ce5f002

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/clickhouse/pkg/sandbox.go Outdated
@Piwriw

Piwriw commented Jul 17, 2026

Copy link
Copy Markdown
Author

Could someone familiar with the ClickHouse readers take a review pass? Happy to address any feedback.

API handlers accept Unix-second query params without an upper bound and
utils.GetSandboxStartEndTime passes them straight into time.Unix. Common
"up to latest" sentinels like 9999999999 (= year 2286) make the previous
start.UTC().UnixNano() wrap to a negative int64 before ClickHouse ever
sees it, silently breaking the fromUnixTimestamp64Nano filter window
built in the prior commit.

Go's time.Time.UnixNano is undefined beyond ~2262-04-11 (int64 ns range).
Add unixNanoForCH which clamps the Unix-second input to the largest value
whose nanosecond representation still fits in int64 (9223372036s =
2262-04-11T23:47:16Z), then converts. Routing all conversions through
this helper keeps the prior fix valid for unbounded end values.

Verified: end=9999999999 raw .UnixNano() = -8446744074709551616 (wrapped),
unixNanoForCH returns 9223372036000000000 (positive, in range).

Signed-off-by: joohwan <piwriw@163.com>
@Piwriw
Piwriw force-pushed the fix/clickhouse-timezone branch from 112194d to ff8d973 Compare July 17, 2026 17:12
The unixNano helper in packages/clickhouse/pkg/sandboxlogs/sandboxlogs.go
(introduced in e2b-dev#3236, 2026-07-08) had the same int64 overflow risk as the
code paths fixed in the previous commit. end=9999999999 (= year 2286, a
common "up to latest" sentinel) would wrap to a negative int64 and break
the fromUnixTimestamp64Nano filter for GET /sandboxes/{sandboxID}/logs
and GET /templates/{templateID}/builds/{buildID}/logs.

Apply the same Unix-second clamp used in packages/clickhouse/pkg/sandbox.go.
The constant is duplicated to avoid pulling the parent package into this
subpackage; consolidate into a shared timeutil package if a third caller
appears.

Signed-off-by: joohwan <piwriw@163.com>
@Piwriw
Piwriw force-pushed the fix/clickhouse-timezone branch from 726e2b2 to ddf14b7 Compare July 17, 2026 17:19
@Piwriw
Piwriw force-pushed the fix/clickhouse-timezone branch from 8e454de to f4b3287 Compare July 17, 2026 17:45
@Piwriw

Piwriw commented Jul 17, 2026

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f4b3287f34

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/clickhouse/pkg/timestamp/timestamp.go Outdated
@Piwriw

Piwriw commented Jul 17, 2026

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: fa5b1acda1

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant