-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Summary
The SyncBackoffService is fully implemented with exponential backoff logic, but backoffItem() is never called anywhere in production code. When sync errors occur (429 rate limiting, content limit errors, or other server errors), items remain in the dirty queue and immediately retry on the next sync, creating an infinite loop.
Environment
- Standard Notes version: Latest (tested against commit ce1fbe4)
- Platform: All (Web, Desktop, Mobile)
User-Reported Symptoms
Users experiencing sync failures report:
- "Too many requests. Please try again later." (429 errors)
- "You have exceeded your content limit. Please upgrade your account." (even with paid Pro accounts)
- Sign out/sign in temporarily resolves the issue
- Emptying trash sometimes helps
- Sync eventually becomes completely non-functional
Steps to Reproduce
- Trigger a sync error (rate limit, content limit, etc.)
- Observe that:
- Error toast is shown
- Items remain in dirty queue
- Next sync immediately retries the same items
- Same error occurs
- Loop continues indefinitely
Root Cause Analysis
backoffItem() Never Called
The SyncBackoffService at packages/services/src/Domain/Sync/SyncBackoffService.ts is:
- ✅ Created and injected at
Dependencies.ts:1361 - ✅ Used to filter items in
SyncService.itemsNeedingSync()at line 459 - ❌
backoffItem()is never called in production code
# Verification:
grep -r "\.backoffItem\(" packages/ --include="*.ts" | grep -v ".spec.ts" | grep -v ".test.js"
# Result: Only the definition - no production callersOnly Two Status Codes Specifically Handled
In SyncService.ts, only these errors have special handling:
- 401 (Invalid Session) - triggers re-auth
- 429 (Too Many Requests) - shows toast
All other errors (400, 403, 500, content limit, etc.) have no backoff logic.
Sync Operation Continues After Errors
AccountSyncOperation.run() continues making HTTP requests even after receiving error responses, potentially exacerbating rate limits.
Expected Behavior
- Items that fail to sync should be backed off exponentially (1s → 2s → 4s → 8s...)
- The sync operation should stop making requests when rate limited
- Non-failing items should continue syncing normally
- Failed items retry only after backoff expires
Actual Behavior
- Failed items immediately retry on every sync attempt
- Operation continues making requests after errors
- Creates thundering herd effect
- Eventually breaks sync completely
Impact
- Severity: High
- User Impact: Sync becomes non-functional, requiring sign-out/sign-in workaround
- Data Risk: Changes don't sync across devices
Proposed Fix
Wire up the existing SyncBackoffService:
- Call
operation.abort()when rate limited to stop further requests - Call
backoffItem()for all items in failed sync operations - Consider extending backoff to other error types (403, 500, etc.)
Additional Context
The backoff service is well-tested (SyncBackoffService.spec.ts) - only the integration is missing.
Related Reports
- Forum #3699 - "Too many requests" errors
- Users report sign-out/sign-in temporarily fixes the issue (resets dirty state)