fix: improve ClickHouse admin page, fix AgentHealth type error
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m13s
CI / docker (push) Successful in 3m46s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 58s

Rewrite ClickHouse admin to show useful storage metrics instead of
often-empty system.events data. Add active queries section.

- Replace performance endpoint: query system.parts for disk size,
  uncompressed size, compression ratio, total rows, part count
- Add /queries endpoint querying system.processes for active queries
- Frontend: storage overview strip, tables with total size, active
  queries DataTable
- Fix AgentHealth.tsx type: agentId → instanceId in inline type cast

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-01 20:18:06 +02:00
parent 188810e54b
commit f82aa26371
7 changed files with 159 additions and 37 deletions

View File

@@ -1,6 +1,7 @@
package com.cameleer3.server.app.controller;
import com.cameleer3.server.app.dto.ClickHousePerformanceResponse;
import com.cameleer3.server.app.dto.ClickHouseQueryInfo;
import com.cameleer3.server.app.dto.ClickHouseStatusResponse;
import com.cameleer3.server.app.dto.ClickHouseTableInfo;
import com.cameleer3.server.app.dto.IndexerPipelineResponse;
@@ -80,20 +81,72 @@ public class ClickHouseAdminController {
}
@GetMapping("/performance")
@Operation(summary = "ClickHouse performance metrics")
@Operation(summary = "ClickHouse storage and performance metrics")
public ClickHousePerformanceResponse getPerformance() {
try {
long selectQueries = queryEvent("SelectQuery");
long insertQueries = queryEvent("InsertQuery");
long insertedRows = queryEvent("InsertedRows");
long readRows = queryEvent("SelectedRows");
String memoryUsage = clickHouseJdbc.queryForObject(
"SELECT formatReadableSize(value) FROM system.metrics WHERE metric = 'MemoryTracking'",
String.class);
return new ClickHousePerformanceResponse(selectQueries, insertQueries,
memoryUsage != null ? memoryUsage : "0 B", insertedRows, readRows);
var row = clickHouseJdbc.queryForMap("""
SELECT
formatReadableSize(sum(bytes_on_disk)) AS disk_size,
formatReadableSize(sum(data_uncompressed_bytes)) AS uncompressed_size,
if(sum(data_uncompressed_bytes) > 0,
round(sum(bytes_on_disk) / sum(data_uncompressed_bytes), 3), 0) AS compression_ratio,
sum(rows) AS total_rows,
count() AS part_count
FROM system.parts
WHERE database = currentDatabase() AND active
""");
String memory = "N/A";
try {
memory = clickHouseJdbc.queryForObject(
"SELECT formatReadableSize(value) FROM system.metrics WHERE metric = 'MemoryTracking'",
String.class);
} catch (Exception ignored) {}
int currentQueries = 0;
try {
Integer q = clickHouseJdbc.queryForObject(
"SELECT toInt32(value) FROM system.metrics WHERE metric = 'Query'",
Integer.class);
if (q != null) currentQueries = q;
} catch (Exception ignored) {}
return new ClickHousePerformanceResponse(
(String) row.get("disk_size"),
(String) row.get("uncompressed_size"),
((Number) row.get("compression_ratio")).doubleValue(),
((Number) row.get("total_rows")).longValue(),
((Number) row.get("part_count")).intValue(),
memory != null ? memory : "N/A",
currentQueries);
} catch (Exception e) {
return new ClickHousePerformanceResponse(0, 0, "N/A", 0, 0);
return new ClickHousePerformanceResponse("N/A", "N/A", 0, 0, 0, "N/A", 0);
}
}
@GetMapping("/queries")
@Operation(summary = "Active ClickHouse queries")
public List<ClickHouseQueryInfo> getQueries() {
try {
return clickHouseJdbc.query("""
SELECT
query_id,
round(elapsed, 2) AS elapsed_seconds,
formatReadableSize(memory_usage) AS memory,
read_rows,
substring(query, 1, 200) AS query
FROM system.processes
WHERE is_initial_query = 1
ORDER BY elapsed DESC
""",
(rs, rowNum) -> new ClickHouseQueryInfo(
rs.getString("query_id"),
rs.getDouble("elapsed_seconds"),
rs.getString("memory"),
rs.getLong("read_rows"),
rs.getString("query")));
} catch (Exception e) {
return List.of();
}
}
@@ -109,11 +162,4 @@ public class ClickHouseAdminController {
indexerStats.getIndexingRate(),
indexerStats.getLastIndexedAt());
}
private long queryEvent(String eventName) {
Long val = clickHouseJdbc.queryForObject(
"SELECT value FROM system.events WHERE event = ?",
Long.class, eventName);
return val != null ? val : 0;
}
}

View File

@@ -2,11 +2,13 @@ package com.cameleer3.server.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "ClickHouse performance metrics")
@Schema(description = "ClickHouse storage and performance metrics")
public record ClickHousePerformanceResponse(
long queryCount,
long insertQueryCount,
String diskSize,
String uncompressedSize,
double compressionRatio,
long totalRows,
int partCount,
String memoryUsage,
long insertedRows,
long readRows
int currentQueries
) {}

View File

@@ -0,0 +1,12 @@
package com.cameleer3.server.app.dto;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "Active ClickHouse query information")
public record ClickHouseQueryInfo(
String queryId,
double elapsedSeconds,
String memory,
long readRows,
String query
) {}