7.7 KiB
7.7 KiB
STORY-06: Results Persistence & Export
Epic
E1: Core Pipeline — As a system, I can persist processing results and export data for analytics.
Related Requirements
| ID | Requirement |
|---|---|
| FR-07 | Metadata logging & audit trail: Stores video ID, timestamps, frame counts, confidence scores, routing decision, model version |
| FR-04 | Video-level confidence aggregation & threshold routing: Results saved to Parquet |
| NFR-05 | Fault Tolerance: Metadata persisted before cleanup |
| NFR-07 | Data Volume Handling: Efficient indexing for ~30TB dataset |
| TC-03 | Storage I/O: Shared NAS/SMB for video input/output |
Description
Implement result persistence, metadata logging, and data export. Update the database with processing results, insert processing log entries, and export summary data in Parquet/JSONL format for analytics. Ensure all persistence happens before scratch space cleanup to prevent data loss.
Scope
In Scope
- Database update for processed videos (status, confidence, routing, timestamps)
- Processing log entry insertion (atomic transaction)
- Parquet/JSONL export to
/data/output - Confidence score persistence (frame-level and video-level)
- Model version tracking in DB and export
- Idempotent result persistence (re-processing doesn't duplicate entries)
- Transaction-safe updates (all-or-nothing)
Out of Scope
- Frame sampling (covered in STORY-03)
- Face detection (covered in STORY-04)
- Classification (covered in STORY-05)
- Review UI (covered in STORY-07)
- Active learning pipeline (covered in STORY-08)
Deliverables
6.1 Result Updater
File: src/result_updater.py
Features:
- Video Record Update: Update
videostable with processing resultsUPDATE videos SET status = 'COMPLETED', last_processed_time = NOW(), confidence_score = %s, routing_decision = %s, model_version = %s, frame_count = %s, updated_at = NOW() WHERE id = %s AND status = 'PROCESSING' - Atomic Transaction: Wrap video update + log insert in single transaction
- State Guard: Only update if status is PROCESSING (prevents duplicate processing)
- Error Handling: Rollback transaction on any error; re-queue video as PENDING
6.2 Processing Logger
File: src/processing_logger.py
Features:
- Log Entry Insertion: Insert record into
processing_logstabledef insert_processing_log(video_id, model_version, frame_count, confidence_score, routing_decision): cursor.execute(""" INSERT INTO processing_logs (video_id, model_version, frame_count, confidence_score, routing_decision, processed_at) VALUES (%s, %s, %s, %s, %s, NOW()) """, (video_id, model_version, frame_count, confidence_score, routing_decision)) - Frame-Level Confidence Storage: Store all frame-level confidence scores
- Option A: JSON array in processing_logs.confidence_scores (TEXT column)
- Option B: Separate
frame_confidencestable (for large datasets) - Default: JSON array in processing_logs (simpler, sufficient for typical frame counts)
- Audit Trail: All log entries include timestamp, model version, and routing decision
6.3 Data Export
File: src/data_export.py
Features:
- Parquet Export: Write summary data to Parquet format
# Schema: # video_id (int64), file_path (string), file_hash (string), # model_version (string), sample_count (int32), # confidence_scores (list<float>), video_confidence (float), # routing (string), processed_at (timestamp) - JSONL Export: Alternative line-delimited JSON format for streaming analytics
{"video_id": 12345, "file_path": "/data/input/video.mp4", "model_version": "v1.2.0", "sample_count": 8, "confidence_scores": [0.82, 0.79, 0.85, 0.76, 0.81, 0.83, 0.78, 0.80], "video_confidence": 0.85, "routing": "MATCH", "processed_at": "2026-08-03T10:30:00Z"} - Output Directory:
/data/output/{model_version}/organized by model version - File Naming:
{model_version}_{batch_id}.parquetand{model_version}_{batch_id}.jsonl - Batch ID: Sequential counter or timestamp-based (e.g.,
v1.2.0_20260803_103000) - Compression: Parquet with Snappy compression
- Export Trigger: Export after all videos in batch are processed (or periodically)
6.4 Scratch Cleanup
File: src/scratch_manager.py (cleanup section)
Features:
- Post-Processing Cleanup: Delete all frames and crops after results are persisted
- Cleanup Order: Results → Logs → Export → Cleanup (critical ordering)
- Cleanup Verification: Verify DB update before deleting scratch files
- Cleanup on Error: If persistence fails, do NOT delete scratch (allow retry)
- Cleanup Logging: Log cleanup completion with video_id and files removed
6.5 Configuration Updates
File: config.yaml (updates)
New fields:
results:
persist_before_cleanup: true
transaction_safe: true
state_guard: true # only update PROCESSING → COMPLETED
export:
format: parquet # parquet, jsonl, or both
output_path: /data/output
compression: snappy
batch_size: 100 # export after N videos
include_frame_confidences: true
Acceptance Criteria
Functional
- Video status transitions from PROCESSING to COMPLETED after successful processing
- Processing log entry is inserted for every processed video
- Frame-level confidence scores are stored (all p_i values)
- Video-level confidence score is stored correctly
- Routing decision is persisted accurately
- Model version is recorded in both videos and processing_logs tables
- Parquet export produces valid, readable files with correct schema
- JSONL export produces valid JSON lines with correct fields
- Export files are organized by model version in /data/output/
- Scratch space is cleaned up after persistence (verified by directory check)
- Re-processing a video updates existing records (no duplicates)
- Transaction rollback on error prevents partial state updates
Non-Functional
- Result persistence completes in < 5 seconds per video
- Parquet export for 100 videos completes in < 10 seconds
- JSONL export for 100 videos completes in < 10 seconds
- Export files are valid (verified by parquet.read_table and jsonl parsing)
- Scratch cleanup frees > 95% of allocated space per video
- No data loss if worker crashes between processing and persistence (PROCESSING state preserved)
Technical Constraints
- All DB updates use parameterized queries (no SQL injection)
- Transaction isolation level is READ COMMITTED or higher
- Parquet files use Snappy compression (not gzip, for speed)
- JSONL files are UTF-8 encoded
- Confidence scores are stored with float64 precision
- Timestamps are in UTC (ISO 8601 format)
Dependencies
- Prerequisites: STORY-01 (Foundation — DB schema), STORY-05 (Classification — provides results)
- Depends on: None (runs after classification)
- Enables: STORY-07 (Review UI — provides routing decisions), STORY-08 (Active Learning — provides training data)
Risks & Mitigations
| Risk | Mitigation |
|---|---|
| NAS write latency slows export | Batch writes; use local tmpfs for export staging |
| Parquet library compatibility with CUDA container | Test pyarrow in Worker container early; pin version |
| Transaction rollback leaves video in PROCESSING state | DB lock timeout (5 min) prevents permanent lock |
| Scratch cleanup before persistence causes data loss | Enforce ordering: persist → verify → cleanup |
Estimated Effort
- Sprint: 5-6 (second half)
- Story Points: 13
- Dependencies: STORY-01, STORY-05