압축파일 손상 관련 해결 해보자~~
1. 배경
TPS 1,000 이상 나올시 해당 데이터들을 압축하고 전송하는 쓰레드에서 문제가 생김.
2. 첫 증상
2-1. 상황
- 조건: TPS 500 × 60s (트래픽 body size = 4.3kb)
- Loader 결과: 29,761 요청 전송, 100% 성공
2-2. 관측
- 클라이언트(loader) 정상, 서버상태는 극히 정상
- 손상은 agent 단일 파일. 같은 run 의 다른 파일은 정상
- gzip 자체는 decompress 가 일부 가능 — 앞부분은 valid gzip stream, 뒷부분이 이상한 상태 ->? 뭐임 압축파일 자체의 문제
2-3. 이 시점에 세운 가설 (나중에 틀렸음을 확인)
"파일이 특정 크기를 넘으면 뭔가 터진다"
이 가설에 기반해 duration 을 60s → 30s → 20s 로 축소 + profile 별 U 가중치 조정 으로 우회 가능한 것처럼 보였고, 일정 기간 "우회 성공" 판정으로 Phase B 실험을 진행했다.
3 테스트 후 결론
트래픽 자체를 저장하는 부분에서 문제가 생긴다. 즉
- hcap 손상 버그는 여전히 존재
- 재현 프로토콜을 고쳐야 정확한 현상 관측 가능
4. 재현 프로토콜 재설계 — robust_v2
4-1. 문제 정의
트래픽 자체가 모이고 그거를 잘 확인하고 있는지 부터가 문제
4-2. 재검증 매트릭스
robust_v2 프로토콜로 재실행한 결과:
| medium_r1 | small | 1000 | 30s | ✅ 정상 | 0% |
| xlarge_v2r1 | xlarge | 1000 | 120s | ❌ 2 파일 손상 | 94% |
| medium_v2r1 | medium | 1000 | 120s | ❌ 2 파일 손상 | 94% |
| small_v2r1 | small | 1000 | 120s | ❌ 2 파일 손상 | 90% |
| small_tps300 | small | 300 | 120s | ❌ 손상 | — |
이 매트릭스가 보여주는 것:
- body profile 독립적 — small(95KB) 에서도 재현
- TPS 독립적 — TPS 300 에서도 재현
- duration 독립적이 아님 — 짧으면(30s) 파일이 특정 크기에 못 미쳐서 우연히 안 깨지는 것뿐
즉 "body/TPS/duration 특정 조합" 이 아니라 "한 minute 누적 파일이 ~19 MB 에 도달" 이라는 단일 조건 이 트리거. 이전의 인과 모델은 틀렸고 새 인과 모델은 단순.
5. Clean binary 검증 — 배포/빌드 문제 기각
5-1. 의심
테스트 중인 agent 가 진짜로 commit 964dafc 에서 빌드된 것인지 확신할 수 없는 상태. dirty flag 가 붙어있거나, 다른 branch 에서 나온 바이너리일 가능성도 있음.
5-2. 조치
5-3. 재테스트 결과
small × TPS 300 × 120s 로 재현 → 동일 증상. Clean binary (964dafc_clean) 에서도 파일이 정확히 같은 방식으로 손상됨.
5-4. 이 단계의 결론
- 배포된 바이너리와 소스의 불일치 아님
- dirty build 아님
- 버그는 commit 964dafc 의 develop branch 에 실재
6. 포렌식 샘플 분석 — 3-region 패턴
이전 회사에서 간단히 했던 포렌식(hex 파일 분석)
6-1. 샘플 확보
재현에 성공한 손상 파일 3 개 + 대조군 1 개를 로컬에 보존:
| sample1_xlarge_3pct_30s_dirty_agent.gz | xlarge × 30s | 20.9 MB | 손상 |
| sample2_small_3pct_tps300_120s_dirty_agent.gz | small × TPS300 × 120s | 17.9 MB | 손상 |
| sample3_small_3pct_tps300_120s_clean_agent.gz | small × TPS300 × 120s × clean binary | 18.5 MB | 손상 |
| reference-healthy/healthy_tail_from_same_run_as_sample3.gz | sample3 와 같은 run 의 다음 minute 파일 | 8.05 MB | 완전 정상 |
reference-healthy/ 는 분석의 핵심 대조군이다. 같은 설정으로 생성됐는데 한 파일은 깨지고 한 파일은 멀쩡. 이 비대칭이 "하드웨어/OS/라이브러리 통째 문제" 가설을 곧바로 기각한다.
6-2. 구조 분석 스크립트
각 파일의 바이트 레벨 구조 스캔. 표준 zlib.decompressobj 로 첫 gzip stream 을 디코드하고 unused_data 의 시작점을 찾아 Region 분할.
6-3. 3-region 패턴
모든 손상 파일이 정확히 동일한 3-region 구조:
상태
| sample1 (xlarge 30s) | 19.97 MB | 7.25 MB | 13.05 MB | 7,314 B | ❌ |
| sample2 (small 120s dirty) | 17.97 MB | 3.21 MB | 14.90 MB | 880 B | ❌ |
| sample3 (small 120s clean) | 18.46 MB | 1.01 MB | 17.50 MB | 4,297 B | ❌ |
| healthy tail (same run as sample3) | 8.06 MB | 8.45 MB | 0 | 0 | ✅ |
6-4. 결정적 관찰 네 가지
- Region A 의 trailer 가 정상 — CRC32 와 ISIZE 가 맞음. 즉 gzip.Close() 가 성공적으로 완료 된 적이 있다.
- Region B 가 sparse hole — 파일시스템 (XFS) 관점에서 blocks * 512 << size 상태. 이건 특정 syscall(lseek, ftruncate, fallocate, pwrite) 중 하나가 호출돼야만 생길 수 있는 파일 형태.
- Region C 가 완전한 deflate fragment — gzip magic byte 없이 compressed data block 만 존재. 즉 "gzip writer 가 새로 열린 건 아니고, 어떤 컨텍스트에서 raw deflate 데이터가 일부 써진 것".
- 같은 run 에서 정상/손상 공존 — sample3 (minute N) 과 reference-healthy (minute N+1) 가 같은 agent 프로세스의 연속된 두 minute. 앞 minute 은 깨지고 뒤 minute 은 완전히 정상. 이건 전송 경계에서만 발동하는 code path 라는 강력한 증거.
6-5. 이 단계의 결론
- 파일 포맷 레벨 손상이 확인됨 (capture 층 문제 아님)
- 3-region 패턴이 일관됨 → 동일 원인
- sparse hole 은 특정 syscall 이 호출돼야만 생김 → 어떤 syscall 이, 언제 호출됐는지 추적 필요
- 일반 로그만으로는 syscall 레벨 원인을 잡을 수 없음 → 시스템콜 레벨 트레이싱 이 필수
7. 포렌식 단계에서 기각된 가설들 (중복 재현 방지용)
아래는 이 과정에서 한 번씩 의심해본 뒤 사실 확인으로 기각된 가설 목록. 이후 단계에서 다시 파지 않기 위해 정리.
기각 근거
| 1 | "Loader 타이밍 문제" | robust_v2 프로토콜 (capture 활성화 확증) 로도 재현 |
| 2 | "Dirty build / 배포 이슈" | 964dafc_clean 로컬 cross-compile 바이너리로도 재현 |
| 3 | "Body 크기 때문" | small profile (95 KB) 로도 재현 |
| 4 | "고 TPS 때문" | TPS 300 로도 재현 |
| 5 | "test 서버 saveStore 블록" | v2 서버 (throttled async) 이식 완료됨. 서버 측 병목 없음 |
| 6 | "AF_PACKET 패킷 드롭" | 파일 포맷 레벨 손상이므로 capture 층 아님. 드롭이라면 gzip format 은 valid 가 유지되어야 함 |
| 7 | "Size-based rotation (128 MB) 오발동" | 손상 파일 크기는 17~20 MB, 128 MB 임계 훨씬 아래 |
| 8 | "하드웨어 / OS / 파일시스템 / 라이브러리 통째 문제" | 같은 run 의 다음 minute 파일은 완전 정상 (reference-healthy) |
8. 재현 가능성 확증
8-1. 재현율
- robust_v2 프로토콜 적용 후 4 번 시도, 4 번 재현. 100%.
- 재현 조건: hcap 파일이 한 minute 안에 ~19 MB 에 도달할 만큼의 트래픽.
- 최소 재현 스펙: small profile × TPS 300 × 120s (낮은 부하에서도 120 초면 파일 깨짐)
8-2. 재현 비용
- 테스트 1 회 ≈ 5 분 (trigger 30s + capture pickup 30s + loader 120s + buffer 150s)
- 분석 1 회 ≈ 10 분 (파일 가져오기 + test 실행)
- 합계 15 분 → root cause 탐색 사이클이 빠름
9. 포렌식 단계 결론
이 시점까지 확정된 것:
- ✅ 버그 실재 — 테스트 프로토콜 버그 아님. 코드 레벨 버그 확정.
- ✅ 재현율 100% — robust_v2 프로토콜 + 최소 스펙 (small × TPS 300 × 120s) 로 확정.
- ✅ 배포/빌드 이슈 아님 — clean binary 에서도 동일 재현.
- ✅ 파일 포맷 레벨 손상 — 3-region 패턴 (valid gzip + sparse hole + deflate fragment).
- ✅ sparse hole 존재 — XFS 의 blocks * 512 << size. 특정 syscall 이 호출됐다는 직접 증거.
- ✅ Rotation 경계 발동 — 같은 run 의 다음 minute 파일이 완전 정상이라는 대조군 증거.
아직 답하지 못한 것:
- ❓ sparse hole 을 만든 syscall 이 정확히 무엇인가?
- ❓ 어떤 코드 경로에서 그 syscall 이 호출됐는가?
- ❓ Rotation 경계에서만 발동하는 이유는?
이 세 질문에 답하려면 syscall 레벨 실시간 추적 이 필요할듯함.
'회사' 카테고리의 다른 글
| 2. ebpf 활용 이슈 해결 (0) | 2026.04.28 |
|---|---|
| 이직 이후 eBPF 활용기 연습 1일차 (0) | 2026.03.21 |
| [TIL 54] 패턴 탐지 검사에 대하여 (2) | 2025.05.09 |
| [TIL 50] windows defender로 인한 파일 삭제 (0) | 2025.03.13 |
| [TIL 49] python subprocess의 대한 문제직면 (0) | 2025.02.26 |