# Feature: Auxiliary Data Persistence

## Description
Persistence layer for supporting data including image metadata and chunk state. Image metadata enables image pipeline to track stored files, while chunk state persistence is critical for crash recovery of route chunk processing. These operations support other components' specific data requirements.

## Component APIs Implemented
- `save_image_metadata(flight_id: str, frame_id: int, file_path: str, metadata: Dict) -> bool`
- `get_image_path(flight_id: str, frame_id: int) -> Optional[str]`
- `get_image_metadata(flight_id: str, frame_id: int) -> Optional[Dict]`
- `save_chunk_state(flight_id: str, chunk: ChunkHandle) -> bool`
- `load_chunk_states(flight_id: str) -> List[ChunkHandle]`
- `delete_chunk_state(flight_id: str, chunk_id: str) -> bool`

## External Tools and Services
- **PostgreSQL**: Primary database with JSONB support
- **SQLAlchemy**: ORM and connection pooling

## Internal Methods
| Method | Purpose |
|--------|---------|
| `_build_chunk_handle_from_row(row)` | Map database row to ChunkHandle object |
| `_serialize_chunk_frames(frames)` | Serialize frame list to JSONB array |
| `_deserialize_chunk_frames(jsonb)` | Deserialize JSONB array to frame list |
| `_serialize_metadata(metadata)` | Serialize metadata dict to JSONB |
| `_deserialize_metadata(jsonb)` | Deserialize JSONB to metadata dict |
| `_upsert_chunk_state(flight_id, chunk)` | Insert or update chunk state |

## Unit Tests
1. **save_image_metadata**
   - New image → metadata persisted with file_path
   - Overwrite same frame_id → updates metadata
   - Verify JSONB serialization of metadata dict
   - Verify uploaded_at timestamp set

2. **get_image_path**
   - Existing image → returns file path string
   - Non-existent frame → returns None
   - Non-existent flight → returns None

3. **get_image_metadata**
   - Existing image → returns metadata dict
   - Verify deserialization of original_name, width, height, file_size
   - Non-existent → returns None

4. **save_chunk_state**
   - New chunk → persisted successfully
   - Update existing chunk → state updated
   - Verify all fields: chunk_id, start_frame_id, end_frame_id, frames
   - Verify anchor fields: has_anchor, anchor_frame_id, anchor_gps
   - Verify matching_status persisted
   - Verify frames JSONB array serialization

5. **load_chunk_states**
   - Flight with 3 chunks → returns all chunk handles
   - No chunks → returns empty list
   - Verify correct deserialization of ChunkHandle fields
   - Verify frames list deserialized from JSONB

6. **delete_chunk_state**
   - Existing chunk → deleted, returns True
   - Non-existent chunk → returns False
   - Verify other chunks unaffected

## Integration Tests
1. **Image metadata lifecycle**
   - save_image_metadata() for 100 frames
   - get_image_path() for each → all paths returned
   - Delete flight → cascade deletes image metadata
   - Verify no orphan records

2. **Chunk state crash recovery**
   - Create flight, save 5 chunk states
   - Simulate crash (close connection)
   - Reconnect, load_chunk_states()
   - Verify all 5 chunks restored with correct state
   - Verify frames lists intact

3. **Chunk lifecycle operations**
   - save_chunk_state() → active chunk
   - Update chunk: add frames, set has_anchor=True
   - save_chunk_state() → verify update
   - delete_chunk_state() after merge → verify removed

4. **Concurrent chunk operations**
   - Multiple chunks saved concurrently
   - Verify no data corruption
   - Verify unique chunk_ids enforced

5. **JSONB query performance**
   - Save chunks with large frames arrays (500+ frames)
   - load_chunk_states() performance within 50ms
   - Verify JSONB indexing effectiveness

6. **Foreign key constraints**
   - save_chunk_state with invalid anchor_frame_id → proper error handling
   - Verify FK constraint fk_anchor_frame enforced
   - Delete referenced image → anchor_frame_id set to NULL


