Skip to content

Commit 798fe93

Browse files
lohanidamodarclaude
andcommitted
refactor: daily MV table uses minimal schema
Daily table only has metric, value, time, resource, resourceId, tenant. No path/status/userAgent/country/tags — those don't aggregate meaningfully. MV groups by metric, resource, resourceId, tenant, day. ORDER BY includes resource and resourceId for efficient billing queries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dc288d5 commit 798fe93

1 file changed

Lines changed: 27 additions & 29 deletions

File tree

src/Usage/Adapter/ClickHouse.php

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,47 +1028,39 @@ private function createTable(string $tableName, string $type, array $indexes): v
10281028
/**
10291029
* Create the events daily SummingMergeTree table.
10301030
*
1031+
* Minimal schema: only metric, value, time, resource, resourceId, tenant.
1032+
* No path/status/userAgent/country/tags — those don't aggregate.
1033+
*
10311034
* @throws Exception
10321035
*/
10331036
private function createDailyTable(): void
10341037
{
10351038
$dailyTableName = $this->getEventsDailyTableName();
10361039
$escapedDailyTable = $this->escapeIdentifier($this->database) . '.' . $this->escapeIdentifier($dailyTableName);
10371040

1038-
// Daily table has same schema as events table
1039-
$columns = ['id String'];
1040-
1041-
foreach ($this->getAttributes('event') as $attribute) {
1042-
/** @var string $id */
1043-
$id = $attribute['$id'];
1044-
1045-
if ($id === 'time') {
1046-
$columns[] = 'time DateTime64(3)';
1047-
} else {
1048-
$columns[] = $this->getColumnDefinition($id, 'event');
1049-
}
1050-
}
1041+
$columns = [
1042+
'metric String',
1043+
'value Int64',
1044+
'time DateTime64(3)',
1045+
'resource Nullable(String)',
1046+
'resourceId Nullable(String)',
1047+
];
10511048

10521049
if ($this->sharedTables) {
10531050
$columns[] = 'tenant Nullable(String)';
10541051
}
10551052

1056-
$indexes = [];
1057-
foreach ($this->getEventIndexes() as $index) {
1058-
/** @var string $indexName */
1059-
$indexName = $index['$id'];
1060-
/** @var array<string> $attributes */
1061-
$attributes = $index['attributes'];
1062-
$escapedIndexName = $this->escapeIdentifier($indexName);
1063-
$escapedAttributes = array_map(fn ($attr) => $this->escapeIdentifier($attr), $attributes);
1064-
$attributeList = implode(', ', $escapedAttributes);
1065-
$indexes[] = "INDEX {$escapedIndexName} ({$attributeList}) TYPE bloom_filter GRANULARITY 1";
1066-
}
1053+
$indexes = [
1054+
'INDEX index_metric (metric) TYPE bloom_filter GRANULARITY 1',
1055+
'INDEX index_time (time) TYPE bloom_filter GRANULARITY 1',
1056+
'INDEX index_resource (resource) TYPE bloom_filter GRANULARITY 1',
1057+
'INDEX index_resourceId (resourceId) TYPE bloom_filter GRANULARITY 1',
1058+
];
10671059

10681060
$columnDefs = implode(",\n ", $columns);
1069-
$indexDefsStr = !empty($indexes) ? ",\n " . implode(",\n ", $indexes) : '';
1061+
$indexDefsStr = ",\n " . implode(",\n ", $indexes);
10701062

1071-
$dailyOrderBy = $this->sharedTables ? '(tenant, metric, time)' : '(metric, time)';
1063+
$dailyOrderBy = $this->sharedTables ? '(tenant, metric, resource, resourceId, time)' : '(metric, resource, resourceId, time)';
10721064

10731065
$createDailyTableSql = "
10741066
CREATE TABLE IF NOT EXISTS {$escapedDailyTable} (
@@ -1101,11 +1093,11 @@ private function createDailyMaterializedView(): void
11011093
if ($this->sharedTables) {
11021094
$innerSelect = "metric, resource, resourceId, tenant, sum(value) as value, toStartOfDay(time) as d";
11031095
$innerGroupBy = "metric, resource, resourceId, tenant, d";
1104-
$outerSelect = "generateUUIDv4() as id, metric, value, d as time, '' as path, '' as method, '' as status, resource, resourceId, '{}' as tags, tenant";
1096+
$outerSelect = "metric, value, d as time, resource, resourceId, tenant";
11051097
} else {
11061098
$innerSelect = "metric, resource, resourceId, sum(value) as value, toStartOfDay(time) as d";
11071099
$innerGroupBy = "metric, resource, resourceId, d";
1108-
$outerSelect = "generateUUIDv4() as id, metric, value, d as time, '' as path, '' as method, '' as status, resource, resourceId, '{}' as tags";
1100+
$outerSelect = "metric, value, d as time, resource, resourceId";
11091101
}
11101102

11111103
$createDailyMvSql = "
@@ -1635,10 +1627,16 @@ public function findDaily(array $queries = []): array
16351627

16361628
$fromTable = $this->buildTableReference($this->getEventsDailyTableName());
16371629

1630+
// Daily table has limited columns — only allow metric, value, time, resource, resourceId, tenant
16381631
$parsed = $this->parseQueries($queries, Usage::TYPE_EVENT);
1639-
$selectColumns = $this->getSelectColumns(Usage::TYPE_EVENT);
16401632
$whereData = $this->buildWhereClause($parsed['filters'], $parsed['params']);
16411633

1634+
$dailyColumns = ['metric', 'value', 'time', 'resource', 'resourceId'];
1635+
if ($this->sharedTables) {
1636+
$dailyColumns[] = 'tenant';
1637+
}
1638+
$selectColumns = implode(', ', array_map(fn ($c) => $this->escapeIdentifier($c), $dailyColumns));
1639+
16421640
$orderClause = !empty($parsed['orderBy']) ? ' ORDER BY ' . implode(', ', $parsed['orderBy']) : '';
16431641
$limitClause = isset($parsed['limit']) ? ' LIMIT {limit:UInt64}' : '';
16441642
$offsetClause = isset($parsed['offset']) ? ' OFFSET {offset:UInt64}' : '';

0 commit comments

Comments
 (0)