perf(slots): BAT-040 — slots RPC DB-level status 필터 (REQ-002/006/073, issue #25)

REQ-002/006 change-req(CHG-20260612-01)→구현→confirm-req-implemented,
REQ-073 change-req(CHG-20260612-02, 관찰 계약 불변 Implemented 유지) 전체 delivery.

- repository.py/in_memory_repository.py: SlotRepository.list_slots(instance_key,
  status_filter=None) — status 값 집합 필터, None=전체(기존 호출자 불변).
- postgresql.py: list_slots DB-level WHERE status = ANY(%s) (full scan + Python
  필터 제거). 스키마/마이그레이션 변경 없음.
- messaging_handlers.py: _FILTER_STATUS 매핑으로 filter→status 집합 위임,
  watchdog 갱신용 holding 조회를 all/holding fetch 결과에서 재사용(2nd 커넥션 생략).
- 관찰 가능한 RPC 계약(REQ-073 AC-1~6) 불변 — 동작 보존 최적화.
- tests: in-memory status_filter 5종 + PG 게이트 1종. 439 passed (+5), ruff clean.

REQ-002·006 Approved→Implemented, REQ-073 Implemented 유지, BAT-040 release-candidate.
issue #25 close.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 19:27:51 +09:00
co-authored by Claude Opus 4.8
parent 14da195b1e
commit d737467d70
16 changed files with 479 additions and 27 deletions
@@ -51,8 +51,14 @@ class InMemorySlotRepository:
"""Test seeding helper — insert a slot row directly."""
self._slots[slot.slot_id] = slot
def list_slots(self, instance_key: str) -> list[Slot]:
return [s for s in self._slots.values() if s.instance_key == instance_key]
def list_slots(
self, instance_key: str, status_filter: Optional[set[str]] = None
) -> list[Slot]:
return [
s for s in self._slots.values()
if s.instance_key == instance_key
and (status_filter is None or s.status.value in status_filter)
]
def fetch_slot(self, slot_id: UUID) -> Optional[Slot]:
return self._slots.get(slot_id)
+9 -2
View File
@@ -37,8 +37,15 @@ class SlotRepository(Protocol):
...
# --- daily_slot CRUD ---
def list_slots(self, instance_key: str) -> list[Slot]:
"""Return all slots owned by this instance."""
def list_slots(
self, instance_key: str, status_filter: Optional[set[str]] = None
) -> list[Slot]:
"""Return slots owned by this instance.
status_filter: when given, only slots whose status value is in the set
are returned (filtered at the storage boundary — REQ-006 pushes this to
SQL). None returns all slots (prior behaviour). issue #25.
"""
...
def fetch_slot(self, slot_id: UUID) -> Optional[Slot]: