Skip to content

Commit 64833d6

Browse files
lohanidamodarclaude
andcommitted
refactor: remove billing MV, keep only daily MV with same schema
- Remove separate billing table and MV (monthly aggregation) - Daily MV uses same column definitions as source table - Billing queries use daily table (SUM over daily aggregated rows) - Only events are pre-aggregated; gauges query raw table Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 02dddce commit 64833d6

1 file changed

Lines changed: 14 additions & 71 deletions

File tree

src/Usage/Adapter/ClickHouse.php

Lines changed: 14 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* Uses a single MergeTree table with a 'type' column ('event' or 'gauge')
1717
* and query-time aggregation (SUM for events, argMax for gauges).
1818
*
19-
* A SummingMergeTree materialized view is created for billing totals (events only).
19+
* A SummingMergeTree materialized view pre-aggregates events by day for fast billing/analytics queries.
2020
*
2121
* Features:
2222
* - Single MergeTree table for all metrics (no period fan-out)
@@ -881,7 +881,7 @@ private function formatParamValue(mixed $value): string
881881
/**
882882
* Setup ClickHouse table structure.
883883
*
884-
* Creates a single MergeTree table and a SummingMergeTree materialized view for billing.
884+
* Creates a single MergeTree table and a SummingMergeTree daily materialized view for events.
885885
*
886886
* @throws Exception
887887
*/
@@ -949,81 +949,21 @@ public function setup(): void
949949

950950
$this->query($createTableSql);
951951

952-
// Create billing target table (SummingMergeTree)
953-
$billingTableName = $tableName . '_billing';
954-
$escapedBillingTable = $this->escapeIdentifier($this->database) . '.' . $this->escapeIdentifier($billingTableName);
955-
956-
$billingColumns = [
957-
'metric String',
958-
'value Int64',
959-
'time DateTime64(3)',
960-
];
961-
962-
if ($this->sharedTables) {
963-
array_splice($billingColumns, 1, 0, ['tenant Nullable(String)']);
964-
}
965-
966-
$billingColumnDefs = implode(",\n ", $billingColumns);
967-
$billingOrderBy = $this->sharedTables ? '(tenant, metric, time)' : '(metric, time)';
968-
969-
$createBillingTableSql = "
970-
CREATE TABLE IF NOT EXISTS {$escapedBillingTable} (
971-
{$billingColumnDefs}
972-
)
973-
ENGINE = SummingMergeTree()
974-
ORDER BY {$billingOrderBy}
975-
SETTINGS allow_nullable_key = 1
976-
";
977-
978-
$this->query($createBillingTableSql);
979-
980-
// Create materialized view for billing (events only)
981-
$billingMvName = $tableName . '_billing_mv';
982-
$escapedBillingMv = $this->escapeIdentifier($this->database) . '.' . $this->escapeIdentifier($billingMvName);
983-
984-
$tenantSelect = $this->sharedTables ? 'tenant' : "'' as tenant";
985-
$groupByClause = $this->sharedTables ? 'metric, tenant, time' : 'metric, time';
986-
987-
$billingSelectColumns = $this->sharedTables
988-
? "metric,\n {$tenantSelect},\n sum(value) as value,\n toStartOfMonth(time) as time"
989-
: "metric,\n sum(value) as value,\n toStartOfMonth(time) as time";
990-
991-
$createBillingMvSql = "
992-
CREATE MATERIALIZED VIEW IF NOT EXISTS {$escapedBillingMv}
993-
TO {$escapedBillingTable}
994-
AS SELECT
995-
{$billingSelectColumns}
996-
FROM {$escapedDatabaseAndTable}
997-
WHERE type = 'event'
998-
GROUP BY {$groupByClause}
999-
";
1000-
1001-
$this->query($createBillingMvSql);
1002-
1003952
// Create daily aggregation target table (SummingMergeTree) for events
953+
// Same column definitions as source table — just pre-aggregated by day
1004954
$dailyTableName = $tableName . '_daily';
1005955
$escapedDailyTable = $this->escapeIdentifier($this->database) . '.' . $this->escapeIdentifier($dailyTableName);
1006956

1007-
$dailyColumns = [
1008-
'metric String',
1009-
'value Int64',
1010-
'time DateTime64(3)',
1011-
];
1012-
1013-
if ($this->sharedTables) {
1014-
array_splice($dailyColumns, 1, 0, ['tenant Nullable(String)']);
1015-
}
1016-
1017-
$dailyColumnDefs = implode(",\n ", $dailyColumns);
1018957
$dailyOrderBy = $this->sharedTables ? '(tenant, metric, time)' : '(metric, time)';
1019958

1020959
$createDailyTableSql = "
1021960
CREATE TABLE IF NOT EXISTS {$escapedDailyTable} (
1022-
{$dailyColumnDefs}
961+
{$columnDefs}{$indexDefs}
1023962
)
1024963
ENGINE = SummingMergeTree()
1025964
ORDER BY {$dailyOrderBy}
1026-
SETTINGS allow_nullable_key = 1
965+
PARTITION BY toYYYYMM(time)
966+
SETTINGS index_granularity = 8192, allow_nullable_key = 1
1027967
";
1028968

1029969
$this->query($createDailyTableSql);
@@ -1032,18 +972,21 @@ public function setup(): void
1032972
$dailyMvName = $tableName . '_daily_mv';
1033973
$escapedDailyMv = $this->escapeIdentifier($this->database) . '.' . $this->escapeIdentifier($dailyMvName);
1034974

1035-
$dailySelectColumns = $this->sharedTables
1036-
? "metric,\n {$tenantSelect},\n sum(value) as value,\n toStartOfDay(time) as time"
1037-
: "metric,\n sum(value) as value,\n toStartOfDay(time) as time";
975+
$tenantSelect = $this->sharedTables ? 'tenant' : "'' as tenant";
976+
$groupByClause = $this->sharedTables ? 'metric, tenant' : 'metric';
977+
978+
$dailySelect = $this->sharedTables
979+
? "generateUUIDv4() as id, metric, {$tenantSelect}, sum(value) as value, 'event' as type, toStartOfDay(time) as time, '{}' as tags"
980+
: "generateUUIDv4() as id, metric, sum(value) as value, 'event' as type, toStartOfDay(time) as time, '{}' as tags";
1038981

1039982
$createDailyMvSql = "
1040983
CREATE MATERIALIZED VIEW IF NOT EXISTS {$escapedDailyMv}
1041984
TO {$escapedDailyTable}
1042985
AS SELECT
1043-
{$dailySelectColumns}
986+
{$dailySelect}
1044987
FROM {$escapedDatabaseAndTable}
1045988
WHERE type = 'event'
1046-
GROUP BY {$groupByClause}
989+
GROUP BY {$groupByClause}, toStartOfDay(time)
1047990
";
1048991

1049992
$this->query($createDailyMvSql);

0 commit comments

Comments
 (0)