-
Notifications
You must be signed in to change notification settings - Fork 41
fix(frontend): prevent AG-UI event stream connection spam #513
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add exponential backoff for SSE reconnection (1s → 30s max) - Close EventSource immediately on error to prevent browser's native reconnect - Fix useEffect dependency array in page.tsx (removed aguiStream object) - Reset reconnect attempts on successful connection - Skip reconnection if component is unmounted Fixes issue where frontend would spam 35k+ requests when backend was unavailable, causing health check failures and pod restarts.
Claude Code ReviewSummaryThis PR successfully fixes a critical production issue where the frontend was spamming 35,000+ requests to the Issues by Severity🚫 Blocker IssuesNone - This PR is production-ready and fixes a critical bug. 🔴 Critical IssuesNone 🟡 Major Issues1. Missing Test Coverage
2. No Observability/Metrics
🔵 Minor Issues1. Magic Numbers Could Be Constants
const MAX_RECONNECT_DELAY = 30000 // 30 seconds max
const BASE_RECONNECT_DELAY = 1000 // 1 second base
2. TypeScript
Positive Highlights✅ Excellent Problem Diagnosis
✅ Correct Fix for Dependency Array Bug
✅ Proper EventSource Management
✅ Exponential Backoff Implementation
✅ Follows Frontend Standards
✅ Security Considerations
RecommendationsPriority 1: Add Tests (Before Next Release)Create test file describe("useAGUIStream reconnection", () => {
it("should implement exponential backoff", () => {
// Test: 1s → 2s → 4s → 8s → 16s → 30s max
})
it("should reset attempts on successful connection", () => {
// Test: reconnectAttemptsRef resets to 0
})
it("should not reconnect if unmounted", () => {
// Test: mountedRef prevents reconnection
})
it("should close EventSource on error", () => {
// Test: eventSource.close() called immediately
})
})Priority 2: Add Monitoring (Follow-up PR)Consider adding observability for production: // Track reconnection metrics
const logReconnectionAttempt = (attempt: number, delay: number) => {
// Could integrate with Langfuse or custom metrics
console.log(`[useAGUIStream] Reconnecting in ${delay}ms (attempt ${attempt})`)
// Optional: Send to observability service
if (attempt > 5) {
// Alert: Excessive reconnections
}
}Priority 3: Document in ADR (Optional)Consider documenting this fix in an ADR:
Code Quality Validation✅ Pre-Commit Checklist (Frontend)
✅ Error Handling Pattern Compliance
✅ Security Standards
Final VerdictStatus: ✅ APPROVED FOR MERGE This PR successfully fixes a critical production issue with a well-implemented solution. The exponential backoff and dependency array fix are both correct and follow established patterns. The only gaps are test coverage and observability, which can be addressed in follow-up work. Impact: This fix will prevent backend pod restarts, reduce unnecessary network traffic, and improve overall system stability. Risk: Low - Changes are isolated to SSE reconnection logic, with proper safeguards for unmount and cleanup. Reviewed by Claude Code following standards from:
🔍 View AI decision process (logs available for 90 days) 📋 View memory system files loaded (click to expand)What Amber Loaded for Code ReviewAmber automatically loaded these repository standards from the memory system:
Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines. |
Problem
The frontend was spamming 35,000+ requests to the
/agui/eventsSSE endpoint when the backend was unavailable or restarting. This caused:Root Cause
Two issues combined to create the spam:
Browser's native EventSource reconnect - The EventSource wasn't being closed on error, so the browser's built-in reconnect fired alongside our custom reconnect logic
Dependency array bug - The
useEffectinpage.tsxhadaguiStreamin its dependency array. SinceaguiStreamis an object that changes on every render, this caused infinite disconnect/reconnect cyclesFix
use-agui-stream.tspage.tsxaguiStreamfrom useEffect dependency arrayTesting