[ENHANCEMENT] Clickhouse: support label and time access in table view#571
[ENHANCEMENT] Clickhouse: support label and time access in table view#571fraser-qs wants to merge 1 commit intoperses:mainfrom
Conversation
Signed-off-by: fraser-qs <fraser@quantum-secret.com>
| // Default step for log queries (60 seconds) | ||
| const stepMs = 60 * 1000; |
There was a problem hiding this comment.
Name convention?
STEP_MS
| function buildLabels(row: Record<string, unknown>, timestampCol: string, valueCol: string): Labels { | ||
| const labels: Labels = {}; | ||
| for (const [key, value] of Object.entries(row)) { | ||
| if (key !== timestampCol && key !== valueCol) { | ||
| labels[key] = value === null || value === undefined ? '' : String(value); | ||
| } | ||
| } | ||
| return labels; | ||
| } |
There was a problem hiding this comment.
Object.entries(row) creates a new intermediate array of [key, value] pairs for every single row. For a dashboard with 1,000+ points and multiple series, this adds unnecessary memory pressure and garbage collection. Iterate over keys directly to avoid creating extra arrays.
ClickHouse queries often return internal columns (like _part or _sample_factor) or extra metadata that you might not want as labels in your Perses dashboard.
The current code converts everything to a string. While safe, it can be slightly optimized by checking if the value is already a string before calling String(value)
function buildLabels(row: Record<string, unknown>, timestampCol: string, valueCol: string): Labels {
const labels: Labels = {};
const keys = Object.keys(row);
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key === timestampCol || key === valueCol || key.startsWith('_')) {
continue;
}
const val = row[key];
if (val === null || val === undefined) {
labels[key] = '';
} else {
labels[key] = typeof val === 'string' ? val : String(val);
}
}
return labels;
}| function labelsToKey(labels: Labels): string { | ||
| return Object.entries(labels) | ||
| .sort(([a], [b]) => a.localeCompare(b)) | ||
| .map(([k, v]) => `${k}=${v}`) | ||
| .join(','); | ||
| } |
There was a problem hiding this comment.
Each time this function runs, it creates multiple intermediate arrays (Object.entries, .sort, .map) and several temporary strings before producing the final key. If your ClickHouse query returns thousands of rows, this can lead to significant garbage collection (GC) pressure and slower dashboard rendering.
function labelsToKey(labels: Labels): string {
const keys = Object.keys(labels).sort();
let keyString = '';
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
const v = labels[k];
if (i > 0) keyString += ',';
keyString += k + '=' + v;
}
return keyString;
}| if (entries.length === 1) return entries[0]?.[1] ?? 'series'; | ||
| return entries.map(([k, v]) => `${k}="${v}"`).join(', '); |
There was a problem hiding this comment.
The function does not sort the labels. If ClickHouse returns columns in a different order for different rows, your legend items might look inconsistent.
The entries[0]?.[1] is a bit redundant since the code already checked that length === 1
Description
Extend Clickhouse time series plugin to give feature parity with Prometheus with label mapping and time range search
Screenshots
Checklist
[<catalog_entry>] <commit message>naming convention using one of thefollowing
catalog_entryvalues:FEATURE,ENHANCEMENT,BUGFIX,BREAKINGCHANGE,DOC,IGNORE.