|
| 1 | +# Database-First Caching Architecture Design |
| 2 | + |
| 3 | +**Issue:** #469 |
| 4 | +**Status:** Design Phase |
| 5 | +**Author:** Claude Code |
| 6 | +**Date:** 2025-12-04 |
| 7 | + |
| 8 | +## Executive Summary |
| 9 | + |
| 10 | +This document outlines the design for implementing a database-first caching architecture where all data flows through the database before being displayed to users, ensuring consistency between stored and displayed data. |
| 11 | + |
| 12 | +## Current State Analysis |
| 13 | + |
| 14 | +### Data Flow Patterns Identified |
| 15 | + |
| 16 | +| Path | Data Type | Current Pattern | Cache Type | Issues | |
| 17 | +|------|-----------|-----------------|------------|--------| |
| 18 | +| Market Data | OHLCV bars | fetch -> cache -> display | SQLite | Cache key mismatch (fixed) | |
| 19 | +| Account Status | Equity, buying power | fetch -> memory cache -> display | In-memory 60s | No persistence | |
| 20 | +| Positions | Holdings, P&L | fetch -> memory cache -> display | In-memory 60s | No audit trail | |
| 21 | +| Orders | Open/filled orders | fetch -> display (merge local) | None + JSON | Dual source of truth | |
| 22 | +| Analysis | MACD/RSI signals | calculate -> database -> display | SQLite | **Only DB-first path** | |
| 23 | + |
| 24 | +### Current vs Proposed Architecture |
| 25 | + |
| 26 | +```mermaid |
| 27 | +flowchart TB |
| 28 | + subgraph Current[Current Architecture] |
| 29 | + A1[Alpaca API] --> B1[In-Memory Cache] |
| 30 | + A1 --> C1[Display to User] |
| 31 | + B1 -.-> D1[Maybe Store] |
| 32 | + end |
| 33 | +
|
| 34 | + subgraph Proposed[Proposed Architecture Database-First] |
| 35 | + A2[Alpaca API] --> B2[UnifiedBrokerCache] |
| 36 | + B2 --> C2[(SQLite Database)] |
| 37 | + C2 --> D2[Display to User] |
| 38 | + end |
| 39 | +``` |
| 40 | + |
| 41 | +### Detailed Data Flow Sequence |
| 42 | + |
| 43 | +```mermaid |
| 44 | +sequenceDiagram |
| 45 | + participant User |
| 46 | + participant CLI |
| 47 | + participant Cache as UnifiedBrokerCache |
| 48 | + participant DB as SQLite |
| 49 | + participant API as Alpaca API |
| 50 | +
|
| 51 | + User->>CLI: Request portfolio |
| 52 | + CLI->>Cache: get_positions(account_id) |
| 53 | + Cache->>DB: Check cache freshness |
| 54 | + alt Cache Fresh |
| 55 | + DB-->>Cache: Return cached data |
| 56 | + Cache-->>CLI: Return positions |
| 57 | + else Cache Stale |
| 58 | + Cache->>API: Fetch fresh data |
| 59 | + API-->>Cache: Return positions |
| 60 | + Cache->>DB: Store to database |
| 61 | + DB-->>Cache: Confirm stored |
| 62 | + Cache-->>CLI: Return positions |
| 63 | + end |
| 64 | + CLI->>Cache: audit_display() |
| 65 | + Cache->>DB: Log audit entry |
| 66 | + CLI-->>User: Display portfolio |
| 67 | +``` |
| 68 | + |
| 69 | +### Cache State Machine |
| 70 | + |
| 71 | +```mermaid |
| 72 | +stateDiagram-v2 |
| 73 | + [*] --> Empty |
| 74 | + Empty --> Fresh: API Fetch + Store |
| 75 | + Fresh --> Stale: TTL Expired |
| 76 | + Stale --> Fresh: Refresh |
| 77 | + Fresh --> Fresh: Cache Hit |
| 78 | + Stale --> Stale: API Fail Serve Stale |
| 79 | +``` |
| 80 | + |
| 81 | +### Database Schema - Entity Relationship |
| 82 | + |
| 83 | +```mermaid |
| 84 | +erDiagram |
| 85 | + broker_state_cache { |
| 86 | + int id PK |
| 87 | + string account_id |
| 88 | + string state_type |
| 89 | + json data_json |
| 90 | + } |
| 91 | +
|
| 92 | + position_snapshots { |
| 93 | + int id PK |
| 94 | + string account_id FK |
| 95 | + string symbol |
| 96 | + float qty |
| 97 | + float unrealized_pnl |
| 98 | + } |
| 99 | +
|
| 100 | + order_snapshots { |
| 101 | + int id PK |
| 102 | + string account_id FK |
| 103 | + string order_id |
| 104 | + string symbol |
| 105 | + string status |
| 106 | + } |
| 107 | +
|
| 108 | + display_audit_log { |
| 109 | + int id PK |
| 110 | + datetime display_time |
| 111 | + string display_type |
| 112 | + json data_json |
| 113 | + } |
| 114 | +
|
| 115 | + broker_state_cache ||--o{ position_snapshots : has |
| 116 | + broker_state_cache ||--o{ order_snapshots : has |
| 117 | +``` |
| 118 | + |
| 119 | +## SQL Schema Definitions |
| 120 | + |
| 121 | +### broker_state_cache Table |
| 122 | + |
| 123 | +```sql |
| 124 | +CREATE TABLE IF NOT EXISTS broker_state_cache ( |
| 125 | + id INTEGER PRIMARY KEY AUTOINCREMENT, |
| 126 | + account_id TEXT NOT NULL, |
| 127 | + state_type TEXT NOT NULL, |
| 128 | + data_json TEXT NOT NULL, |
| 129 | + fetched_at TEXT NOT NULL, |
| 130 | + expires_at TEXT NOT NULL, |
| 131 | + UNIQUE(account_id, state_type) |
| 132 | +); |
| 133 | +``` |
| 134 | + |
| 135 | +## Implementation Plan |
| 136 | + |
| 137 | +### Phase 1: Core Infrastructure (This PR) |
| 138 | + |
| 139 | +1. Create UnifiedBrokerCache class - SQLite-backed broker state caching |
| 140 | +2. Fix cache_adapter.py - Fixed cache key mismatch between GET and SET |
| 141 | +3. Create BrokerSnapshotManager - Store position/order snapshots |
| 142 | + |
| 143 | +### Phase 2: Integration |
| 144 | + |
| 145 | +1. Replace BrokerStateCache usage with UnifiedBrokerCache |
| 146 | +2. Update portfolio_tools.py to query from cache |
| 147 | +3. Update order_tools.py to use unified orders table |
| 148 | + |
| 149 | +### Migration Strategy |
| 150 | + |
| 151 | +```mermaid |
| 152 | +flowchart TD |
| 153 | + A[Start Migration] --> B{Feature Flag Enabled?} |
| 154 | + B -->|No| C[Use Existing JSON] |
| 155 | + B -->|Yes| D[Write to Both DB + JSON] |
| 156 | + D --> E{Validation Pass?} |
| 157 | + E -->|No| F[Log Discrepancy] |
| 158 | + F --> C |
| 159 | + E -->|Yes| G[Read from DB Only] |
| 160 | + G --> H[Remove JSON Fallback] |
| 161 | +``` |
| 162 | + |
| 163 | +## Success Metrics |
| 164 | + |
| 165 | +- Cache hit rate greater than 80 percent for broker state |
| 166 | +- Display latency less than 500ms |
| 167 | +- 100 percent audit coverage for displayed data |
| 168 | +- Zero discrepancy between displayed and stored data |
| 169 | + |
| 170 | +## Timeline Estimate |
| 171 | + |
| 172 | +- Phase 1 (Core): 2-3 hours |
| 173 | +- Phase 2 (Integration): 3-4 hours |
| 174 | +- Phase 3 (Display): 2-3 hours |
| 175 | +- Phase 4 (Validation): 1-2 hours |
| 176 | + |
| 177 | +Total: approximately 10 hours of implementation work |
0 commit comments