SLI / SLO 카탈로그
techspec의 8개 SLI(S1~S8)를 실측 가능한 숫자 SLO로 확정하고, 각각의 측정 윈도·데이터소스 쿼리·에러버짓 공식·관련 NFR/KPI ID를 고정한다. 모든 SLO 목표치는 운영 데이터가 없는 진입 단계의 초기 확정치(설계)이며 운영 베이스라인으로 분기별 조정한다 — 달성치(achieved)는 기재하지 않는다. 그리고 이 카탈로그의 전제이자 갭인 METRICS.writeDataPoint 미방출(현재 TODO)을 OBS-METRICS-001로 정규 요구화한다. 본 페이지는 NFR 페이지가 위임한 SLO 상세의 단일 소유처다.
1. 원칙 — 측정 불가능한 SLO는 요구사항이 아니다
SLO는 "약속한 숫자"가 아니라 "지속적으로 측정되어 위반 시 행동을 유발하는 숫자"일 때만 요구사항이 된다. 따라서 이 카탈로그의 모든 SLI는 측정 경로가 코드에 존재함을 전제로 한다.
- 측정 평면. 모든 시스템 SLI(S1·S2·S3·S5·S6·S8)는 Workers Analytics Engine의 METRICS 데이터셋(
loginippon_metrics)에 기록한 데이터포인트를 집계해 산출한다. 제품 KPI 계열(S4·S7)은 D1 집계가 1차 소스이되, 운영 SLO 렌즈로는 동일 정의를 재참조한다(techspec과 정합). - 상관 키. 모든 데이터포인트·로그에
request_id·shipment_id·tenant_id를 부착해 엣지→Queue→DO→D1 경로를 추적한다.request_id는 미들웨어(server/src/middleware/error.ts)가 생성해AuthContext.requestId로 운반한다. - 개인정보 최소화. 차원(태그)에는 식별자만 싣고 좌표·실명은 싣지 않는다(security-privacy 감사·PII 정책과 정합).
loginippon_metrics) 바인딩은 존재하나(server/src/types/index.ts METRICS: AnalyticsEngineDataset), writeDataPoint 호출은 TODO다. 즉 현재 S1·S2·S3·S5·S6·S8은 측정되지 않는다. 이 카탈로그는 목표를 약속하기 전에 방출(emission)을 먼저 요구한다 → OBS-METRICS-001. 측정 평면이 없는 SLO는 본 TRD에서 "요구사항"이 아니라 "의도"로 간주한다.
2. SLO 카탈로그 (S1~S8)
아래 8행은 각 SLI의 정의, §4.1/§4.2 정규 수치표에서 글자 그대로 인용한 SLO 목표, 측정 윈도, 데이터소스 쿼리(어떤 메트릭/태그를 어떻게 집계하는가), 에러버짓 공식, 관련 NFR/KPI ID를 고정한다. 목표치는 전부 초기 확정치(설계)이며 운영 베이스라인으로 조정한다.
| SLI | 정의 / 측정 지점 | SLO 목표 설계 | 측정 윈도 | 데이터소스 쿼리 (loginippon_metrics) | 에러버짓 공식 | 관련 ID |
|---|---|---|---|---|---|---|
| S1 인제스트 accept 지연 | POST /v1/ingest/positions 수신 → 202 {accepted} 반환까지(server/src/ingest/positions.ts 핸들러 entry→exit). 무거운 처리는 비동기. |
p95 ≤ 200ms, p99 ≤ 400ms | 28일 롤링 | blob: metric="ingest_accept"; double1=latency_ms; idx=route. quantileWeighted(latency_ms, .95) WHERE metric='ingest_accept' AND outcome='ok' over 28d. |
budget = events(latency>200ms) / events 총; 목표 ≤5%(p95 정의상) | NFR-PERF-001 |
| S2 지오펜스 평가 lag | enqueue(GeofenceJob) → enter/exit 판정 + event INSERT까지. 컨슈머 handleGeofenceBatch(server/src/consumers/queue.ts)에서 ping.captured_at 또는 enqueue ts 대비 처리완료 ts. |
p95 ≤ 5s, p99 ≤ 15s | 28일 롤링 | blob: metric="geofence_lag"; double1=lag_ms; idx=queue='loginippon-geofence-eval'. quantileWeighted(lag_ms,.95). 큐 깊이는 Queues 지표 보조. |
budget = events(lag>5s)/총. 큐 max_batch_timeout=5s와 정합 — 초과는 백로그 신호 |
NFR-PERF-002 |
| S3 웹훅 전달 성공률 | 첫 시도 또는 재시도 래더 내 2xx 수신 비율. 발신 지점 handleNotifyBatch(queue.ts, 큐 loginippon-notify). 발신 타임아웃 5s hard. |
≥ 99.0% / 28일 | 28일 롤링 | blob: metric="webhook_delivery"; idx=outcome∈{2xx,4xx,5xx,timeout}; double1=attempt. sum(outcome='2xx') / count(distinct delivery_id) over 28d. |
budget = 1 − 0.99 = 1.0% 실패/28일; 재시도 소진(8회) 후에도 비-2xx면 실패 1건 | NFR-RELY-001 · FR-DLV-WH-001 |
| S4 Tracking Rate (추적 커버리지) | received_pings / expected_pings; expected = active_segment_sec / ping_cadence. 제품 KPI 겸 수집 파이프라인 건강도. 스마트폰 GPS 한계로 100%는 비현실 — 정직 노출. | MVP exit floor ≥ 85%, 정상상태 지향 ≥ 92% | 역월 (테넌트/캐리어 드릴다운) | 1차: D1 집계(active_segment vs received pings). 보조: metric="ingest_accept" shipment 태그 카운트 / 기대 cadence. |
floor 미달 = 게이트 위반(에러버짓 아님; phase-gates P1 exit DoD 연결) | KPI-TRACK-001 |
| S5 가용성 | 대시보드/API 성공 응답 비율(5xx·timeout 제외). Web Analytics + 합성 점검(synthetic) 보강. 엣지 자동확장 전제. | ≥ 99.9% / 역월 | 역월 (calendar month) | blob: metric="http"; idx=outcome∈{ok,5xx,timeout}. 1 − sum(outcome∈{5xx,timeout})/count(*). 합성 프로브 별도 데이터셋 row. |
budget = 0.1%/월 ≈ 43분/역월 다운타임 허용 | NFR-AVAIL-001 |
| S6 WebSocket 실시간 반영 | 위치 push → 대시보드 broadcast까지. pushToDo 수신 → broadcast({type:'position'})(server/src/do/shipment.ts). Flutter 단일-shipment 라이브 뷰 체감 직결. |
p95 ≤ 1.5s, p99 ≤ 3s | 28일 롤링 | blob: metric="ws_broadcast"; double1=fanout_ms; idx=shipment_id(저카디널리티 샘플). quantileWeighted(fanout_ms,.95). DO 내부 계측. |
budget = events(fanout>1.5s)/총. console 플릿 개요는 폴링이라 별도(NFR-PERF-003b staleness) | NFR-PERF-003 |
| S7 ETA 오차 (MAPE) | |predicted − actual_arrival| / actual 평균(배달 12h 전 기준). 룰 기반은 "추정치"(eta_is_estimate=1) 정직 표기. 콜드스타트엔 하드 SLO 아님. |
룰 기반 ≤ 25%(하드 SLO 아님), ML 후 ≤ 15% | 역월 (레인-클래스별) | D1: 완료 운행의 predicted vs actual_arrival join. 보조 metric="eta_error" double1=ape; idx=lane_class. avg(ape). |
하드 게이트 아님 → 번레이트 알람 대신 추세 보고. ML 전환 트리거는 FR-ENG-ETA-002 | KPI-ETA-001 |
| S8 이벤트 정규화 정확도 | raw_code → canonical 해결률. 컨슈머 정규화 단계 event_code_map 미스 카운트. 미매핑 raw_code는 알림→매핑 보강. |
≥ 99% canonical 해결(미매핑 <1%/일), 충돌 0 | 역월 (일 단위 미스 추세 병행) | blob: metric="normalize"; idx=outcome∈{resolved,unmapped,collision}; blob2=raw_code. sum(resolved)/count(*); count(unmapped)/hour 알람. |
budget = 1.0% 미매핑/일; collision은 무관용(0 목표) |
KPI-NORM-001 · DM-EVT-ENUM-001 |
3. 에러버짓 정책
가용성·성공률 SLO(S5·S3)에는 에러버짓 = 1 − SLO를 둔다. 버짓은 "위반 없이 쓸 수 있는 실패의 양"이며, 변경 속도(릴리스)와 안정화를 저울질하는 통화다.
| SLO | 버짓 = 1 − SLO | 윈도 | 소진 시 행동 |
|---|---|---|---|
| S5 가용성 ≥ 99.9% | 0.1% / 월 ≈ 43분 다운타임 | 역월 | 버짓 소진 시 prod 승격 차단(릴리스 게이트) — 신규 기능보다 안정화·회귀 수정 우선. pipeline-ops의 CI 게이트와 결합. |
| S3 웹훅 ≥ 99.0% | 1.0% 실패 / 28일 | 28일 롤링 | 대량 disabled 또는 성공률 하락 시 발신 경로·구독자 엔드포인트 점검. 버짓 50% 소진 시 경고, 100% 시 발신 변경 동결. |
번 레이트(burn rate) 알람. 단일 임계가 아니라 소진 속도로 경보한다. 번레이트 = (윈도 내 실패율) / (허용 실패율). 다중창 알람으로 거짓 경보를 줄인다.
- 빠른 소진(fast burn): 1시간 창에서 번레이트 ≥ 14.4 (= 1시간에 월 버짓의 2% 소진) → HIGH, 즉시 LINE. 페이지 가능 사건.
- 느린 소진(slow burn): 6시간 창에서 번레이트 ≥ 6 → MEDIUM, 이메일 + 대시보드. 추세 보고.
- S7(MAPE)·S4(Tracking Rate floor)는 가용성형 버짓이 아니라 게이트/추세로 다룬다 — S4 floor 미달은 phase-gates exit DoD 위반, S7은 ML 전환 트리거(FR-ENG-ETA-002).
4. OBS-METRICS-001 — METRICS.writeDataPoint 방출 요구
위 카탈로그 전체는 데이터포인트 방출에 의존한다. 현재 방출이 없으므로(§1 갭), 이를 정규 요구사항으로 못박는다. 아래 카드가 본 페이지가 소유하는 유일한 ID다.
요구. 시스템은 S1·S2·S3·S5·S6·S8 각 SLI의 정의된 측정 지점에서 env.METRICS.writeDataPoint(...)를 반드시 호출하여 loginippon_metrics 데이터셋에 데이터포인트를 방출해야 한다(MUST). 현재 writeDataPoint는 TODO이며 측정 평면이 부재하다 — 이 요구가 충족되기 전까지 S1·S2·S3·S5·S6·S8 SLO는 "의도"이지 측정 가능한 요구사항이 아니다.
호출 지점(SLI 매핑).
- S1 인제스트:
server/src/ingest/positions.ts—202반환 직전, 핸들러 entry→exit 경과로ingest_accept방출. - S2 / S8 컨슈머:
server/src/consumers/queue.tshandleGeofenceBatch— 평가 lag(geofence_lag) + 정규화 결과(normalize: resolved/unmapped/collision)를 ping 처리마다 방출. - S3 웹훅 발신:
handleNotifyBatch(큐loginippon-notify) — POST 응답 코드·시도 횟수로webhook_delivery방출(발신 구현 시 동시 배선; 현재 ack-only STUB). - S6 DO broadcast:
server/src/do/shipment.tsbroadcast()— push 수신→fan-out 경과로ws_broadcast방출. - S5 가용성:
server/src/middleware/error.ts응답 경로 — 전 요청 outcome(ok/5xx/timeout)으로http방출 + 합성 프로브 row.
태그 스키마(표준). 모든 데이터포인트는 차원 request_id·route(또는 queue/op)·tenant·outcome를 인덱스/blob에 부착한다(좌표·실명 금지 — PII 최소화). 지연 계열은 doubles에 latency_ms/lag_ms/fanout_ms를 싣는다.
수용기준
- Given 정상 인제스트 1건, When
POST /v1/ingest/positions가202를 반환, Thenloginippon_metrics에metric='ingest_accept',outcome='ok',route·tenant·request_id차원과latency_ms를 가진 데이터포인트가 정확히 1건 기록된다. - Given 미매핑 raw_code 1건, When 컨슈머 정규화가
event_code_map미스로 떨어짐, Thenmetric='normalize',outcome='unmapped',raw_codeblob을 가진 데이터포인트가 기록되어 S8/§5 알람이 집계 가능하다. - Given 임의 데이터포인트, When 차원을 검사, Then
request_id·route(or queue/op)·tenant·outcome가 모두 존재하고 좌표/실명 필드는 부재한다.
5. 알림 임계
여기서의 알림은 시스템 건강(SLO 위반·예외 폭증·비용)을 다룬다 — 제품 예외(배달 지연·荷待ち 초과 등)와 채널은 재사용(LINE/이메일)하되 대상이 다르다. 임계는 디바운싱·중복 억제로 거짓 경보를 줄인다.
| 트리거 | 임계 설계 | 심각도 | 채널·동작 | 관련 |
|---|---|---|---|---|
| 미매핑 raw_code 급증 (S8) | unmapped > 50 / 시간 | LOW~MEDIUM | 대시보드 + 일일 요약 → event_code_map 보강. 정밀도 매트릭스 충돌은 무관용. |
KPI-NORM-001 |
| 추적손실 오경보율 (FR-ENG-EXC-003 정합) | 오경보율 > 5% | MEDIUM | 이메일 + 임계 튜닝 검토. TRACKING_LOST(마지막 유효 핑 후 45min, Cron 5min sweep)의 거짓 양성 비율 감시. |
FR-ENG-EXC-003 |
| 가용성/지연 SLO 빠른 소진 (S5·S1·S6) | 번레이트 1h ≥ 14.4 | HIGH | LINE 즉시 + 버짓 소진 추세 동반 보고. prod 승격 차단 연동. | NFR-AVAIL-001 |
| 큐 백로그/소비 지연 (S2) | p95 lag > 5s 지속 or DLQ 증가 | HIGH | LINE/이메일. 소비자 스케일·핫스팟 점검. max_batch_timeout=5s 초과는 백로그 신호. |
NFR-PERF-002 |
| 웹훅 성공률 하락 / 대량 disabled (S3) | 28일 성공률 < 99% 추세 | MEDIUM~HIGH | 이메일 + 대시보드. 8연속 실패 auto-disable 급증 감시. | NFR-RELY-001 |
| 비용 가드레일 근접 | D1 쓰기 ≥ 70% 일 예산, R2 ≥ 80% 티어, DO active-time 월 cap 근접 | MEDIUM | 이메일 + 비용/한도근접 대시보드. 아카이브·샤딩·폴링 분할 레버 검토. | NFR-COST-001 · NFR-SCALE-001 |
근거·상호참조
- techspec: 08-operations/observability-slo.md — Analytics Engine·writeDataPoint·SLI S1~S8·에러버짓·알림·상관 키(
request_id/shipment_id/tenant_id) - techspec: 08-operations/delivery-pipeline.md — 에러버짓 릴리스 게이트(prod 승격 차단)
- techspec: 05-delivery-layer/delivery.md(웹훅·KPI 엔벨로프) · 04-tracking-engine/engine.md(Tracking Rate·추적손실·예외)
- code: server —
src/types/index.ts(METRICS: AnalyticsEngineDataset바인딩, writeDataPoint TODO) ·src/ingest/positions.ts(S1,202 {accepted}) ·src/consumers/queue.ts(S2/S3/S8:handleGeofenceBatch/handleNotifyBatch·event_code_map) ·src/do/shipment.ts(S6:pushToDo/broadcast) ·src/consumers/cron.ts(추적손실sweepActive*/5) ·src/middleware/error.ts(S5 outcome·request_id) · README.md - Research: tracking-engine(수집→처리 수 초·예외) · delivery-layer(Tracking Rate·OTD·MAPE)
- TRD 내부: nonfunctional(NFR-PERF/AVAIL/RELY/COST/SCALE 소유) · north-star-kpi(KPI-TRACK/ETA/NORM 소유) · functional(FR-DLV-WH-001·FR-ENG-EXC-003·FR-ENG-ETA-002) · data-model(DM-EVT-ENUM-001) · pipeline-ops(릴리스 게이트·인시던트) · phase-gates-roadmap(S4 floor DoD) · acceptance-traceability · regulatory(RR-LEGAL-001) · security-privacy