fix: add 8s timeout to NeoInstituteContext school node fetch

Races the SchoolNeoDBService.getSchoolNode() call against an 8-second
timeout. If Neo4j is slow or unavailable the workspace now loads within
seconds rather than waiting for the full axios 120s timeout. The context
degrades gracefully — workspace opens without institute data, error is
logged as a warning not an exception.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kcar 2026-05-21 17:28:57 +00:00
parent d3c2a9bdff
commit ab1f8111f6

View File

@ -5,6 +5,8 @@ import { SchoolNeoDBService } from '../services/graph/schoolNeoDBService';
import { CCSchoolNodeProps } from '../utils/tldraw/cc-base/cc-graph/cc-graph-types'; import { CCSchoolNodeProps } from '../utils/tldraw/cc-base/cc-graph/cc-graph-types';
import { logger } from '../debugConfig'; import { logger } from '../debugConfig';
const NEO_INSTITUTE_TIMEOUT_MS = 8000;
export interface NeoInstituteContextType { export interface NeoInstituteContextType {
schoolNode: CCSchoolNodeProps | null; schoolNode: CCSchoolNodeProps | null;
isLoading: boolean; isLoading: boolean;
@ -22,7 +24,7 @@ const NeoInstituteContext = createContext<NeoInstituteContextType>({
export const NeoInstituteProvider: React.FC<{ children: ReactNode }> = ({ children }) => { export const NeoInstituteProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const { user } = useAuth(); const { user } = useAuth();
const { profile, isInitialized: isUserInitialized } = useUser(); const { profile, isInitialized: isUserInitialized } = useUser();
const [schoolNode, setSchoolNode] = useState<CCSchoolNodeProps | null>(null); const [schoolNode, setSchoolNode] = useState<CCSchoolNodeProps | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [isInitialized, setIsInitialized] = useState(false); const [isInitialized, setIsInitialized] = useState(false);
@ -35,7 +37,7 @@ export const NeoInstituteProvider: React.FC<{ children: ReactNode }> = ({ childr
hasUser: !!user, hasUser: !!user,
isInitialized isInitialized
}); });
// Wait for user profile to be ready // Wait for user profile to be ready
if (!isUserInitialized) { if (!isUserInitialized) {
logger.debug('neo-institute-context', '⏳ Waiting for user initialization...'); logger.debug('neo-institute-context', '⏳ Waiting for user initialization...');
@ -58,7 +60,17 @@ export const NeoInstituteProvider: React.FC<{ children: ReactNode }> = ({ childr
userEmail: user?.email userEmail: user?.email
}); });
const node = await SchoolNeoDBService.getSchoolNode(profile.school_db_name); // Race the Neo4j call against a timeout so a slow/unavailable Neo4j
// never blocks the workspace from loading for more than 8 seconds.
const timeoutPromise = new Promise<null>((_, reject) =>
setTimeout(() => reject(new Error(`Neo4j timed out after ${NEO_INSTITUTE_TIMEOUT_MS}ms`)), NEO_INSTITUTE_TIMEOUT_MS)
);
const node = await Promise.race([
SchoolNeoDBService.getSchoolNode(profile.school_db_name),
timeoutPromise
]);
if (node) { if (node) {
setSchoolNode(node); setSchoolNode(node);
logger.debug('neo-institute-context', '✅ School node loaded', { logger.debug('neo-institute-context', '✅ School node loaded', {
@ -71,7 +83,7 @@ export const NeoInstituteProvider: React.FC<{ children: ReactNode }> = ({ childr
} catch (error) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Failed to load school node'; const errorMessage = error instanceof Error ? error.message : 'Failed to load school node';
logger.error('neo-institute-context', '❌ Failed to load school node', { logger.warn('neo-institute-context', '⚠️ School node unavailable — workspace will load without institute data', {
error: errorMessage, error: errorMessage,
schoolDbName: profile.school_db_name schoolDbName: profile.school_db_name
}); });
@ -79,7 +91,7 @@ export const NeoInstituteProvider: React.FC<{ children: ReactNode }> = ({ childr
} finally { } finally {
setIsLoading(false); setIsLoading(false);
setIsInitialized(true); setIsInitialized(true);
logger.debug('neo-institute-context', '✅ Institute context initialization complete'); logger.debug('neo-institute-context', '✅ Institute context initialization complete');
} }
}; };