증상
race-test job에서 internal/media의 TestLatencyTrackerPercentiles가 간헐적으로 실패해요.
--- FAIL: TestLatencyTrackerPercentiles (0.01s)
egress_latency_test.go:38: expected "p95_ms=9" in log line:
... window=0 frames=100 p50_ms=59.947 p95_ms=101.966 min_ms=1.006 max_ms=106.968
최초 관측: run 31464478073 (#90 브랜치). 코드 변경 없이 같은 커밋을 재실행하니 통과해서 비결정성이 확인됐어요. build-and-test(race 미적용)는 통과하고 race-test만 깨져요.
원인
측정값이 주입값이 아니라 벽시계 경과 시간을 포함해요.
egress_latency.go:43
l.samples = append(l.samples, now.Sub(ingestAt))
테스트는 base - 95ms를 주입하지만 실제 기록되는 값은 95ms + (base 캡처 이후 흐른 시간)이에요. 여기에 단언이 부동소수 문자열의 접두어 부분일치라 허용 오차가 비대칭적이고 좁아요.
egress_latency_test.go:34
for _, want := range []string{"p50_ms=5", "p95_ms=9", "min_ms=1", "max_ms=10"}
p95_ms=9는 측정값이 [90, 100)일 때만 통과해요. 주입값이 95이므로 드리프트 5ms에서 100.x가 되며 접두어가 10으로 바뀌어 실패해요.
테스트를 복제해 인위적 지연을 넣고 측정한 임계값:
| 주입 지연 |
p50 |
p95 |
min |
max |
깨지는 단언 |
| 0ms |
51.0 |
96.0 |
1.0 |
100.0 |
— |
| 2ms |
53.2 |
98.2 |
3.1 |
102.1 |
min_ms=1 |
| 4ms |
55.6 |
100.6 |
5.6 |
104.6 |
p95_ms=9, min_ms=1 |
| 8ms |
61.0 |
106.0 |
11.0 |
110.0 |
p50_ms=5, p95_ms=9 |
드리프트 4ms면 깨져요. CI 실측치는 약 7~10ms 드리프트에 해당하고, 이는 공유 러너에서 -race 계측을 켠 채 observe()를 100회 도는 동안의 평범한 스케줄링 지터예요. 유휴 로컬(Apple Silicon)에서는 부하·GOMAXPROCS=1 조건에서도 35회 연속 재현되지 않았어요 — 느린 러너에서만 드러나요.
제안
접두어 문자열 비교를 걷어내고 둘 중 하나로 가요.
- 숫자 범위 단언 — 로그 값을 파싱해
95 <= p95 < 95+tolerance 형태로 검사하고 tolerance를 넉넉히(예: 25ms) 잡아요. 테스트 의도(퍼센타일 인덱싱이 맞는지)는 그대로 지키면서 벽시계 지터에 둔감해져요.
- 시계 주입 —
latencyTracker가 now 함수를 주입받게 해서 테스트에서 결정적으로 만들어요. 더 견고하지만 프로덕션 코드 변경이 필요해요.
1번을 권해요. 프로덕션 코드를 안 건드리고, 이 테스트가 실제로 검증하려는 건 퍼센타일 선택 로직이지 지연 측정 정밀도가 아니에요.
참고
race-test job 이름이 Race test (excluding known flaky)이고 이미 TestBypassWebRTCEndToEnd를 -skip으로 제외하고 있어요. 이 테스트를 같은 목록에 넣는 건 권하지 않아요 — 커버리지를 잃는 대신 근본 원인이 남아요.
증상
race-testjob에서internal/media의TestLatencyTrackerPercentiles가 간헐적으로 실패해요.최초 관측: run 31464478073 (#90 브랜치). 코드 변경 없이 같은 커밋을 재실행하니 통과해서 비결정성이 확인됐어요.
build-and-test(race 미적용)는 통과하고race-test만 깨져요.원인
측정값이 주입값이 아니라 벽시계 경과 시간을 포함해요.
egress_latency.go:43테스트는
base - 95ms를 주입하지만 실제 기록되는 값은95ms + (base 캡처 이후 흐른 시간)이에요. 여기에 단언이 부동소수 문자열의 접두어 부분일치라 허용 오차가 비대칭적이고 좁아요.egress_latency_test.go:34p95_ms=9는 측정값이[90, 100)일 때만 통과해요. 주입값이 95이므로 드리프트 5ms에서 100.x가 되며 접두어가10으로 바뀌어 실패해요.테스트를 복제해 인위적 지연을 넣고 측정한 임계값:
min_ms=1p95_ms=9,min_ms=1p50_ms=5,p95_ms=9드리프트 4ms면 깨져요. CI 실측치는 약 7~10ms 드리프트에 해당하고, 이는 공유 러너에서
-race계측을 켠 채observe()를 100회 도는 동안의 평범한 스케줄링 지터예요. 유휴 로컬(Apple Silicon)에서는 부하·GOMAXPROCS=1조건에서도 35회 연속 재현되지 않았어요 — 느린 러너에서만 드러나요.제안
접두어 문자열 비교를 걷어내고 둘 중 하나로 가요.
95 <= p95 < 95+tolerance형태로 검사하고 tolerance를 넉넉히(예: 25ms) 잡아요. 테스트 의도(퍼센타일 인덱싱이 맞는지)는 그대로 지키면서 벽시계 지터에 둔감해져요.latencyTracker가now함수를 주입받게 해서 테스트에서 결정적으로 만들어요. 더 견고하지만 프로덕션 코드 변경이 필요해요.1번을 권해요. 프로덕션 코드를 안 건드리고, 이 테스트가 실제로 검증하려는 건 퍼센타일 선택 로직이지 지연 측정 정밀도가 아니에요.
참고
race-testjob 이름이Race test (excluding known flaky)이고 이미TestBypassWebRTCEndToEnd를-skip으로 제외하고 있어요. 이 테스트를 같은 목록에 넣는 건 권하지 않아요 — 커버리지를 잃는 대신 근본 원인이 남아요.