--- title: interview / idempotency-rate-limit-design-tradeoffs-2026-06-09 source_type: interview-prep status: raw related_branches: [feature-rate-limit-idempotency-contract] related_projects: [ca-skeleton] tags: [interview, ca-skeleton, idempotency, rate-limit, concurrency] created: 2026-06-09 --- # interview: 멱등성 / rate-limit 설계 트레이드오프 > Layer: `raw/interviews/` — feature-rate-limit-idempotency-contract 구현에서 나올 수 있는 질문. ## Parent / 부모 - [[raw/branch-notes/feature-rate-limit-idempotency-contract]] ## 예상 질문 / Q&A - **Q. 동시에 같은 idempotency key가 오면?** A. DB unique 제약(`(tenant, principal, idempotency_key, use_case_name)`)을 동시성 중재자로 사용. 첫 요청이 IN_FLIGHT row 선점(insert), 후속은 insert 실패 → read. read가 IN_FLIGHT면 200ms까지 poll 후 초과 시 409 IDEMPOTENT_IN_FLIGHT(retryable=false, client는 polling). - **Q. 200ms wait는 표준인가?** A. 아니다. IETF draft/Toss는 즉시 409 SHOULD. 200ms는 client retry 친화적 "변형"이고 thread를 잡는 비용이 있어 부하 테스트로 튜닝 대상. 면접에서 "표준 따름"으로 말하면 안 됨. - **Q. 같은 key + 다른 body는?** A. body SHA-256 fingerprint 비교 → 다르면 422 IDEMPOTENT_REQUEST_MISMATCH(IETF 422 권고 정합). 단 canonicalization(키 순서/공백) 미적용 시 false mismatch 위험 — 본 구현은 직렬화된 payload 기준. - **Q. single-tenant인데 unique 제약이 동작하나? (tenant NULL)** A. PostgreSQL은 NULL을 distinct로 취급 → NULL tenant면 dedup 실패. 그래서 tenant 컬럼을 `NOT NULL DEFAULT ''`로 두고 매퍼가 null↔'' 변환. - **Q. 만료(TTL) 처리?** A. 읽기에서 만료 row를 absent 취급 + tryBegin에서 만료 row reclaim(delete 후 insert) + 주기적 reaper(@Scheduled bulk delete) 3중. 읽기 필터와 쓰기 선점이 같은 만료 기준을 공유해야 "유령 충돌"이 없음. - **Q. rate-limit 알고리즘은?** A. single-node in-process fixed-window counter(ConcurrentHashMap.compute + AtomicInteger). 장점: X-RateLimit-Reset이 창 종료로 정확. 단점: 창 경계 burst 허용, 멀티 인스턴스면 N배(distributed limiter는 out of scope). - **Q. 왜 filter가 아니라 interceptor?** A. unauth key가 `IP + route template`을 요구하는데 servlet filter는 handler mapping 전이라 template을 모름. interceptor는 `BEST_MATCHING_PATTERN_ATTRIBUTE`로 `/v1/worklogs/{id}`를 얻음. ## 관련 - [[raw/branch-notes/feature-rate-limit-idempotency-contract]] - [[wiki/concepts/idempotency-key-design]]