Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions archon-ui-main/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useState, useEffect } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { queryClient } from './features/shared/queryClient';
import { KnowledgeBasePage } from './pages/KnowledgeBasePage';
import { SettingsPage } from './pages/SettingsPage';
import { MCPPage } from './pages/MCPPage';
Expand All @@ -18,27 +19,6 @@ import { MigrationBanner } from './components/ui/MigrationBanner';
import { serverHealthService } from './services/serverHealthService';
import { useMigrationStatus } from './hooks/useMigrationStatus';

// Create a client with optimized settings for our polling use case
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// Keep data fresh for 2 seconds by default
staleTime: 2000,
// Cache data for 5 minutes
gcTime: 5 * 60 * 1000,
// Retry failed requests 3 times
retry: 3,
// Refetch on window focus
refetchOnWindowFocus: true,
// Don't refetch on reconnect by default (we handle this manually)
refetchOnReconnect: false,
},
mutations: {
// Retry mutations once on failure
retry: 1,
},
},
});

const AppRoutes = () => {
const { projectsEnabled } = useSettings();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { callAPIWithETag } from "../../../features/shared/apiWithEtag";
import { STALE_TIMES } from "../../../features/shared/queryPatterns";
import type { HealthResponse } from "../types";

/**
Expand Down Expand Up @@ -38,10 +39,10 @@ export function useBackendHealth() {
return Math.min(1500 * 1.5 ** attemptIndex, 10000);
},
// Refetch every 30 seconds when healthy
refetchInterval: 30000,
refetchInterval: STALE_TIMES.normal,
// Keep trying to connect on window focus
refetchOnWindowFocus: true,
// Consider data fresh for 20 seconds
staleTime: 20000,
// Consider data fresh for 30 seconds
staleTime: STALE_TIMES.normal,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import { useInfiniteQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import { STALE_TIMES } from "@/features/shared/queryPatterns";
import { knowledgeKeys } from "../../hooks/useKnowledgeQueries";
import { knowledgeService } from "../../services";
import type { ChunksResponse, CodeExample, CodeExamplesResponse, DocumentChunk } from "../../types";
Expand Down Expand Up @@ -56,7 +57,7 @@ export function useInspectorPagination({
return hasMore ? allPages.length : undefined;
},
enabled: !!sourceId,
staleTime: 60000,
staleTime: STALE_TIMES.normal,
initialPageParam: 0,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export function useTaskCounts() {
queryKey: taskKeys.counts(),
queryFn: () => taskService.getTaskCountsForAllProjects(),
refetchInterval: false, // Don't poll, only refetch manually
staleTime: STALE_TIMES.rare,
staleTime: STALE_TIMES.frequent,
});
Comment on lines +37 to 43

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix polling inversion: background ends up polling faster than foreground.

useSmartPolling(10_000) returns 5s when not focused, which is faster than the 10s active interval and contradicts the polling guideline. Use visibility to pause when hidden and keep 10s while visible.

-export function useTaskCounts() {
-  const { refetchInterval: countsRefetchInterval } = useSmartPolling(10_000); // 10s bg polling with smart pause
+export function useTaskCounts() {
+  // Pause when tab is hidden; poll every 10s when visible (foreground or background).
+  const { isVisible } = useSmartPolling();
   return useQuery<Awaited<ReturnType<typeof taskService.getTaskCountsForAllProjects>>>({
     queryKey: taskKeys.counts(),
     queryFn: () => taskService.getTaskCountsForAllProjects(),
-    refetchInterval: countsRefetchInterval,
+    refetchInterval: isVisible ? 10_000 : false,
     staleTime: STALE_TIMES.frequent,
   });
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { refetchInterval: countsRefetchInterval } = useSmartPolling(10_000); // 10s bg polling with smart pause
return useQuery<Awaited<ReturnType<typeof taskService.getTaskCountsForAllProjects>>>({
queryKey: taskKeys.counts(),
queryFn: () => taskService.getTaskCountsForAllProjects(),
refetchInterval: false, // Don't poll, only refetch manually
staleTime: STALE_TIMES.rare,
refetchInterval: countsRefetchInterval,
staleTime: STALE_TIMES.frequent,
});
export function useTaskCounts() {
// Pause when tab is hidden; poll every 10s when visible (foreground or background).
const { isVisible } = useSmartPolling();
return useQuery<Awaited<ReturnType<typeof taskService.getTaskCountsForAllProjects>>>({
queryKey: taskKeys.counts(),
queryFn: () => taskService.getTaskCountsForAllProjects(),
refetchInterval: isVisible ? 10_000 : false,
staleTime: STALE_TIMES.frequent,
});
}
🤖 Prompt for AI Agents
archon-ui-main/src/features/projects/tasks/hooks/useTaskQueries.ts around lines
37 to 43: the current call useSmartPolling(10_000) produces a shorter interval
when the page is hidden (background polling faster than foreground). Change the
useSmartPolling call so the foreground interval remains 10_000ms and polling is
paused (or slowed to effectively disabled) when the document is not visible;
specifically, pass the options/arguments that enforce a 10s interval when
visible and pause on hidden (e.g., useSmartPolling({ interval: 10_000,
pauseWhenHidden: true }) or the equivalent for our hook API) so background does
not poll faster than foreground.

}

Expand Down
79 changes: 79 additions & 0 deletions archon-ui-main/src/features/shared/queryClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { QueryClient } from "@tanstack/react-query";
import { STALE_TIMES } from "./queryPatterns";

/**
* Centralized QueryClient configuration for the entire application
*
* Benefits:
* - Single source of truth for cache configuration
* - Automatic request deduplication for same query keys
* - Smart retry logic that avoids retrying on client errors
* - Optimized garbage collection and structural sharing
*/
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
// Default stale time - most data is considered fresh for 30 seconds
staleTime: STALE_TIMES.normal,

// Keep unused data in cache for 10 minutes (was 5 minutes)
gcTime: 10 * 60 * 1000,

// Smart retry logic - don't retry on 4xx errors
retry: (failureCount, error: any) => {
// Don't retry on 404 (Not Found)
if (error?.status === 404) return false;
// Don't retry on 400 (Bad Request)
if (error?.status === 400) return false;
// Don't retry on 401 (Unauthorized)
if (error?.status === 401) return false;
// Don't retry on 403 (Forbidden)
if (error?.status === 403) return false;
// Retry up to 2 times for other errors (5xx, network, etc)
return failureCount < 2;
},

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
// Exponential backoff for retries
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),

// Disable aggressive refetching to reduce API calls
refetchOnWindowFocus: false,
refetchOnReconnect: false,
refetchOnMount: true,

// Network behavior
networkMode: "online",

// Enable structural sharing for efficient re-renders
structuralSharing: true,
},

mutations: {
// No retries for mutations - let user explicitly retry
retry: false,

// Network behavior
networkMode: "online",
},
},
});

/**
* Create a test QueryClient with optimized settings for tests
* Used by test-utils.tsx for consistent test behavior
*/
export function createTestQueryClient(): QueryClient {
return new QueryClient({
defaultOptions: {
queries: {
retry: false,
staleTime: 0, // Always fresh in tests
gcTime: 0, // No caching in tests
refetchOnWindowFocus: false,
},
mutations: {
retry: false,
},
},
});
}
10 changes: 10 additions & 0 deletions archon-ui-main/src/features/shared/queryPatterns.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,13 @@ export const STALE_TIMES = {
rare: 300_000, // 5 minutes - for rarely changing configuration
static: Infinity, // Never stale - for static data like settings
} as const;

// Re-export commonly used TanStack Query types for convenience
export type { QueryKey, QueryOptions } from "@tanstack/react-query";

// Helper to create a disabled query options object
export const DISABLED_QUERY_OPTIONS = {
enabled: false,
staleTime: Infinity,
gcTime: 0,
} as const;
10 changes: 3 additions & 7 deletions archon-ui-main/src/features/testing/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { QueryClientProvider } from "@tanstack/react-query";
import { render as rtlRender } from "@testing-library/react";
import type React from "react";
import { createTestQueryClient } from "../shared/queryClient";
import { ToastProvider } from "../ui/components/ToastProvider";
import { TooltipProvider } from "../ui/primitives/tooltip";

Expand All @@ -11,12 +12,7 @@ import { TooltipProvider } from "../ui/primitives/tooltip";
export function renderWithProviders(
ui: React.ReactElement,
{
queryClient = new QueryClient({
defaultOptions: {
queries: { retry: false },
mutations: { retry: false },
},
}),
queryClient = createTestQueryClient(),
...renderOptions
} = {},
) {
Expand Down