첫 오픈소스 PR이 머지됐다.
대상은 open-telemetry/opentelemetry-dotnet, 이슈는 LogRecordSharedPool의 thread-safety 문제였다.
PR은 2026-02-17, 설날 당일에 main 브랜치로 들어갔다.
TL;DR
- 이슈 #6233 (새 창):
LogRecordSharedPool.Rent()가 동시성 상황에서 동일한LogRecord를 여러 스레드에 반환해, 로그 데이터가 섞이거나 중복 전송되는 버그 - PR #6833 (새 창):
TryRentCoreRare실패 시 루프를 이어가던 로직을 break로 바꾸고, 새LogRecord를 생성하도록 수정 - 타임라인: 이슈 오픈(2025-04-07) → PR 연결(2026-01-17) → 리뷰 반영(2026-01-30 전후) → 머지(2026-02-17)
1. 이슈: 로그가 섞여서 두 번 나간다
이슈 #6233 (새 창)의 내용은 이렇다.
The Rent() method of LogRecordSharedPool is not thread safe and this causes the same LogRecord to be given out multiple times and then the data from one is overwritten or combined into a single log which then gets submitted twice.
멀티스레드 환경에서 Rent()를 동시에 호출하면 같은 LogRecord 인스턴스를 두 스레드가 받아간다.
두 스레드가 각자의 데이터를 같은 객체에 쓰면, attribute가 뒤섞인 로그 하나가 두 번 전송된다.
이슈 작성자가 남긴 힌트가 있었다.
By forcing the use of the LogRecordThreadStaticPool this never happens.
ThreadStaticPool은 스레드마다 별도 인스턴스를 쓰니 당연히 재현이 안 된다.
이 힌트 하나로 원인을 공유 풀의 동시 접근으로 좁힐 수 있었다.
왜 이 문제가 발생하는가?
LogRecordSharedPool은 LogRecord 객체를 재사용하는 풀이다.
Rent()로 꺼내서 쓰고, 다 쓰면 Return()으로 돌려준다.
문제는 Rent() 내부의 TryRentCoreRare가 실패했을 때의 처리였다.
race condition으로 TryRentCoreRare가 실패하면, 기존 코드는 continue로 루프를 이어가며 새 인덱스를 시도했다.
이 과정에서 이미 다른 스레드가 가져간 슬롯을 다시 반환하는 경우가 생겼다.
2. 수정 방향
수정은 단순했다.
TryRentCoreRare가 실패하면 루프를 continue로 이어가지 말고, break로 끊고 새 LogRecord 인스턴스를 생성한다.
이렇게 하면 race condition이 발생해도 중복 반환 대신 새 객체를 만들어 돌려준다.
PR #6833 (새 창)의 Changes 요약:
- Race Condition 수정:
TryRentCoreRare실패 시continue→break로 변경, 새LogRecord생성 - 계산 최적화:
slotIndex계산을 추출해 중복 modulo 연산 제거 - 회귀 테스트 추가:
LogRecordSharedPoolTests.cs에RentShouldNeverReturnSameInstanceConcurrently추가
테스트에서 failure detail 수집은 ConcurrentQueue로 처리했다.
버그 자체가 동시성 문제인데, 테스트 코드가 thread-safe하지 않으면 테스트 결과를 신뢰할 수 없다.
// 멀티스레드 환경에서 List는 안전하지 않다
var failures = new List<string>();
// ConcurrentQueue로 race condition 없이 수집
var failures = new ConcurrentQueue<string>();3. Stale 봇, CHANGELOG, 63개 CI 체크
Stale 봇
중간에 봇이 PR을 stale로 표시했다.
수백 개의 PR이 열려 있는 레포에서 이런 자동화는 자연스러운 일이지만, 처음에는 당황스러웠다.
"활동 없으면 닫는다"는 문구를 보고 바로 코멘트를 달아 PR을 유지했다.
CHANGELOG 요구
리뷰어 martincostello가 남긴 피드백 중 하나가 이거였다.
Appropriate CHANGELOG.md files updated for non-trivial changes
코드는 고쳤더라도 CHANGELOG가 없으면 불완전하다는 관점이었다.
사용자가 "이 버전에서 무엇이 바뀌었는지" 추적할 수 있어야 한다는 뜻이다.
이 피드백으로 오픈소스도 사용자에게 배포되는 제품이라는 걸 이해했다.
63개 CI 체크
승인이 났다고 바로 머지되는 게 아니었다.
merge queue에 들어가서 CI 체크 63개를 통과해야 최종 머지가 이루어진다.
4. 설날에 머지됐다
PR #6833 (새 창)은 2026-02-17, rajkumar-rangaraj에 의해 main으로 머지됐다.
커밋은 84d0d3e, merge queue를 통해 들어갔다.
2026년 설날이 2/17이고 공식 연휴가 2/16~2/18이다.
"설날에 첫 오픈소스 PR이 머지됐다"는 말이 날짜까지 맞아떨어졌다.
5. 한계
코드 변경 자체보다 이슈 → PR → 리뷰 → CHANGELOG → merge queue → 머지까지의 흐름을 직접 경험한 것이 더 큰 수확이었다.
첫 PR이라 조건이 좋았다.
good first issue 라벨이 붙어 있었고, 수정 범위가 명확했고, 리뷰어도 친절했다.
더 복잡한 이슈, 더 많은 이해관계자가 얽힌 PR에서는 다른 어려움이 있을 것이다.
다음
한 번의 기여로 끝내지 않으려 한다.
다음에는 이슈 분석부터 테스트, 문서, CHANGELOG까지 한 번에 챙기는 방식으로 기여를 이어가는 것이 목표다.