Keyboard shortcuts

Press ← or → to navigate between chapters

Press ? to show this help

Press Esc to hide this help

증분 파이프라인의 장기 성능 유지

새 데이터만 처리하더라도 작은 파일과 트랜잭션이 계속 쌓이면 데이터셋의 현재 뷰를 읽고 쓰는 비용이 증가합니다. 수만 또는 수십만 번의 작은 갱신이 누적되면 수주나 수개월 뒤에 성능 저하가 나타날 수 있으며, 실제 시점은 실행 빈도에 따라 달라집니다.

여기서 다루는 대상은 일반 데이터셋에 파일과 행을 누적하는 파이프라인입니다. 증분 출력 API의 replace 모드나 Iceberg의 행 단위 변경을 포함하여 모든 증분 실행이 추가만 가능하다는 뜻은 아닙니다.

파일 수와 트랜잭션 수 구분

누적 상태영향
파일 수 증가현재 뷰의 파일을 조회하는 백엔드 요청이 느려지고 미리보기, Contour와 변환이 영향을 받습니다. Python 환경의 파일 적재 부담도 커질 수 있습니다.
트랜잭션 수 증가파일 조회뿐 아니라 뷰 범위 해석, 통계와 이력 조회가 느려집니다. Spark 실행 정보가 생기기 전 환경 준비 단계에서 지연이 나타날 수 있습니다.

파일이 많으면 분 단위 변환이 수시간 또는 수일로 늘어날 수 있고, 극단적인 경우 백엔드 요청이 시간 초과되어 빌드가 실패할 수 있습니다. APPEND 또는 UPDATE 트랜잭션에 Spark DataFrame을 쓸 때도 현재 출력 뷰의 기존 파일 목록을 메모리에 구성해야 하므로 시간이 걸리고 드라이버 메모리가 부족해질 수 있습니다. 이 경우 드라이버 메모리가 큰 Spark 프로필이 필요할 수 있습니다.

두 개수는 별도로 확인합니다. 빈 트랜잭션이 많으면 파일 수가 적어도 이력 해석이 느려질 수 있고, 한 트랜잭션에 파일이 많으면 트랜잭션 수가 적어도 파일 문제가 발생합니다.

정기적인 전체 스냅샷

전체 입력을 다시 처리하여 새 SNAPSHOT 출력으로 대체하면 현재 뷰를 새 파일 집합으로 정리하고 효율적으로 파티션할 수 있습니다. 반복 계산 비용은 크지만 가끔 전체 재계산을 수행하여 한 뷰에 파일과 트랜잭션이 계속 쌓이는 것을 줄일 수 있습니다. 빈도는 재계산 비용과 읽기 및 쓰기 성능 사이에서 정합니다.

일반적인 증분 입력이 스냅샷으로 바뀌면 그 입력을 사용하는 변환도 비증분 실행이 필요하다고 판단할 수 있습니다. 이를 이용하여 원하는 정리 주기에 항상 스냅샷으로 빌드되는 보조 입력을 두고 파이프라인이 전체 재계산으로 전환되도록 구성할 수 있습니다. snapshot_inputs나 강제 증분 설정처럼 이 판단에 영향을 주는 예외는 함께 확인합니다.

이 방법에는 제약이 있습니다.

  • 보조 입력이 계보, 검색과 폴더에 나타나 구조가 복잡해집니다.
  • 드물게 실행해도 전체 재계산 비용이 클 수 있습니다.
  • 스냅샷을 처리하는 동안 후속 데이터 전달이 지연되어 SLA에 영향을 줄 수 있습니다.
  • Data Connection 동기화가 직접 관리하는 원시 데이터셋의 누적 상태를 이 방법으로 정리할 수는 없습니다.
  • 변환이 비증분 실행을 올바르게 처리하도록 작성되어 있어야 합니다.

데이터셋 프로젝션

누적 상태의 읽기 비용을 줄이는 권장 방법은 데이터셋 프로젝션(dataset projection)을 추가하는 것입니다. 프로젝션은 원본 데이터셋과 별도로 저장하고 갱신하는 조회용 표현입니다. 원본의 추가 전용 파일 구성을 그대로 유지할 필요가 없어 내부 데이터를 재구성하는 컴팩션을 수행할 수 있습니다.

프로젝션이 원본의 최신 상태까지 완성되어 있지 않아도 사용할 수 있습니다. 읽기는 프로젝션에 들어 있는 구간과 원본에만 있는 나머지 구간을 결합하여 같은 데이터셋 뷰를 재구성합니다. 예를 들어 추가 트랜잭션 100개 중 처음 99개가 프로젝션에 있으면 그 99개는 프로젝션에서 읽고 마지막 한 개만 원본에서 읽습니다. 이 때문에 매번이 아니라 일별 또는 주별 갱신만으로도 효과를 얻을 수 있습니다.

원본과 별도 리소스이므로 양쪽 빌드를 동시에 수행할 수 있습니다. 예를 들어 원본의 트랜잭션 9까지 커밋되었고 프로젝션은 1~8을 담은 상태에서, 원본의 트랜잭션 10과 새 프로젝션 빌드가 동시에 진행된다고 가정합니다. 프로젝션 빌드는 커밋된 9까지를 읽을 수 있고, 현재 독자는 기존 프로젝션의 1~8과 원본의 9를 결합하여 읽습니다. 아직 진행 중인 10을 완료된 데이터처럼 읽지 않습니다.

제약은 다음과 같습니다.

  • 원본 외에 별도 저장 공간이 필요합니다.
  • Foundry Retention과 별도 구성 없이 바로 함께 사용하는 방식은 지원되지 않습니다.
  • 원본 파일과 트랜잭션 자체를 정리하지 않습니다. 프로젝션을 제거하면 원본을 읽는 비용이 다시 커질 수 있습니다.

보존 정책

업무에서 최근 데이터만 필요하면 Foundry Retention으로 오래된 트랜잭션을 제거할 수 있습니다. 다만 트랜잭션 사이에 집계나 차분 계산 같은 의존 관계가 없어야 합니다.

Python 증분 데코레이터의 allow_retention=True는 Foundry Retention이 수행한 삭제 때문에 증분 실행이 무효화되지 않도록 하는 설정입니다. 기본값은 False이며, 허용하지 않은 삭제는 비증분 실행을 유발할 수 있습니다. 일반 사용자 삭제를 허용하는 별도 설정과 구분합니다.

보존 기간을 줄이면 과거 데이터를 잃습니다. 각 트랜잭션이 독립적인 업무 단위가 아니면 상태도 불일치할 수 있습니다. 예를 들어 시작 이벤트만 삭제되고 종료 이벤트가 남으면 두 이벤트를 결합하는 계산이 올바르지 않을 수 있습니다.

빈 출력 트랜잭션 중단

파일이 없거나 빈 파일만 있는 트랜잭션도 유효한 갱신으로 커밋되면 스케줄과 후속 처리를 일으킬 수 있습니다. 가능한 한 원천에서 이런 갱신을 줄입니다.

Data Connection은 파일이 없는 트랜잭션을 중단하지만 빈 파일을 생성할 수는 있습니다. 사용자 지정 플러그인은 증분 메타데이터를 갱신하려고 비어 있지 않은 파일을 포함해야 하는 경우도 있어 플러그인만으로 해결하기 어려울 수 있습니다. 변환이나 다른 방식에서는 파일 없는 트랜잭션을 커밋할 수도 있습니다.

가장 상위 변환에서 입력 또는 출력이 비어 있음을 확인한 뒤 output.abort()로 해당 출력 트랜잭션을 중단하면 불필요한 갱신이 하위로 이어지는 것을 막을 수 있습니다. 이 메서드는 그 출력의 쓰기를 무시하는 동작이며, 전체 작업과 모든 출력을 중단하는 ctx.abort_job()과는 범위가 다릅니다.

빈 결과를 의도적으로 중단하는 방식은 작업을 실패시키는 방법과 다릅니다. 실패 통계를 늘리지 않으며 증분 입력의 처리 위치는 다음으로 진행합니다. 처리해야 할 입력이 있는데 중단하면 그 데이터를 건너뛸 수 있으므로, 선행 조건 실패 등 일반 오류를 감추는 수단으로 사용하지 않습니다.

변경 로그 데이터셋의 행 수

추가 전용 트랜잭션에 변경 로그를 기록하면 수정 의미를 표현하고 증분 조인 및 집계를 구현할 수 있습니다. 그러나 파일과 트랜잭션 수뿐 아니라 변경 이력을 표현하는 행 자체도 계속 늘어납니다. 현재 상태를 계산하는 단계나 전체 재계산에서 이 비용이 나타납니다.

스냅샷이 중간의 부분 상태를 정리할 수는 있지만, 행 수를 실질적으로 줄이려면 명확한 업무 키와 변경 순서에 따라 최신 상태로 축약해야 합니다. 과거 임의 시점의 상태가 필요한 업무에서는 모든 이력을 최신 행 하나로 줄이는 방식이 적합하지 않을 수 있습니다.

프로젝션은 파일 배치의 읽기 비용을 줄여도 업무상 필요한 변경 행을 자동으로 없애지는 않습니다. 보존 정책도 서로 의존하는 시작 및 종료 이벤트를 무조건 독립적으로 삭제할 수 없으므로, 변경 로그의 상태 보존 요구를 설계 단계에서 정해야 합니다.