| # Data Pipeline & Streaming Services Upgrade Roadmap |
|
|
| ## Executive Summary |
| This document outlines a technical roadmap to upgrade the data pipelines and streaming services of the CLI Proxy API. The current implementation suffers from significant buffering bottlenecks, excessive I/O operations (double-writes), and scalability limits in log retrieval. The proposed upgrades focus on zero-copy streaming, asynchronous processing, and optimized data persistence. |
|
|
| ## Current Architecture Assessment |
|
|
| ### 1. Proxy & Streaming Service |
| **Current State:** |
| - **Buffering:** The proxy buffers the entire response body in memory for non-streaming responses (and gzip streams) to perform content rewriting. |
| - **Gzip Handling:** `internal/api/modules/amp/proxy.go` reads the full body into memory to decompress it if `Content-Encoding` is present, defeating the purpose of streaming for compressed upstream responses. |
| - **Response Rewriting:** `ResponseRewriter` buffers non-streaming bodies entirely. For SSE, it attempts to parse chunks individually, which is brittle if JSON tokens span across network chunk boundaries. |
|
|
| **Bottlenecks:** |
| - High memory pressure during large response payloads. |
| - Increased Time-To-First-Byte (TTFB) due to buffering. |
| - Potential data corruption in SSE if network chunks split `data:` lines or JSON fields. |
|
|
| ### 2. Data Pipeline (Logging) |
| **Current State:** |
| - **Write Path:** `FileStreamingLogWriter` writes chunks to a temporary file asynchronously. However, `Close()` triggers a synchronous "assembly" phase that reads the temp file back and writes it to the final log file. This results in 2x Disk I/O (Write Temp -> Read Temp -> Write Final). |
| - **Read Path:** `LogRepository` scans *all* files in the log directory to build a list or find logs. Reading a specific log involves iterating through lines in memory (`logAccumulator`). |
|
|
| **Bottlenecks:** |
| - Double I/O penalty for every logged request. |
| - Log retrieval performance degrades linearly (O(N)) with the number of log files. |
| - Synchronous blocking on file system operations during request finalization. |
|
|
| --- |
|
|
| ## Technical Roadmap |
|
|
| ### Phase 1: Zero-Buffer Streaming Proxy |
| **Goal:** Eliminate memory buffering in the proxy layer to minimize latency and memory footprint. |
|
|
| #### 1.1 Streaming Decompression |
| - **Task:** Refactor `proxy.go` to use a streaming `gzip.Reader` (or `brotli`/`zstd` wrappers) that wraps the `http.Response.Body`. |
| - **Implementation:** Create a `DecompressingReadCloser` that transparently decompresses as `Read()` is called, rather than pre-reading the whole body. |
| - **Benefit:** Constant memory usage regardless of response size. |
|
|
| #### 1.2 Streaming Response Rewriter |
| - **Task:** Rewrite `ResponseRewriter` to use a streaming JSON parser (e.g., `json.Decoder` or a token-based replacer) instead of `gjson`/`sjson` on full buffers. |
| - **Implementation:** |
| - Create a `TokenReplacingReader` that scans the stream for specific keys (`model`, `modelVersion`) and replaces values on the fly. |
| - Ensure it maintains state across `Read()` calls to handle tokens split across buffer boundaries. |
| - **Benefit:** Zero-latency overhead for model name rewriting; safe for large JSON bodies. |
|
|
| ### Phase 2: Robust SSE Handling |
| **Goal:** Ensure 100% reliability for streaming AI responses (Server-Sent Events). |
|
|
| #### 2.1 Stateful SSE Parser |
| - **Task:** Replace the naive line-splitting logic in `response_rewriter.go`. |
| - **Implementation:** |
| - Implement a state machine that buffers only incomplete lines. |
| - Process full `data: {...}` lines as they become available. |
| - Handle multi-line JSON data correctly. |
| - **Benefit:** Prevents corruption when network packets fragment SSE messages. |
|
|
| ### Phase 3: High-Performance Logging Pipeline |
| **Goal:** Decouple logging from request latency and reduce I/O. |
|
|
| #### 3.1 Eliminate Double-Writes |
| - **Task:** Redesign the log storage format to allow append-only writing without post-request assembly. |
| - **Implementation:** |
| - Change log format to a structured line-based JSON (NDJSON) or a format that doesn't require a specific "header-first, body-second" physical layout if possible. |
| - Alternatively, keep the temp file approach but use `sendfile` (via `io.Copy` optimizations) to merge files efficiently, or just move/rename the temp file to the final location if the order can be adjusted. |
| - **Recommendation:** Switch to a directory-per-request or a pure append-only log file where request metadata and body chunks are interleaved but tagged with a Request ID. This allows writing directly to the final destination. |
|
|
| #### 3.2 Async Log Persister |
| - **Task:** Move file I/O entirely out of the request context. |
| - **Implementation:** |
| - A background worker pool receives `LogEntry` objects (metadata, body chunks) via a buffered channel. |
| - Workers handle file opening/writing/closing independently of the HTTP handler. |
| - **Benefit:** Zero impact of disk latency on API response times. |
|
|
| ### Phase 4: Scalable Data Access |
| **Goal:** Make log retrieval instant regardless of history size. |
|
|
| #### 4.1 Indexing Strategy |
| - **Task:** Stop scanning all files for listing/searching. |
| - **Implementation:** |
| - Maintain a lightweight `index.json` or SQLite DB that tracks: `RequestID`, `Timestamp`, `Path`, `StatusCode`, `Filename`. |
| - Update the index asynchronously when logs are finalized. |
| - **Benefit:** O(1) lookup by Request ID; O(log N) lookup by time range. |
|
|
| #### 4.2 Optimized Reader |
| - **Task:** Read logs efficiently. |
| - **Implementation:** |
| - When tailing logs (`latest`), read the file backwards from the end (using `Seek`) rather than scanning from the start. |
| - Implement pagination for log listing based on the index. |
|
|
| --- |
|
|
| ## Execution Plan |
|
|
| 1. **Step 1 (Critical):** Fix the Proxy buffering. This is the biggest risk for production stability. |
| - Refactor `proxy.go` gzip handling. |
| - Refactor `ResponseRewriter` for streaming JSON. |
|
|
| 2. **Step 2 (Reliability):** Fix SSE parsing in `ResponseRewriter`. |
| - Implement stateful line buffering. |
|
|
| 3. **Step 3 (Performance):** Optimize Log Writing. |
| - Refactor `RequestLogger` to avoid double-write. |
|
|
| 4. **Step 4 (Scalability):** Implement Log Indexing. |
| - Add `LogIndexService` and update `LogRepository` to use it. |
|
|