API / old /data-pipeline-roadmap.md
sshinmen's picture
Update HF Spaces deployment with latest changes
2e6b65c
|
Raw
History Blame Contribute Delete
6.21 kB
# 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.