feat: Add circuit breaker for file sync failures#364
Conversation
Implements a circuit breaker pattern to prevent infinite retry loops when files consistently fail to sync. After 3 consecutive failures, files are skipped until they're modified. Changes: - Add circuit breaker in sync_service.py with MAX_CONSECUTIVE_FAILURES = 3 - Track file failures with checksums to detect when files change - Add SkippedFile dataclass to report skipped files with failure details - Update SyncReportResponse schema to include skipped_files field - Display skipped files in CLI status command (summary + verbose modes) - Add comprehensive tests for all circuit breaker scenarios The circuit breaker: - Tracks failures per file with SHA-256 checksums - Resets automatically when file content changes - Clears failure tracking on successful sync - Prevents blocking sync operations on problematic files - Logs warnings for skipped files Fixes #189 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
|
Claude finished @phernandez's task —— View job Code Review Complete ✓I've completed a comprehensive review of PR #364 against Basic Memory's team standards. Overall, this is a well-implemented circuit breaker pattern with excellent test coverage and thoughtful design decisions. ✅ StrengthsCode Quality & Architecture
Testing Excellence
Documentation & UX
🔍 Code Quality ReviewLine Length ✓All lines respect the 100-character limit Coding Conventions ✓
SQLAlchemy & FastAPI ✓
Async Patterns ✓
🎯 Review ChecklistCode Quality & Standards
Testing & Documentation
Basic Memory Architecture
Security & Performance
💡 Suggestions for Consideration1. Circuit Breaker Threshold ConfigurationLocation: The # In BasicMemoryConfig
sync_max_consecutive_failures: int = 3
# In SyncService.__init__
self._max_failures = app_config.sync_max_consecutive_failuresRationale: Power users may want to adjust this threshold based on their workflow or for debugging. 2. Skipped Files PersistenceCurrently, circuit breaker state is in-memory only. If the sync process restarts, failure tracking is lost. This is probably fine for most use cases, but consider documenting this behavior or exploring persistent storage for high-reliability scenarios. 3. Minor: Import OrderLocation: The import of 4. Test Marker for Circuit Breaker TestsConsider adding a custom pytest marker pytest -m circuit_breaker🎨 Design Decision Validation✅ Checksum-based ResetExcellent choice. Using SHA-256 checksums to detect file changes is:
✅ 3-Failure ThresholdGood balance between:
✅ Leveraging Existing InfrastructureSmart to use ✅ CLI Status IntegrationPerfect placement. Users naturally check 🔧 Code-Specific ObservationsExcellent Error Handling Pattern
except Exception as e:
error_msg = str(e)
logger.error(f"Failed to sync file: path={path}, error={error_msg}")
await self._record_failure(path, error_msg)
return None, NoneThis gracefully handles all exceptions and records them for circuit breaker tracking. Clean Separation of Concerns
Each method has a single, clear responsibility. Robust Checksum Failure Handling
try:
checksum = await self._compute_checksum_async(path)
except Exception:
checksum = "" # Better than crashingGraceful degradation prevents circuit breaker from blocking on checksum errors. 📊 Test Coverage AnalysisThe PR adds 249 lines of test code for ~200 lines of production code (>100% coverage ratio). Test scenarios cover:
🚦 Final AssessmentOverall Quality: ⭐⭐⭐⭐⭐ ExcellentThis PR demonstrates:
Recommendation: ✅ APPROVEThis implementation is production-ready and follows all Basic Memory standards. The circuit breaker pattern is a valuable addition that improves reliability without compromising the local-first architecture. Note on Test Execution: I was unable to run | |
Summary
Implements a circuit breaker pattern to prevent infinite retry loops when files consistently fail to sync. After 3 consecutive failures, files are automatically skipped until they're modified.
Changes
Core Implementation
MAX_CONSECUTIVE_FAILURES = 3thresholdFileFailureInfodataclass tracks failure count, timestamp, error, and checksumSchema Updates
path: File path relative to project rootreason: Error message from last failurefailure_count: Number of consecutive failuresfirst_failed: Timestamp of first failureskipped_filesfieldCLI Enhancement
Testing
test_circuit_breaker_skips_after_three_failurestest_circuit_breaker_resets_on_file_changetest_circuit_breaker_clears_on_successtest_circuit_breaker_tracks_multiple_filestest_circuit_breaker_handles_checksum_computation_failuretextwrap.dedent()for readable test contentDesign Decisions
SyncReport.skipped_filesinstead of creating new status mechanismsTesting
Example Output
Non-verbose mode:
Verbose mode:
Closes #189
🤖 Generated with Claude Code