Phase 3 of the production launch plan: task reliability improvements
to prevent DB lock issues at scale and handle transient wallet API
failures gracefully.
- 3.1 Batched point expiration: rewrite per-card Python loop to chunked
processing (LIMIT 500 FOR UPDATE SKIP LOCKED). Each chunk commits
independently, releasing row locks before processing the next batch.
Notifications sent after commit (outside lock window). Warning emails
also chunked with same pattern.
- 3.2 Wallet sync exponential backoff: replace time.sleep(2) single
retry with 4 attempts using [1s, 4s, 16s] backoff delays. Per-card
try/except ensures one failing card doesn't block the batch.
Failed card IDs logged for observability.
342 tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix rate limiter to extract real client IP and handle sync/async endpoints
- Rate-limit public enrollment (10/min) and program info (30/min) endpoints
- Add 409 Conflict to non-retryable status codes in retry decorator
- Cache private key in get_save_url() to avoid re-reading JSON per call
- Make update_class() return bool success status with error-level logging
- Move Google Wallet config from core to loyalty module config
- Document time.sleep() safety in retry decorator (threadpool execution)
- Add per-card retry (1 retry, 2s delay) to wallet sync task
- Add logo URL reachability check (HEAD request) to validate_config()
- Add 26 comprehensive unit tests for GoogleWalletService
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>