feat: remove OpenSearch, add ClickHouse admin page
Remove all OpenSearch code, dependencies, configuration, deployment manifests, and CI/CD references. Replace the OpenSearch admin page with a ClickHouse admin page showing cluster status, table sizes, performance metrics, and indexer pipeline stats. - Delete 11 OpenSearch Java files (config, search impl, admin controller, DTOs, tests) - Delete 3 OpenSearch frontend files (admin page, CSS, query hooks) - Delete deploy/opensearch.yaml K8s manifest - Remove opensearch Maven dependencies from pom.xml - Remove opensearch config from application.yml, Dockerfile, docker-compose - Remove opensearch from CI workflow (secrets, deploy, cleanup steps) - Simplify ThresholdConfig (remove OpenSearch thresholds, database-only) - Change default search backend from opensearch to clickhouse - Add ClickHouseAdminController with /status, /tables, /performance, /pipeline - Add ClickHouseAdminPage with StatCards, pipeline ProgressBar, tables DataTable - Update CLAUDE.md, HOWTO.md, and source comments Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
77
ui/src/api/queries/admin/clickhouse.ts
Normal file
77
ui/src/api/queries/admin/clickhouse.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { adminFetch } from './admin-api';
|
||||
import { useRefreshInterval } from '../use-refresh-interval';
|
||||
|
||||
// ── Types ──────────────────────────────────────────────────────────────
|
||||
|
||||
export interface ClickHouseStatus {
|
||||
reachable: boolean;
|
||||
version: string | null;
|
||||
uptime: string | null;
|
||||
host: string | null;
|
||||
}
|
||||
|
||||
export interface ClickHouseTableInfo {
|
||||
name: string;
|
||||
engine: string;
|
||||
rowCount: number;
|
||||
dataSize: string;
|
||||
dataSizeBytes: number;
|
||||
partitionCount: number;
|
||||
}
|
||||
|
||||
export interface ClickHousePerformance {
|
||||
queryCount: number;
|
||||
insertQueryCount: number;
|
||||
memoryUsage: string;
|
||||
insertedRows: number;
|
||||
readRows: number;
|
||||
}
|
||||
|
||||
export interface IndexerPipeline {
|
||||
queueDepth: number;
|
||||
maxQueueSize: number;
|
||||
failedCount: number;
|
||||
indexedCount: number;
|
||||
debounceMs: number;
|
||||
indexingRate: number;
|
||||
lastIndexedAt: string | null;
|
||||
}
|
||||
|
||||
// ── Query Hooks ────────────────────────────────────────────────────────
|
||||
|
||||
export function useClickHouseStatus() {
|
||||
const refetchInterval = useRefreshInterval(30_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'clickhouse', 'status'],
|
||||
queryFn: () => adminFetch<ClickHouseStatus>('/clickhouse/status'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function useClickHouseTables() {
|
||||
const refetchInterval = useRefreshInterval(60_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'clickhouse', 'tables'],
|
||||
queryFn: () => adminFetch<ClickHouseTableInfo[]>('/clickhouse/tables'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function useClickHousePerformance() {
|
||||
const refetchInterval = useRefreshInterval(30_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'clickhouse', 'performance'],
|
||||
queryFn: () => adminFetch<ClickHousePerformance>('/clickhouse/performance'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function useIndexerPipeline() {
|
||||
const refetchInterval = useRefreshInterval(10_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'clickhouse', 'pipeline'],
|
||||
queryFn: () => adminFetch<IndexerPipeline>('/clickhouse/pipeline'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { adminFetch } from './admin-api';
|
||||
import { useRefreshInterval } from '../use-refresh-interval';
|
||||
|
||||
// ── Types ──────────────────────────────────────────────────────────────
|
||||
|
||||
export interface OpenSearchStatus {
|
||||
reachable: boolean;
|
||||
clusterHealth: string;
|
||||
version: string | null;
|
||||
nodeCount: number;
|
||||
host: string;
|
||||
}
|
||||
|
||||
export interface PipelineStats {
|
||||
queueDepth: number;
|
||||
maxQueueSize: number;
|
||||
failedCount: number;
|
||||
indexedCount: number;
|
||||
debounceMs: number;
|
||||
indexingRate: number;
|
||||
lastIndexedAt: string | null;
|
||||
}
|
||||
|
||||
export interface IndexInfo {
|
||||
name: string;
|
||||
docCount: number;
|
||||
size: string;
|
||||
sizeBytes: number;
|
||||
health: string;
|
||||
primaryShards: number;
|
||||
replicas: number;
|
||||
}
|
||||
|
||||
export interface IndicesPage {
|
||||
indices: IndexInfo[];
|
||||
totalIndices: number;
|
||||
totalDocs: number;
|
||||
totalSize: string;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface PerformanceStats {
|
||||
queryCacheHitRate: number;
|
||||
requestCacheHitRate: number;
|
||||
searchLatencyMs: number;
|
||||
indexingLatencyMs: number;
|
||||
heapUsedBytes: number;
|
||||
heapMaxBytes: number;
|
||||
}
|
||||
|
||||
// ── Query Hooks ────────────────────────────────────────────────────────
|
||||
|
||||
export function useOpenSearchStatus() {
|
||||
const refetchInterval = useRefreshInterval(30_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'opensearch', 'status'],
|
||||
queryFn: () => adminFetch<OpenSearchStatus>('/opensearch/status'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function usePipelineStats() {
|
||||
const refetchInterval = useRefreshInterval(10_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'opensearch', 'pipeline'],
|
||||
queryFn: () => adminFetch<PipelineStats>('/opensearch/pipeline'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
export function useOpenSearchIndices(page = 0, size = 20, search = '', prefix = 'executions') {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'opensearch', 'indices', prefix, page, size, search],
|
||||
queryFn: () => {
|
||||
const params = new URLSearchParams();
|
||||
params.set('page', String(page));
|
||||
params.set('size', String(size));
|
||||
params.set('prefix', prefix);
|
||||
if (search) params.set('search', search);
|
||||
return adminFetch<IndicesPage>(`/opensearch/indices?${params}`);
|
||||
},
|
||||
placeholderData: (prev) => prev,
|
||||
});
|
||||
}
|
||||
|
||||
export function useOpenSearchPerformance() {
|
||||
const refetchInterval = useRefreshInterval(30_000);
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'opensearch', 'performance'],
|
||||
queryFn: () => adminFetch<PerformanceStats>('/opensearch/performance'),
|
||||
refetchInterval,
|
||||
});
|
||||
}
|
||||
|
||||
// ── Mutation Hooks ─────────────────────────────────────────────────────
|
||||
|
||||
export function useDeleteIndex() {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (indexName: string) =>
|
||||
adminFetch<void>(`/opensearch/indices/${indexName}`, { method: 'DELETE' }),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin', 'opensearch', 'indices'] });
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -10,20 +10,8 @@ export interface DatabaseThresholds {
|
||||
queryDurationCritical: number;
|
||||
}
|
||||
|
||||
export interface OpenSearchThresholds {
|
||||
clusterHealthWarning: string;
|
||||
clusterHealthCritical: string;
|
||||
queueDepthWarning: number;
|
||||
queueDepthCritical: number;
|
||||
jvmHeapWarning: number;
|
||||
jvmHeapCritical: number;
|
||||
failedDocsWarning: number;
|
||||
failedDocsCritical: number;
|
||||
}
|
||||
|
||||
export interface ThresholdConfig {
|
||||
database: DatabaseThresholds;
|
||||
opensearch: OpenSearchThresholds;
|
||||
}
|
||||
|
||||
// ── Query Hooks ────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user