Skip to content

chore(clickhouse): 30d TTL on the trace-list rollup tables#137

Merged
arseniycodes merged 2 commits into
mainfrom
ash/trace-list-rollup-ttl
Jul 3, 2026
Merged

chore(clickhouse): 30d TTL on the trace-list rollup tables#137
arseniycodes merged 2 commits into
mainfrom
ash/trace-list-rollup-ttl

Conversation

@arseniycodes

@arseniycodes arseniycodes commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #136. The rollup tables (otel_traces_recent, otel_traces_summary) were created without a TTL, so they'd grow unbounded — and otel_traces_recent holds one row per span (the full span history). Give both the same 30-day TTL as otel_traces (toDateTime(<ts>) + toIntervalDay(30)) so the derived data ages out in lockstep with the spans it summarizes.

  • migrations/006_otel_traces_rollup_ttl.sqlALTER … MODIFY TTL for existing clusters (idempotent; ttl_only_drop_parts = 1 keeps enforcement to whole-part drops).
  • Bootstrap schema updated inline so fresh clusters get the TTL at creation.

No app code change.


Note

Low Risk
Infrastructure-only ClickHouse retention on derived tables; read path already falls back to raw traces when rollups don’t cover the window.

Overview
Adds 30-day retention on the trace-list rollup tables otel_traces_recent and otel_traces_summary so they no longer grow without bound (especially otel_traces_recent, which stores one row per span).

otel_traces_recent expires rows 30 days after each span’s ts. otel_traces_summary uses end_unix_nano (last span end), not trace start, so long-running traces don’t lose summary stats while recent rows for the same trace_id still exist.

Existing HA clusters get idempotent ALTER … MODIFY TTL in 006_otel_traces_rollup_ttl.sql; fresh installs pick up the same rules in ha-replicated-otel.sql, still with ttl_only_drop_parts = 1. No application changes—older windows that rollups no longer cover keep using the existing raw otel_traces fallback via the coverage gate.

Reviewed by Cursor Bugbot for commit 2c3fe52. Bugbot is set up for automated code reviews on this repo. Configure here.


Summary by cubic

Add a 30-day TTL to the ClickHouse rollup tables otel_traces_recent and otel_traces_summary to cap storage. The summary TTL anchors on the trace end so fast-path stats stay consistent; older windows fall back to the raw scan.

  • Migration
    • Adds infra/clickhouse/migrations/006_otel_traces_rollup_ttl.sql to ALTER ... MODIFY TTL on superlog.otel_traces_recent and superlog.otel_traces_summary across the superlog_ha cluster.
    • TTL rules: toDateTime(ts) + toIntervalDay(30) on otel_traces_recent; toDateTime(fromUnixTimestamp64Nano(end_unix_nano)) + toIntervalDay(30) on otel_traces_summary.
    • Bootstrap schema updated so new clusters get the TTL; keeps ttl_only_drop_parts = 1 for whole-part drops.

Written for commit 2c3fe52. Summary will update on new commits.

Review in cubic

…otel_traces

The 005 rollup tables were created without a TTL, so they would grow unbounded —
and otel_traces_recent is one row per span (the full span history). Add a
30-day TTL matching otel_traces (toDateTime(<ts>) + toIntervalDay(30)) via
migration 006 (ALTER MODIFY TTL) and inline in the bootstrap schema, so the
derived data ages out in lockstep with the spans it summarizes.

@cursor cursor 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit dc51437. Configure here.

Comment thread infra/clickhouse/migrations/006_otel_traces_rollup_ttl.sql Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

3 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="infra/clickhouse/schema/ha-replicated-otel.sql">

<violation number="1" location="infra/clickhouse/schema/ha-replicated-otel.sql:476">
P2: Trace-list rollups are now hard-capped at 30 days, but the repository’s `otel_traces` source table definition still has no TTL. That means older traces can still exist in `otel_traces` while `otel_traces_recent`/`otel_traces_summary` have already dropped them, causing the rollup-backed list path to under-report historical traces relative to source data. Consider adding/updating the source table TTL in the same schema/migration set (or documenting/enforcing it in code-managed SQL) so retention actually stays in lockstep.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

ENGINE = ReplicatedMergeTree('/clickhouse/{cluster}/tables/{shard}/{database}/{table}', '{replica}')
PARTITION BY toDate(ts)
ORDER BY (project_id, ts)
TTL toDateTime(ts) + toIntervalDay(30)

@cubic-dev-ai cubic-dev-ai Bot Jul 3, 2026

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.

P2: Trace-list rollups are now hard-capped at 30 days, but the repository’s otel_traces source table definition still has no TTL. That means older traces can still exist in otel_traces while otel_traces_recent/otel_traces_summary have already dropped them, causing the rollup-backed list path to under-report historical traces relative to source data. Consider adding/updating the source table TTL in the same schema/migration set (or documenting/enforcing it in code-managed SQL) so retention actually stays in lockstep.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At infra/clickhouse/schema/ha-replicated-otel.sql, line 476:

<comment>Trace-list rollups are now hard-capped at 30 days, but the repository’s `otel_traces` source table definition still has no TTL. That means older traces can still exist in `otel_traces` while `otel_traces_recent`/`otel_traces_summary` have already dropped them, causing the rollup-backed list path to under-report historical traces relative to source data. Consider adding/updating the source table TTL in the same schema/migration set (or documenting/enforcing it in code-managed SQL) so retention actually stays in lockstep.</comment>

<file context>
@@ -473,6 +473,7 @@ CREATE TABLE IF NOT EXISTS superlog.otel_traces_recent ON CLUSTER superlog_ha
 ENGINE = ReplicatedMergeTree('/clickhouse/{cluster}/tables/{shard}/{database}/{table}', '{replica}')
 PARTITION BY toDate(ts)
 ORDER BY (project_id, ts)
+TTL toDateTime(ts) + toIntervalDay(30)
 SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1
 ;
</file context>
Fix with cubic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional, and now spelled out in the migration comment. We are deliberately not adding a TTL to the open-core otel_traces (005’s rationale stands). Capping only the derived tables does not under-report: the read path gates on the recent index covering the window start (traceRollupCoversWindow) and falls back to the raw otel_traces scan for anything older than the rollup retains. So beyond the derived retention the list is served the slow-but-complete way from source, rather than silently truncated — retention stays correct without forcing a source-table TTL.

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.

The parent comment was wrong here: the derived-table TTL is intentional, and traceRollupCoversWindow already falls back to the raw otel_traces scan, so older windows aren’t silently truncated. The source table TTL doesn’t need to be added for correctness in this PR.

Comment thread infra/clickhouse/schema/ha-replicated-otel.sql Outdated
Comment thread infra/clickhouse/migrations/006_otel_traces_rollup_ttl.sql Outdated
…ntion

Review feedback:

- Summary TTL was anchored on `start` (earliest span). A trace whose spans span
  several days would then lose its summary row 30d after its FIRST span, while
  otel_traces_recent rows (and source spans) for its LATER spans still exist —
  the fast path could find the trace_id but return no stats. Anchor the summary
  TTL on `end_unix_nano` (the trace's last span) so the summary outlives / expires
  with the last recent row.

- The comment claimed otel_traces carries a 30d TTL; the open-core source table
  has none. Reframe: this is a standalone 30d bound on the derived tables (the
  recent index is one row per span, so it would otherwise grow unbounded), made
  safe by the read path's coverage gate, which falls back to the raw scan for any
  window the rollup no longer covers — so a shorter derived retention never
  under-reports.
@arseniycodes arseniycodes merged commit a17bd20 into main Jul 3, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant