Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions src/Usage/Adapter/ClickHouse.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ class ClickHouse extends SQL

protected string $namespace = '';

/** @var int|null Retention in days; when set, setup() applies a TTL to the snapshot table. Null disables TTL. */
private ?int $retention = null;

/** @var bool Whether to log queries for debugging */
private bool $enableQueryLogging = false;

Expand Down Expand Up @@ -507,6 +510,35 @@ public function isSharedTables(): bool
return $this->sharedTables;
}

/**
* Set the retention window in days. When set, setup() applies a TTL to the
* snapshot table so rows older than the window are dropped by background
* merges. The aggregated table is left untouched. Pass null to disable
* (the default).
*
* @param int|null $days
* @return self
* @throws Exception If $days is not positive
*/
public function setRetention(?int $days): self
{
if ($days !== null && $days < 1) {
throw new Exception('Retention must be a positive number of days');
}
$this->retention = $days;
return $this;
}

/**
* Get the retention window in days, or null when TTL is disabled.
*
* @return int|null
*/
public function getRetention(): ?int
{
return $this->retention;
}

/**
* Get the table name with namespace prefix.
* Namespace is used to isolate tables for different projects/applications.
Expand Down Expand Up @@ -1071,6 +1103,36 @@ public function setup(): void
";

$this->query($createCounterTableSql);

// Apply retention to the snapshot table only, as a separate idempotent
// ALTER. CREATE TABLE IF NOT EXISTS won't add a TTL to an existing
// table, and MODIFY TTL is a no-op when unchanged, so setup() stays
// re-runnable. The aggregated table is intentionally left untouched —
// it backs long-term usage/billing history. materialize_ttl_after_modify
// = 0 defers the purge to background merges rather than an immediate
// mutation.
if ($this->retention !== null) {
$this->query(
"ALTER TABLE {$escapedSnapshotDatabaseAndTable} "
. "MODIFY TTL toDateTime(time) + INTERVAL {$this->retention} DAY "
. 'SETTINGS materialize_ttl_after_modify = 0'
);
} else {
// Disabling retention must actively strip any TTL a previous run
// applied; otherwise rows keep being purged despite retention being
// null. ClickHouse errors (code 36) when REMOVE TTL runs on a table
// that has no TTL, so swallow that specific case to keep setup()
// idempotent.
try {
$this->query(
"ALTER TABLE {$escapedSnapshotDatabaseAndTable} REMOVE TTL"
);
} catch (Exception $e) {
if (!str_contains($e->getMessage(), "doesn't have any table TTL expression")) {
throw $e;
}
}
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
}

/**
Expand Down
Loading