왜 overlap 못 함
chat 과 heartbeat 가 OAuth credential 과 ChromaDB access 공유. parallel 실행은 rate-limit 충돌 + ChromaDB lock 경합 위험.
가벼운 상호 배제
- stream counter 가 live chat stream 추적.
- heartbeat flag 가 background tick 추적.
- chat 이 heartbeat flag clear 기다린 후 시작.
- heartbeat 가 chat stream active 시 tick skip.
- 둘 다 진행 전 check, 진행 중엔 안 함 — lock 경합 없음.
왜 asyncio.Lock 보다 단순
단일 user 시스템. 경합이 항상 '아빠가 지금 active?' — boolean 질문. lock 이 contested concurrency 함의, 여기 일어나는 일 아님.
가벼워 보여도 lifecycle은 엄격해야 해. chat은 stream counter를 시작 전에 올리고 모든 exit path의 finally에서 내려. heartbeat는 active flag를 세운 뒤 실패해도 반드시 해제해. counter 하나가 새면 이후 작업 전체가 영원히 "누가 사용 중"이라고 믿어 버려.
skip과 wait도 방향이 달라. 사람이 시작한 chat은 진행 중인 heartbeat가 끝나길 짧게 기다릴 수 있지만, background tick은 사람이 말하고 있으면 양보하고 다음 기회로 넘겨. 상호 배제의 우선순위가 product 가치, 곧 아빠의 현재 대화에 맞춰져 있어.
asyncio lock보다 단순하다는 말은 race가 없다는 뜻이 아니야. check와 flag 전환이 같은 event loop 안에서 어떤 순서로 일어나는지 test해야 해. future worker process가 늘어나면 boolean contract가 더는 충분하지 않을 수도 있어. 지금 topology에 맞는 가장 작은 도구를 쓰는 거야.
이 guard가 보호하는 것도 resource만은 아니야. background reply가 아빠의 live chat 사이에 끼어들거나 avatar state를 덮으면 관계의 turn-taking이 깨져. technical mutex가 conversation etiquette까지 지키는 셈이야.
검증할 땐 chat 시작과 heartbeat 시작을 같은 event-loop tick 근처에서 반복해. 둘이 동시에 active로 관측되는 순간이 없는지, error와 cancellation 뒤 flag가 남지 않는지 봐.