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}
|
username: ${CLICKHOUSE_USERNAME:default}
|
||||||
password: ${CLICKHOUSE_PASSWORD:}
|
password: ${CLICKHOUSE_PASSWORD:}
|
||||||
|
|
||||||
|
logging:
|
||||||
|
level:
|
||||||
|
com.clickhouse: INFO
|
||||||
|
org.apache.hc.client5: WARN
|
||||||
|
|
||||||
management:
|
management:
|
||||||
endpoints:
|
endpoints:
|
||||||
web:
|
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",
|
"name": "ui",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cameleer/design-system": "^0.1.26",
|
"@cameleer/design-system": "^0.1.28",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
"openapi-fetch": "^0.17.0",
|
"openapi-fetch": "^0.17.0",
|
||||||
@@ -278,9 +278,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cameleer/design-system": {
|
"node_modules/@cameleer/design-system": {
|
||||||
"version": "0.1.26",
|
"version": "0.1.28",
|
||||||
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.26/design-system-0.1.26.tgz",
|
"resolved": "https://gitea.siegeln.net/api/packages/cameleer/npm/%40cameleer%2Fdesign-system/-/0.1.28/design-system-0.1.28.tgz",
|
||||||
"integrity": "sha512-lu76c4F1Vz6fdXLjv434zh5jm61uSrkyNwmRbLtrs3tZbjovL8JWwj0Ao6akBJ84axzUt3GCbugVJwiLZvUKdA==",
|
"integrity": "sha512-pDzuiIW2zpkYeX9mR1ENxO2Xk+GTMNUQzxsC7C+p0veEnOC1XObHse9Q06GKJQ9+mff0eFCk9cuYCwW3kwB0rA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
"react": "^19.0.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"
|
"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": {
|
"dependencies": {
|
||||||
"@cameleer/design-system": "^0.1.26",
|
"@cameleer/design-system": "^0.1.28",
|
||||||
"@tanstack/react-query": "^5.90.21",
|
"@tanstack/react-query": "^5.90.21",
|
||||||
"lucide-react": "^1.7.0",
|
"lucide-react": "^1.7.0",
|
||||||
"openapi-fetch": "^0.17.0",
|
"openapi-fetch": "^0.17.0",
|
||||||
|
|||||||
@@ -108,10 +108,11 @@ export function useRbacStats() {
|
|||||||
|
|
||||||
// ── User Query Hooks ───────────────────────────────────────────────────
|
// ── User Query Hooks ───────────────────────────────────────────────────
|
||||||
|
|
||||||
export function useUsers() {
|
export function useUsers(enabled = true) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['admin', 'users'],
|
queryKey: ['admin', 'users'],
|
||||||
queryFn: () => adminFetch<UserDetail[]>('/users'),
|
queryFn: () => adminFetch<UserDetail[]>('/users'),
|
||||||
|
enabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,10 +126,11 @@ export function useUser(userId: string | null) {
|
|||||||
|
|
||||||
// ── Role Query Hooks ───────────────────────────────────────────────────
|
// ── Role Query Hooks ───────────────────────────────────────────────────
|
||||||
|
|
||||||
export function useRoles() {
|
export function useRoles(enabled = true) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['admin', 'roles'],
|
queryKey: ['admin', 'roles'],
|
||||||
queryFn: () => adminFetch<RoleDetail[]>('/roles'),
|
queryFn: () => adminFetch<RoleDetail[]>('/roles'),
|
||||||
|
enabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,10 +144,11 @@ export function useRole(roleId: string | null) {
|
|||||||
|
|
||||||
// ── Group Query Hooks ──────────────────────────────────────────────────
|
// ── Group Query Hooks ──────────────────────────────────────────────────
|
||||||
|
|
||||||
export function useGroups() {
|
export function useGroups(enabled = true) {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ['admin', 'groups'],
|
queryKey: ['admin', 'groups'],
|
||||||
queryFn: () => adminFetch<GroupDetail[]>('/groups'),
|
queryFn: () => adminFetch<GroupDetail[]>('/groups'),
|
||||||
|
enabled,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -272,15 +272,16 @@ function LayoutContent() {
|
|||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { timeRange, autoRefresh } = useGlobalFilters();
|
const { timeRange, autoRefresh, refreshTimeRange } = useGlobalFilters();
|
||||||
const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString());
|
const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString());
|
||||||
const { data: agents } = useAgents();
|
const { data: agents } = useAgents();
|
||||||
const { data: attributeKeys } = useAttributeKeys();
|
const { data: attributeKeys } = useAttributeKeys();
|
||||||
|
|
||||||
// --- Admin search data (only fetched on admin pages) ----------------
|
// --- Admin search data (only fetched on admin pages) ----------------
|
||||||
const { data: adminUsers } = useUsers();
|
const isAdminPage = location.pathname.startsWith('/admin');
|
||||||
const { data: adminGroups } = useGroups();
|
const { data: adminUsers } = useUsers(isAdminPage);
|
||||||
const { data: adminRoles } = useRoles();
|
const { data: adminGroups } = useGroups(isAdminPage);
|
||||||
|
const { data: adminRoles } = useRoles(isAdminPage);
|
||||||
|
|
||||||
const { username, logout } = useAuthStore();
|
const { username, logout } = useAuthStore();
|
||||||
const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
|
const { open: paletteOpen, setOpen: setPaletteOpen } = useCommandPalette();
|
||||||
@@ -302,7 +303,6 @@ function LayoutContent() {
|
|||||||
const [filterQuery, setFilterQuery] = useState('');
|
const [filterQuery, setFilterQuery] = useState('');
|
||||||
|
|
||||||
// --- Section open states ------------------------------------------
|
// --- Section open states ------------------------------------------
|
||||||
const isAdminPage = location.pathname.startsWith('/admin');
|
|
||||||
const [appsOpen, setAppsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_APPS, true));
|
const [appsOpen, setAppsOpen] = useState(() => isAdminPage ? false : readCollapsed(SK_APPS, true));
|
||||||
const [adminOpen, setAdminOpen] = useState(() => isAdminPage ? true : readCollapsed(SK_ADMIN, false));
|
const [adminOpen, setAdminOpen] = useState(() => isAdminPage ? true : readCollapsed(SK_ADMIN, false));
|
||||||
const [starredOpen, setStarredOpen] = useState(true);
|
const [starredOpen, setStarredOpen] = useState(true);
|
||||||
@@ -331,13 +331,16 @@ function LayoutContent() {
|
|||||||
}
|
}
|
||||||
if (appsOpen) {
|
if (appsOpen) {
|
||||||
// Already open — navigate to all applications
|
// Already open — navigate to all applications
|
||||||
if (!autoRefresh) queryClient.invalidateQueries();
|
if (!autoRefresh) {
|
||||||
|
refreshTimeRange();
|
||||||
|
queryClient.invalidateQueries();
|
||||||
|
}
|
||||||
navigate(`/${scope.tab}`);
|
navigate(`/${scope.tab}`);
|
||||||
} else {
|
} else {
|
||||||
setAppsOpen(true);
|
setAppsOpen(true);
|
||||||
writeCollapsed(SK_APPS, true);
|
writeCollapsed(SK_APPS, true);
|
||||||
}
|
}
|
||||||
}, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, queryClient]);
|
}, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);
|
||||||
|
|
||||||
const toggleAdmin = useCallback(() => {
|
const toggleAdmin = useCallback(() => {
|
||||||
if (!isAdminPage) {
|
if (!isAdminPage) {
|
||||||
@@ -562,6 +565,7 @@ function LayoutContent() {
|
|||||||
|
|
||||||
// When not auto-refreshing, treat navigation as a manual refresh
|
// When not auto-refreshing, treat navigation as a manual refresh
|
||||||
if (!autoRefresh) {
|
if (!autoRefresh) {
|
||||||
|
refreshTimeRange();
|
||||||
queryClient.invalidateQueries();
|
queryClient.invalidateQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -580,7 +584,7 @@ function LayoutContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
navigate(path, { state });
|
navigate(path, { state });
|
||||||
}, [navigate, scope.tab, autoRefresh, queryClient]);
|
}, [navigate, scope.tab, autoRefresh, refreshTimeRange, queryClient]);
|
||||||
|
|
||||||
// --- Render -------------------------------------------------------
|
// --- Render -------------------------------------------------------
|
||||||
const camelLogo = (
|
const camelLogo = (
|
||||||
|
|||||||
Reference in New Issue
Block a user