Skip to content

feat(clickhouse): add optional retention TTL on the snapshot table#20

Open
levivannoort wants to merge 2 commits into
utopia-php:devfrom
levivannoort:feat/retention-ttl
Open

feat(clickhouse): add optional retention TTL on the snapshot table#20
levivannoort wants to merge 2 commits into
utopia-php:devfrom
levivannoort:feat/retention-ttl

Conversation

@levivannoort

Copy link
Copy Markdown

What

Adds setRetention(?int $days) to the ClickHouse usage adapter. When set, setup() applies an idempotent TTL to the snapshot (ReplacingMergeTree) table so latest-state rows past the retention window are dropped by background merges:

$adapter->setRetention(30);
$usage->setup(); // ALTER TABLE {ns}_usage_snapshot ... MODIFY TTL toDateTime(time) + INTERVAL 30 DAY

Default null preserves current behaviour (no TTL).

Scope: snapshot only

Only the snapshot table gets a TTL. The aggregated {ns}_usage (SummingMergeTree) table is intentionally left untouched — it backs long-term usage/billing history, so dropping rows there would delete billing data.

Why in setup()

Retention is part of the table's schema, and setup() already owns everything else about it. Keeping TTL here means callers that already run setup() get retention with no extra plumbing, and no downstream code has to re-derive table names.

How

  • Applied as a separate ALTER … MODIFY TTL, not baked into CREATE TABLE, since CREATE TABLE IF NOT EXISTS won't touch an existing table. MODIFY TTL is a no-op when unchanged, so setup() stays idempotent/re-runnable.
  • SETTINGS materialize_ttl_after_modify = 0 defers the purge to normal background merges instead of an immediate mutation.
  • time is DateTime64(3); toDateTime(time) narrows it for the TTL expression.
  • setRetention() rejects non-positive day counts.

Pint (composer lint) and PHPStan level max (composer check) pass on the changed file.

🤖 Generated with Claude Code

Add setRetention(?int $days) to the ClickHouse adapter. When set, setup()
applies an idempotent `ALTER TABLE ... MODIFY TTL toDateTime(time) + INTERVAL
<days> DAY` to the snapshot (ReplacingMergeTree) table so latest-state rows
older than the window are dropped by background merges.

- Only the snapshot table gets a TTL; the aggregated (SummingMergeTree) table
  is left untouched since it backs long-term usage/billing history.
- Applied as a separate ALTER (not in CREATE TABLE) so it also covers
  already-existing tables; MODIFY TTL is a no-op when unchanged, keeping
  setup() re-runnable.
- materialize_ttl_after_modify = 0 defers the purge to background merges
  instead of an immediate mutation.
- Default null preserves existing behaviour (no TTL).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jul 2, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds an optional retention TTL to the ClickHouse adapter's snapshot (ReplacingMergeTree) table. When setRetention($days) is called before setup(), the table gets an idempotent MODIFY TTL applied; when retention is null (the default), setup() runs REMOVE TTL so any previously-set TTL is actively cleared.

  • setRetention(?int $days) validates positive values and returns self for fluent chaining; the backing field is correctly private with no subclass bypass.
  • setup() now unconditionally reconciles the snapshot table's TTL state on every run — applying MODIFY TTL … INTERVAL N DAY or REMOVE TTL — keeping the method idempotent and self-contained.

Confidence Score: 5/5

Safe to merge. The change is narrowly scoped to schema management in setup(), the property is private with a validated setter, and both retention paths (apply TTL / remove TTL) are present and idempotent.

The new TTL logic is well-contained: injection risk is eliminated because $this->retention is always a validated positive integer or null before it reaches the SQL string. The REMOVE TTL branch ensures the snapshot table's TTL state is always reconciled to the caller's intent on every setup() run. Both concerns raised in prior review threads are addressed in this revision. No new defects were identified.

No files require special attention.

Important Files Changed

Filename Overview
src/Usage/Adapter/ClickHouse.php Adds optional retention TTL on the snapshot table: setRetention(?int $days) / getRetention() methods, private property with positive-integer guard, and idempotent MODIFY TTL / REMOVE TTL branches in setup(). Both previously-raised issues (protected visibility, missing REMOVE TTL for null) are resolved.

Reviews (2): Last reviewed commit: "fix(clickhouse): remove TTL when retenti..." | Re-trigger Greptile

Comment thread src/Usage/Adapter/ClickHouse.php
Comment thread src/Usage/Adapter/ClickHouse.php Outdated
…n private

Addresses review feedback on the retention TTL:
- setup() now issues ALTER TABLE ... REMOVE TTL on the null path so
  disabling retention actually strips a previously applied TTL, matching
  the documented "pass null to disable" contract. REMOVE TTL is a no-op
  on a table without a TTL, so setup() stays idempotent.
- $retention is now private, matching the other sensitive fields and
  preventing subclasses from bypassing the positive-day guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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