Skip to content

[ENHANCEMENT] Clickhouse: support label and time access in table view#571

Open
fraser-qs wants to merge 1 commit intoperses:mainfrom
fraser-qs:clickhouse-labels
Open

[ENHANCEMENT] Clickhouse: support label and time access in table view#571
fraser-qs wants to merge 1 commit intoperses:mainfrom
fraser-qs:clickhouse-labels

Conversation

@fraser-qs
Copy link
Contributor

@fraser-qs fraser-qs commented Feb 12, 2026

Description

Extend Clickhouse time series plugin to give feature parity with Prometheus with label mapping and time range search

Screenshots

image

Checklist

  • Pull request has a descriptive title and context useful to a reviewer.
  • Pull request title follows the [<catalog_entry>] <commit message> naming convention using one of the
    following catalog_entry values: FEATURE, ENHANCEMENT, BUGFIX, BREAKINGCHANGE, DOC,IGNORE.
  • All commits have DCO signoffs.

Signed-off-by: fraser-qs <fraser@quantum-secret.com>
@fraser-qs fraser-qs requested a review from a team as a code owner February 12, 2026 22:06
@fraser-qs fraser-qs requested review from shahrokni and removed request for a team February 12, 2026 22:06
Comment on lines +82 to +83
// Default step for log queries (60 seconds)
const stepMs = 60 * 1000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Name convention?
STEP_MS

Comment on lines +37 to +45
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;
}
Copy link
Contributor

@shahrokni shahrokni Feb 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
}

Comment on lines +48 to +53
function labelsToKey(labels: Labels): string {
return Object.entries(labels)
.sort(([a], [b]) => a.localeCompare(b))
.map(([k, v]) => `${k}=${v}`)
.join(',');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
}

Comment on lines +59 to +60
if (entries.length === 1) return entries[0]?.[1] ?? 'series';
return entries.map(([k, v]) => `${k}="${v}"`).join(', ');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments