-
Notifications
You must be signed in to change notification settings - Fork 145
feat: new clickhouse operation table rollups #8479
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| import type { Action } from '../clickhouse'; | ||
|
|
||
| type Granularity = 'daily' | 'hourly' | 'minutely'; | ||
|
|
||
| const pickUInt = (aggregation: Granularity) => (aggregation === 'daily' ? 'UInt64' : 'UInt32'); | ||
|
|
||
| const aggregateColumns = (granularity: Granularity) => ` | ||
| total ${pickUInt(granularity)} CODEC(T64, ZSTD(1)), | ||
| total_ok ${pickUInt(granularity)} CODEC(T64, ZSTD(1)), | ||
| duration_avg AggregateFunction(avg, UInt64) CODEC(ZSTD(1)), | ||
| duration_quantiles AggregateFunction(quantilesTDigest(0.75, 0.9, 0.95, 0.99), UInt64) CODEC(ZSTD(1)) | ||
| `; | ||
|
|
||
| const aggregateStates = (granularity: Granularity) => ` | ||
| CAST(count() AS ${pickUInt(granularity)}) AS total, | ||
| CAST(sum(ok) AS ${pickUInt(granularity)}) AS total_ok, | ||
| avgState(duration) AS duration_avg, | ||
| quantilesTDigestState(0.75, 0.9, 0.95, 0.99)(duration) AS duration_quantiles | ||
| `; | ||
|
|
||
| const createRollups = async ( | ||
| exec: (query: string) => Promise<void>, | ||
| granularity: Granularity, | ||
| bucket: 'toStartOfMinute' | 'toStartOfHour' | 'toStartOfDay', | ||
| partitionBy: string, | ||
| ttlInterval: string, | ||
| ) => { | ||
| const table = `operations_v01_${granularity}`; | ||
|
|
||
| await exec(` | ||
| CREATE TABLE IF NOT EXISTS default.${table} | ||
| ( | ||
| target LowCardinality(String) CODEC(ZSTD(1)), | ||
| graph_id LowCardinality(String) CODEC(ZSTD(1)), | ||
| timestamp DateTime('UTC') CODEC(DoubleDelta, LZ4), | ||
| hash String CODEC(ZSTD(1)), | ||
| client_name String CODEC(ZSTD(1)), | ||
| client_version String CODEC(ZSTD(1)), | ||
| graph_version_id String CODEC(ZSTD(1)), | ||
| ${aggregateColumns(granularity)} | ||
| ) | ||
| ENGINE = SummingMergeTree | ||
| PARTITION BY ${partitionBy} | ||
| PRIMARY KEY (target, graph_id, timestamp, hash) | ||
| ORDER BY (target, graph_id, timestamp, hash, client_name, client_version, graph_version_id) | ||
| TTL timestamp + INTERVAL ${ttlInterval} | ||
| SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1 | ||
| `); | ||
|
|
||
| await exec(` | ||
| CREATE MATERIALIZED VIEW IF NOT EXISTS default.${table}_mv TO default.${table} | ||
| AS ( | ||
| SELECT | ||
| target, | ||
| graph_id, | ||
| ${bucket}(timestamp) AS timestamp, | ||
| hash, | ||
| client_name, | ||
| client_version, | ||
| graph_version_id, | ||
| ${aggregateStates(granularity)} | ||
| FROM default.operations | ||
| GROUP BY target, graph_id, timestamp, hash, client_name, client_version, graph_version_id | ||
| ) | ||
| `); | ||
|
|
||
| await exec(` | ||
| CREATE TABLE IF NOT EXISTS default.${table}_by_timestamp | ||
| ( | ||
| target LowCardinality(String) CODEC(ZSTD(1)), | ||
| graph_id LowCardinality(String) CODEC(ZSTD(1)), | ||
| timestamp DateTime('UTC') CODEC(DoubleDelta, LZ4), | ||
| graph_version_id String CODEC(ZSTD(1)), | ||
| ${aggregateColumns(granularity)} | ||
| ) | ||
| ENGINE = SummingMergeTree | ||
| PARTITION BY ${partitionBy} | ||
| PRIMARY KEY (target, graph_id, timestamp) | ||
| ORDER BY (target, graph_id, timestamp, graph_version_id) | ||
| TTL timestamp + INTERVAL ${ttlInterval} | ||
| SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1 | ||
| `); | ||
|
|
||
| await exec(` | ||
| CREATE MATERIALIZED VIEW IF NOT EXISTS default.${table}_by_timestamp_mv TO default.${table}_by_timestamp | ||
| AS ( | ||
| SELECT | ||
| target, | ||
| graph_id, | ||
| graph_version_id, | ||
| ${bucket}(timestamp) AS timestamp, | ||
| ${aggregateStates(granularity)} | ||
| FROM default.operations | ||
| GROUP BY target, graph_id, timestamp, graph_version_id | ||
| ) | ||
| `); | ||
|
|
||
| await exec(` | ||
| CREATE TABLE IF NOT EXISTS default.${table}_by_client | ||
| ( | ||
| target LowCardinality(String) CODEC(ZSTD(1)), | ||
| graph_id LowCardinality(String) CODEC(ZSTD(1)), | ||
| timestamp DateTime('UTC') CODEC(DoubleDelta, LZ4), | ||
| client_name String CODEC(ZSTD(1)), | ||
| client_version String CODEC(ZSTD(1)), | ||
| graph_version_id String CODEC(ZSTD(1)), | ||
| ${aggregateColumns(granularity)} | ||
| ) | ||
| ENGINE = SummingMergeTree | ||
| PARTITION BY ${partitionBy} | ||
| PRIMARY KEY (target, graph_id, timestamp, client_name, client_version) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This table was under utilized in your benchmarks. I still think it's worth adding a |
||
| ORDER BY (target, graph_id, timestamp, client_name, client_version, graph_version_id) | ||
| TTL timestamp + INTERVAL ${ttlInterval} | ||
| SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1 | ||
| `); | ||
|
|
||
| await exec(` | ||
| CREATE MATERIALIZED VIEW IF NOT EXISTS default.${table}_by_client_mv TO default.${table}_by_client | ||
| AS ( | ||
| SELECT | ||
| target, | ||
| graph_id, | ||
| ${bucket}(timestamp) AS timestamp, | ||
| client_name, | ||
| client_version, | ||
| graph_version_id, | ||
| ${aggregateStates(granularity)} | ||
| FROM default.operations | ||
| GROUP BY target, graph_id, timestamp, client_name, client_version, graph_version_id | ||
| ) | ||
| `); | ||
| }; | ||
|
|
||
| export const action: Action = async exec => { | ||
| await exec(` | ||
| ALTER TABLE default.operations | ||
| ADD COLUMN IF NOT EXISTS graph_id LowCardinality(String) DEFAULT '' CODEC(ZSTD(1)) AFTER target, | ||
| ADD COLUMN IF NOT EXISTS graph_version_id String DEFAULT '' CODEC(ZSTD(1)) AFTER graph_id | ||
| `); | ||
|
|
||
| await createRollups(exec, 'minutely', 'toStartOfMinute', 'toStartOfHour(timestamp)', '24 HOUR'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even though it means storing a bit more (b/c |
||
| await createRollups(exec, 'hourly', 'toStartOfHour', 'toYYYYMMDD(timestamp)', '30 DAY'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did we not want to do larger partitions? |
||
| await createRollups(exec, 'daily', 'toStartOfDay', 'toYYYYMM(timestamp)', '1 YEAR'); | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be called
_by_hashfor consistency?