fix: reduce ClickHouse log noise, admin query spam, and diagram scan perf
- Set com.clickhouse log level to INFO and org.apache.hc.client5 to WARN - Admin hooks (useUsers/useGroups/useRoles) now only fetch on admin pages, eliminating AUDIT view_users entries on every UI click - Add ClickHouse projection on route_diagrams for (tenant_id, route_id, instance_id, created_at) to avoid full table scans on diagram lookups - Bump @cameleer/design-system to v0.1.28 (PAUSED mode time range fix, refreshTimeRange API) - Call refreshTimeRange before invalidateQueries in PAUSED mode manual refresh so sidebar clicks use current time window Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,11 @@ clickhouse:
|
||||
username: ${CLICKHOUSE_USERNAME:default}
|
||||
password: ${CLICKHOUSE_PASSWORD:}
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.clickhouse: INFO
|
||||
org.apache.hc.client5: WARN
|
||||
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- Projection for fast route_id + instance_id lookups on route_diagrams.
|
||||
-- The primary key is (tenant_id, content_hash) which serves hash-based lookups.
|
||||
-- Queries filtering by route_id + instance_id were scanning millions of rows.
|
||||
ALTER TABLE route_diagrams
|
||||
ADD PROJECTION IF NOT EXISTS prj_route_instance
|
||||
(SELECT content_hash, created_at ORDER BY tenant_id, route_id, instance_id, created_at);
|
||||
|
||||
ALTER TABLE route_diagrams MATERIALIZE PROJECTION IF NOT EXISTS prj_route_instance
|
||||
8
ui/package-lock.json
generated
8
ui/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "ui",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@cameleer/design-system": "^0.1.26",
|
||||
"@cameleer/design-system": "^0.1.28",
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"lucide-react": "^1.7.0",
|
||||
"openapi-fetch": "^0.17.0",
|
||||
@@ -278,9 +278,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@cameleer/design-system": {
|
||||
"version": "0.1.26",
|
||||
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.26/design-system-0.1.26.tgz",
|
||||
"integrity": "sha512-lu76c4F1Vz6fdXLjv434zh5jm61uSrkyNwmRbLtrs3tZbjovL8JWwj0Ao6akBJ84axzUt3GCbugVJwiLZvUKdA==",
|
||||
"version": "0.1.28",
|
||||
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.28/design-system-0.1.28.tgz",
|
||||
"integrity": "sha512-pDzuiIW2zpkYeX9mR1ENxO2Xk+GTMNUQzxsC7C+p0veEnOC1XObHse9Q06GKJQ9+mff0eFCk9cuYCwW3kwB0rA==",
|
||||
"dependencies": {
|
||||
"lucide-react": "^1.7.0",
|
||||
"react": "^19.0.0",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"generate-api:live": "curl -s http://localhost:8081/api/v1/api-docs -o src/api/openapi.json && openapi-typescript src/api/openapi.json -o src/api/schema.d.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cameleer/design-system": "^0.1.26",
|
||||
"@cameleer/design-system": "^0.1.28",
|
||||
"@tanstack/react-query": "^5.90.21",
|
||||
"lucide-react": "^1.7.0",
|
||||
"openapi-fetch": "^0.17.0",
|
||||
|
||||
@@ -108,10 +108,11 @@ export function useRbacStats() {
|
||||
|
||||
// ── User Query Hooks ───────────────────────────────────────────────────
|
||||
|
||||
export function useUsers() {
|
||||
export function useUsers(enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'users'],
|
||||
queryFn: () => adminFetch<UserDetail[]>('/users'),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -125,10 +126,11 @@ export function useUser(userId: string | null) {
|
||||
|
||||
// ── Role Query Hooks ───────────────────────────────────────────────────
|
||||
|
||||
export function useRoles() {
|
||||
export function useRoles(enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'roles'],
|
||||
queryFn: () => adminFetch<RoleDetail[]>('/roles'),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -142,10 +144,11 @@ export function useRole(roleId: string | null) {
|
||||
|
||||
// ── Group Query Hooks ──────────────────────────────────────────────────
|
||||
|
||||
export function useGroups() {
|
||||
export function useGroups(enabled = true) {
|
||||
return useQuery({
|
||||
queryKey: ['admin', 'groups'],
|
||||
queryFn: () => adminFetch<GroupDetail[]>('/groups'),
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -272,15 +272,16 @@ function LayoutContent() {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const queryClient = useQueryClient();
|
||||
const { timeRange, autoRefresh } = useGlobalFilters();
|
||||
const { timeRange, autoRefresh, refreshTimeRange } = useGlobalFilters();
|
||||
const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString());
|
||||
const { data: agents } = useAgents();
|
||||
const { data: attributeKeys } = useAttributeKeys();
|
||||
|
||||
// --- Admin search data (only fetched on admin pages) ----------------
|
||||
const { data: adminUsers } = useUsers();
|
||||
const { data: adminGroups } = useGroups();
|
||||
const { data: adminRoles } = useRoles();
|
||||
const isAdminPage = location.pathname.startsWith('/admin');
|
||||
const { data: adminUsers } = useUsers(isAdminPage);
|
||||
const { data: adminGroups } = useGroups(isAdminPage);
|
||||
const { data: adminRoles } = useRoles(isAdminPage);
|
||||
|
||||
const { username, logout } = useAuthStore();
|
||||
const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
|
||||
@@ -302,7 +303,6 @@ function LayoutContent() {
|
||||
const [filterQuery, setFilterQuery] = useState('');
|
||||
|
||||
// --- Section open states ------------------------------------------
|
||||
const isAdminPage = location.pathname.startsWith('/admin');
|
||||
const [appsOpen, setAppsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_APPS, true));
|
||||
const [adminOpen, setAdminOpen] = useState(() => isAdminPage ? true : readCollapsed(SK_ADMIN, false));
|
||||
const [starredOpen, setStarredOpen] = useState(true);
|
||||
@@ -331,13 +331,16 @@ function LayoutContent() {
|
||||
}
|
||||
if (appsOpen) {
|
||||
// Already open — navigate to all applications
|
||||
if (!autoRefresh) queryClient.invalidateQueries();
|
||||
if (!autoRefresh) {
|
||||
refreshTimeRange();
|
||||
queryClient.invalidateQueries();
|
||||
}
|
||||
navigate(`/${scope.tab}`);
|
||||
} else {
|
||||
setAppsOpen(true);
|
||||
writeCollapsed(SK_APPS, true);
|
||||
}
|
||||
}, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, queryClient]);
|
||||
}, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);
|
||||
|
||||
const toggleAdmin = useCallback(() => {
|
||||
if (!isAdminPage) {
|
||||
@@ -562,6 +565,7 @@ function LayoutContent() {
|
||||
|
||||
// When not auto-refreshing, treat navigation as a manual refresh
|
||||
if (!autoRefresh) {
|
||||
refreshTimeRange();
|
||||
queryClient.invalidateQueries();
|
||||
}
|
||||
|
||||
@@ -580,7 +584,7 @@ function LayoutContent() {
|
||||
}
|
||||
|
||||
navigate(path, { state });
|
||||
}, [navigate, scope.tab, autoRefresh, queryClient]);
|
||||
}, [navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);
|
||||
|
||||
// --- Render -------------------------------------------------------
|
||||
const camelLogo = (
|
||||
|
||||
Reference in New Issue
Block a user