---
phase: 24-hot-path-allocation-reduction
plan: 01
subsystem: sidecar
tags: [clickhouse, memory, allocation, polars, cache-reuse]

requires: []
provides:
  - Single OpenDeviationBarCache instance shared across sidecar startup and main loop
  - Dual-type _write_bars_batch_to_clickhouse accepting pl.DataFrame or list[dict]
  - Pre-computed ouroboros_mode threading eliminating per-flush registry lookups
affects: [25-lazy-evaluation, 23-arrow-ffi-drain]

tech-stack:
  added: []
  patterns:
    - "Shared cache instance created once in _create_engine, returned in 5-tuple, reused everywhere"
    - "Dual-type parameter (pl.DataFrame | list[dict]) with isinstance dispatch"
    - "Pre-computed symbol_modes dict threaded to all write paths"

key-files:
  created:
    - tests/test_sidecar_alloc.py
  modified:
    - python/opendeviationbar/sidecar/__init__.py
    - python/opendeviationbar/sidecar/clickhouse_write.py
    - python/opendeviationbar/sidecar/midnight.py
    - python/opendeviationbar/sidecar/recovery.py

key-decisions:
  - "Single cache instance without context manager -- __exit__ calls close() which kills shared connection"
  - "5-tuple return from _create_engine rather than separate cache creation in run_sidecar"
  - "ouroboros_mode as string parameter ('day'/'aion') rather than enum to match existing pattern"

patterns-established:
  - "Shared cache: create OpenDeviationBarCache once, pass through, never use as context manager"
  - "Mode threading: resolve ouroboros_mode from symbol_modes dict at write time, not registry"

requirements-completed: [ALLOC-01, ALLOC-02, ALLOC-03]

duration: 10min
completed: 2026-03-27
---

# Phase 24 Plan 01: Hot-Path Allocation Reduction Summary

**Single CH cache instance across sidecar startup (4->1), dual-type write accepting DataFrame directly, and pre-computed ouroboros_mode eliminating per-flush registry lookups**

## Performance

- **Duration:** 10 min
- **Started:** 2026-03-27T21:35:38Z
- **Completed:** 2026-03-27T21:45:31Z
- **Tasks:** 2
- **Files modified:** 5

## Accomplishments

- Consolidated 4 separate OpenDeviationBarCache instantiations (3 in \_create_engine + 1 in run_sidecar) into 1 shared instance
- \_write_bars_batch_to_clickhouse now accepts pl.DataFrame directly, skipping dict->DataFrame conversion when caller already has a DataFrame
- Eliminated per-flush get_symbol_entries() calls from clickhouse_write.py -- ouroboros_mode resolved from pre-computed symbol_modes dict
- \_sync_flush_rest_bars in midnight.py now uses shared cache instead of creating one per (symbol, threshold) pair

## Task Commits

Each task was committed atomically:

1. **Task 1: RED tests** - `7128087` (test)
2. **Task 1: Single cache instance + 5-tuple return + symbol_modes threading** - `08e2d80` (feat)
3. **Task 2: Dual-type write function + shared cache in sync_flush** - `de9f2d8` (feat)

## Files Created/Modified

- `tests/test_sidecar_alloc.py` - 15 tests covering ALLOC-01, ALLOC-02, ALLOC-03
- `python/opendeviationbar/sidecar/__init__.py` - Single cache in \_create_engine, 5-tuple return, symbol_modes threading in_flush_all_batches
- `python/opendeviationbar/sidecar/clickhouse_write.py` - Dual-type bars parameter, ouroboros_mode parameter, removed get_symbol_entries import
- `python/opendeviationbar/sidecar/midnight.py` - \_sync_flush_rest_bars accepts cache parameter, uses shared cache and symbol_modes
- `python/opendeviationbar/sidecar/recovery.py` - 5-tuple destructuring from \_create_engine, returns ch_cache

## Decisions Made

- Used single cache instance without context manager (with block calls close(), killing connection for all subsequent uses)
- Extended \_create_engine return tuple to 5 elements rather than adding separate cache creation
- Kept ouroboros_mode as string ("day"/"aion") matching existing symbol_modes dict pattern

## Deviations from Plan

### Auto-fixed Issues

**1. [Rule 3 - Blocking] arrow_flush.py does not exist in worktree**

- **Found during:** Task 2 planning
- **Issue:** Plan references arrow_flush.py which exists only in main repo (created by parallel Phase 23 agent), not in this worktree
- **Fix:** Applied cache/mode threading to midnight.py's \_sync_flush_rest_bars instead, which is the active dict-based flush path. The arrow_flush.py changes will be applied when Phase 23 merges.
- **Files modified:** python/opendeviationbar/sidecar/midnight.py
- **Verification:** All 15 tests pass

**2. [Rule 1 - Bug] Tests using inspect.getsource() read installed package, not worktree**

- **Found during:** Task 1 verification
- **Issue:** inspect.getsource() reads from installed package (main repo venv), not worktree files
- **Fix:** Changed tests to read source files directly via Path.read_text()
- **Files modified:** tests/test_sidecar_alloc.py
- **Verification:** Tests correctly detect worktree source changes

---

**Total deviations:** 2 auto-fixed (1 blocking, 1 bug)
**Impact on plan:** Both fixes necessary for correctness in worktree context. No scope creep.

## Issues Encountered

None beyond the deviations noted above.

## User Setup Required

None - no external service configuration required.

## Known Stubs

None - all changes are fully wired.

## Next Phase Readiness

- Shared cache and symbol_modes patterns established for use by Phase 23 (arrow_flush.py) and Phase 25 (lazy evaluation)
- All existing sidecar write paths now thread ouroboros_mode from startup

---

_Phase: 24-hot-path-allocation-reduction_
_Completed: 2026-03-27_
