티스토리 뷰
[Open source contribution] Apache Paimon 오픈소스 기여 경험기 - 인터페이스 기본값이 실제 값을 덮고 있었습니다 — Paimon GSFileIO 파일 상태 수정
ebson 2026. 9. 3. 13:35GCS 파일 상태가 0과 null을 돌려주고 있었습니다
Apache Paimon은 여러 클라우드 스토리지를 Hadoop 호환 파일시스템으로 감싸 다룹니다. 각 스토리지 구현은 파일 상태를 Paimon의 FileStatus 형태로 감싸 내보내는데, 구글 클라우드 스토리지용인 paimon-gs-impl에서 이 감싸는 과정에 빈틈이 있었습니다. 이번 글에서는 그것을 메운 apache/paimon#8559를 이야기하려 합니다.
문제는 HadoopCompliantFileIO 안의 HadoopFileStatus에 있었습니다. 이 클래스는 Hadoop의 FileStatus를 감싸, 경로·크기·디렉터리 여부·수정 시각 같은 값을 감싼 대상에 위임해 돌려줍니다. 그런데 접근 시각과 소유자를 돌려주는 메서드는 오버라이드하지 않고 있었습니다. 그러면 Paimon FileStatus 인터페이스의 기본 구현이 그대로 쓰이는데, 그 기본값은 접근 시각이 0, 소유자가 null입니다. 감싼 Hadoop FileStatus가 실제 값을 들고 있는데도, 기본값이 그 값을 덮어 버리는 상태였습니다.
파일 상태의 접근 시각과 소유자는 파일의 메타데이터로, 파일 목록을 조회하거나 상태를 들여다보는 쪽으로 전달됩니다. 접근 시각은 수정 시각과 마찬가지로 에포크 기준 밀리초로 표현되는 타임스탬프이고, 소유자는 파일을 소유한 주체를 가리키는 문자열입니다. FileStatus 인터페이스에서 소유자는 아예 널을 허용하도록 선언돼 있어, 오버라이드하지 않으면 null이 정상값처럼 흘러갑니다. 이 값이 실제 값 대신 0과 null로 나온다면, GCS 위에서 파일 메타데이터에 기대는 어떤 판단도 잘못된 정보를 받게 됩니다. 눈에 띄는 오류나 예외가 나는 것이 아니라, 조용히 틀린 값이 흘러간다는 점이 특히 신경 쓰였습니다.
다섯 형제는 이미 위임하고 있었습니다
이 빈틈이 억지스러운 가정이 아니라는 근거는 형제 구현들에 있었습니다. 같은 HadoopCompliantFileIO 구조를 쓰는 s3, oss, obs, cosn, azure 구현은 모두 접근 시각과 소유자를 감싼 상태에 위임하고 있었습니다. GS 구현만 이 두 메서드를 빠뜨려, 형제 사이의 일관성이 어긋난 상태였습니다.
이 위임 방식 자체는 core의 HadoopFileIO와 s3·oss에서 처음 자리 잡았고, 이후 각 스토리지 모듈로 하나씩 퍼져 나갔습니다. 그 확산 과정에서 GS 모듈만 두 메서드가 누락된 채 남아 있었던 것으로 보입니다.
감싼 상태에 위임하기
고치는 방향은 형제 구현과 똑같이, 두 메서드를 감싼 status에 위임하도록 오버라이드하는 것이었습니다.
// TO-BE
@Override
public long getAccessTime() {
return status.getAccessTime();
}
@Override
public String getOwner() {
return status.getOwner();
}
바로 위에 이미 있던 getModificationTime()이 status.getModificationTime()으로 위임하는 것과 같은 형태입니다. 더한 것은 이 두 메서드뿐이고, 감싸는 다른 로직은 건드리지 않았습니다.
이 변경으로 GCS에서 파일 상태를 조회할 때 접근 시각과 소유자가 인터페이스 기본값이 아니라 실제 값으로 돌아오게 됐습니다. 다른 다섯 스토리지 구현과 같은 방식으로 파일 상태를 내보내게 되어, 형제 사이의 일관성도 회복됐습니다.
규칙을 따라가며 확인한 것들
이 변경은 paimon-filesystems/paimon-gs-impl 모듈에 있습니다. 파일 상태를 감싸 내보내는 부분을 손댔지만, 저장되는 데이터의 포맷을 바꾸는 것이 아니라 이미 감싼 Hadoop 상태의 값을 그대로 전달하도록 두 접근자를 채운 것이라 변경 범위가 좁았습니다.
형제 구현이 같은 위임을 이미 하고 있었다는 점이 무엇을 어떻게 고칠지의 근거가 되었습니다. 손댈 자리와 방식, 그리고 값이 0·null이 아니라 실제 값이어야 한다는 기대까지 모두 형제 코드가 알려 주고 있었습니다. 수정이 옳은지 확인하는 방법도 형제 비교에서 나왔습니다. s3·oss·obs·cosn·azure 구현에서 이 두 메서드가 어떻게 위임하는지 나란히 놓고, GS의 새 오버라이드가 같은 형태인지 대조했습니다.
Paimon은 빌드에 Maven을 쓰고 저는 바꾼 모듈만 스코프로 잡아 검증했으며, 포맷은 mvn spotless:apply로 정렬한 뒤, 위반이 남으면 빌드를 실패시키는 체크스타일까지 통과하는지 확인했습니다.
squash 머지라 PR 제목이 곧 커밋 메시지가 되어 [module] Description 형식을 지켜야 해서, 제목을 [fs] Fix GSFileIO to return actual file access time and owner로 정했습니다. 본문은 템플릿의 ### Purpose와 ### Tests 두 섹션으로 나눠, 어느 형제 구현들이 이미 이 위임을 하고 있었는지와 이 위임이 core에서 어떻게 시작됐는지를 함께 적어 근거를 드러냈습니다.
기본값이 값을 덮는 자리를 알아보기
돌아보면 이 버그는 잘못된 코드를 쓴 것이 아니라, 필요한 코드를 빠뜨린 데서 왔습니다. 두 메서드를 오버라이드하지 않으면 인터페이스 기본값이 조용히 자리를 채우기 때문에, 컴파일도 되고 실행도 되지만 값만 틀리는, 눈에 잘 안 띄는 종류의 문제가 됩니다. 감싸는 클래스는 감싼 대상의 어떤 값을 내보내고 어떤 값을 빠뜨리는지 하나씩 확인해야 온전하다는 것을 실제로 해 보며 알게 됐습니다.
닮은 구현들이 나란히 있을 때 그중 하나만 메서드가 빠져 있으면 그 자리가 대개 손볼 지점이라는 것도 다시 확인했습니다. 형제 코드를 나란히 놓고 어떤 메서드가 있고 없는지를 비교하는 일이, 이런 누락형 버그를 찾아내는 꽤 믿을 만한 방법이라는 감각이 남았습니다.
'OPEN SOURCE' 카테고리의 다른 글
- Total
- Today
- Yesterday
- 캐시 성능 비교
- Java Performance
- 스레드 생명주기
- Redis 성능 개선
- 동시성처리
- Enum 기반 싱글톤
- Redis vs DB
- Eager Initialization
- Redis 캐시 전략
- 백엔드 성능 설계
- 백엔드 성능
- 캐시와 인덱스
- 트래픽 처리
- Cache Avalanche
- 트랜잭션 관리
- spring batch 5
- Cache Penetration
- DB 인덱스 성능
- Hot Key 문제
- DB 트랜잭션
- Initialization-on-Demand Holder Idiom
- Cache Aside
- TTL 설계
- 백엔드 아키텍처
- 백엔드 성능 튜닝
- 캐시 장애
- mybatis
- Double-Checked Locking
- InterruptedException
- Spring Batch
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 |

