Dataflow는 단 한 번의 레코드 처리를 지원합니다. 이 페이지에서는 낮은 지연 시간을 보장하면서도 Dataflow가 단 한 번 처리를 구현하는 방법을 설명합니다.
개요
일괄 파이프라인은 항상 단 한 번 처리를 사용합니다. 스트리밍 파이프라인은 기본적으로 단 한 번 처리를 사용하지만 한 번 이상 처리를 사용할 수도 있습니다.
단 한 번 처리는 각 파이프라인 스테이지의 결과를 포함하여 레코드 처리 결과에 대한 보장을 제공합니다. 특히 소스의 파이프라인에서 도착하거나 이전 스테이지에서 스테이지에 도착하는 각 레코드에 대해 Dataflow가 다음을 보장합니다.
- 레코드가 처리되고 손실되지 않습니다.
- 파이프라인 내에 있는 처리 결과가 최대 한 번 반영됩니다.
즉, 레코드가 한 번 이상 처리되고 결과가 정확히 한 번 커밋됩니다.
단 한 번 처리는 출력에 중복된 레코드 없이 정확한 결과를 보장합니다. Dataflow는 단 한 번 처리 시맨틱스를 유지하면서 지연 시간을 최소화하도록 최적화되어 있습니다. 하지만 단 한 번 처리는 중복 제거 수행을 위한 비용이 발생합니다. 중복 레코드를 허용할 수 있는 사용 사례에서는 한 번 이상 처리 모드를 사용 설정하여 비용을 줄이고 지연 시간을 개선할 수 있습니다. 단 한 번 처리와 한 번 이상 처리 중에서 선택에 대한 자세한 내용은 파이프라인 스트리밍 모드 설정을 참조하세요.
지연 데이터
단 한 번 처리는 파이프라인의 정확성을 보장합니다. 파이프라인이 레코드를 처리할 때 Dataflow는 레코드가 출력에 반영되고 레코드가 중복되지 않도록 보장합니다.
하지만 스트리밍 파이프라인에서 단 한 번 처리는 레코드가 늦게 도착할 수 있기 때문에 완전한 결과를 보장할 수 없습니다. 예를 들어 파이프라인이 Count
와 같이 일정 기간 동안 집계를 수행한다고 가정해 보세요. 단 한 번 처리의 경우 해당 기간 내에 도착하는 레코드에 대해 결과가 정확하지만 지연된 레코드가 삭제될 수 있습니다.
일반적으로 이론상 레코드가 임의로 늦게 도착할 수 있기 때문에 스트리밍 파이프라인에서 완전성을 보장할 수 있는 방법이 없습니다. 제한적인 상황에서는 결과를 생성하기 위해 영원히 기다려야 합니다. 보다 실질적으로 Apache Beam에서는 지연 데이터를 삭제하기 위한 기준점과 집계된 결과를 배출할 시기를 구성할 수 있습니다. 자세한 내용은 Apache Beam 문서의 워터마크 및 지연 데이터를 참조하세요.
부작용
부작용이 항상 단 한 번의 처리에 일어나지는 않습니다. 싱크가 단 한 번의 처리 시맨틱스를 구현하지 않는 한 이러한 부작용에는 외부 저장소에 데이터를 기록하는 것도 포함된다는 것이 중요합니다.
특히 Dataflow는 각 레코드가 각 변환을 단 한 번만 통과한다고 보장하지 않습니다. 재시도 또는 작업자 오류로 인해 Dataflow는 레코드를 변환에 여러 번 전송하거나 여러 작업자에서 동시에 수행할 수도 있습니다.
단 한 번의 처리에 따라 Dataflow는 출력에서 중복을 삭제합니다. 하지만 변환의 코드에 부작용이 있으면 이러한 효과가 여러 번 발생할 수 있습니다. 예를 들어 변환으로 원격 서비스 호출이 수행될 때 이 호출은 동일한 레코드에 대해 여러 번 수행될 수 있습니다. 부작용은 일부 상황에서 데이터 손실로 이어질 수도 있습니다. 예를 들어 변환이 출력 생성을 위해 파일을 읽고, 출력이 커밋될 때까지 기다리지 않고 즉시 파일을 삭제한다고 가정해 보세요. 결과를 커밋할 때 오류가 발생하면 Dataflow가 변환을 다시 시도하지만 이번에는 변환이 삭제된 파일을 읽을 수 없습니다.
로깅
처리 로그 출력은 처리가 발생했음을 나타내지만 데이터가 커밋되었는지 여부를 나타내지 않습니다. 따라서 처리된 데이터의 결과가 영구 스토리지에 한 번만 커밋되었더라도 데이터가 여러 번 처리된 것으로 로그 파일에 표시될 수 있습니다. 또한 로그에 항상 처리 및 커밋된 데이터가 반영되지는 않습니다. 스로틀링으로 인해 로그가 삭제되거나 다른 로깅 서비스 문제로 인해 손실될 수 있습니다.
단 한 번 스트리밍
이 섹션에서는 Dataflow가 비확정적 처리, 늦은 데이터, 커스텀 코드와 같은 복잡한 항목을 관리하는 방법을 포함하여 Dataflow가 스트리밍 작업에 대해 단 한 번 처리를 구현하는 방법을 설명합니다.
Dataflow 스트리밍 셔플
스트리밍 Dataflow 작업은 각 작업자에 작업 범위를 지정하여 여러 다른 작업자에서 병렬로 실행됩니다. 작업자 오류, 자동 확장, 기타 이벤트에 대한 응답으로 시간이 지남에 따라 지정이 변경될 수 있지만 각 GroupByKey
변환 이후 키가 동일한 모든 레코드가 동일한 작업자에서 처리됩니다. GroupByKey
변환은 Count
, FileIO
등의 복합 변환에 사용되는 경우가 많습니다. 지정된 키에 대한 레코드가 동일한 작업자에게 전달되기 위해 Dataflow 작업자는 리모트 프로시져 콜(RPC)을 사용하여 이들 간에 데이터를 셔플합니다.
셔플 중 레코드가 손실되지 않도록 보장하기 위해 Dataflow에는 업스트림 백업이 사용됩니다. 업스트림 백업을 사용하면 레코드를 전송하는 작업자가 레코드 수신에 대한 긍정 확인이 수신될 때까지 RPC를 재시도합니다. 레코드 처리의 부작용은 영구 스토리지 다운스트림에 커밋됩니다. 레코드를 전송하는 작업자가 사용할 수 없게 되면 Dataflow가 RPC를 계속 재시도하여 모든 레코드가 한 번 이상 전송되도록 보장합니다.
이러한 재시도로 인해 항목이 중복될 수 있기 때문에 모든 메시지에는 고유 ID가 태그 지정됩니다. 각 수신자는 이미 표시되어 처리된 모든 ID 카탈로그를 저장합니다. 레코드가 수신되면 Dataflow가 카탈로그에서 해당 ID를 조회합니다. ID가 발견되면 레코드가 이미 수신되어 커밋된 것이고, 중복 항목으로 삭제됩니다. 레코드 ID의 안정성을 보장하기 위해 단계별 모든 출력이 스토리지에 체크포인트됩니다. 그 결과 반복된 RPC 호출로 인해 동일한 메시지가 여러 번 전송된 경우 메시지가 스토리지에 한 번만 커밋됩니다.
낮은 지연 시간 보장
단 한 번 처리할 수 있게 하려면 특히 모든 레코드에서 I/O를 방지하여 I/O를 줄여야 합니다. 이 목적을 달성하기 위해 Dataflow는 블룸 필터 및 가비지 컬렉션을 사용합니다.
블룸 필터
블룸 필터는 신속한 세트 멤버십 검사를 가능하게 하는 소형 데이터 구조입니다. Dataflow에서 각 작업자는 표시되는 모든 ID의 블룸 필터를 유지합니다. 새 레코드 ID가 도착하면 작업자가 필터에서 ID를 조회합니다. 필터가 false를 반환하면 이 레코드가 중복된 것이 아니고, 작업자가 안정적인 스토리지에서 ID를 조회하지 않습니다.
Dataflow는 연속적인 블룸 필터 집합을 시간별 버킷으로 유지합니다. 레코드가 도착하면 Dataflow가 시스템 타임스탬프를 기준으로 검사할 적합한 필터를 선택합니다. 이 단계에서는 필터가 가비지 수집될 때 블룸 필터가 포화되는 것을 방지하고 시작 시 검색해야 하는 데이터 양을 제한합니다.
가비지 컬렉션
스토리지가 레코드 ID로 채워지지 않도록 Dataflow는 가비지 컬렉션을 사용하여 오래된 레코드를 삭제합니다. Dataflow는 시스템 타임스탬프를 사용하여 가비지 컬렉션 워터마크를 계산합니다.
이 워터마크는 제공된 단계에서 대기하는 데 걸린 물리적 시간을 기반으로 합니다. 따라서 파이프라인 중 속도가 느린 부분에 대한 정보를 제공합니다. 이 메타데이터는 Dataflow 모니터링 인터페이스에 표시되는 시스템 지연 측정항목의 기초입니다.
워터마크보다 오래된 타임스탬프로 레코드가 도착하고 이 시간의 ID가 이미 가비지 수집된 경우에는 레코드가 무시됩니다. 레코드 도착이 확인될 때까지 가비지 컬렉션을 트리거하는 낮은 워터마크가 진행되기 않기 때문에 이러한 늦은 도착 레코드는 중복됩니다.
비확정적 소스
Dataflow는 Apache Beam SDK를 사용하여 데이터를 파이프라인으로 읽습니다. 처리가 실패하면 Dataflow가 소스에서 읽기를 재시도할 수 있습니다. 이 경우 Dataflow는 소스에서 생성된 모든 고유 레코드가 단 한 번만 기록되도록 보장해야 합니다. Pub/Sub Lite 또는 Kafka와 같은 확정적 소스의 경우 레코드가 기록된 오프셋을 기준으로 읽혀져서 이 단계를 수행할 필요가 없습니다.
Dataflow가 레코드 ID를 자동으로 할당할 수 없으므로, 비확정적 소스는 Dataflow에 중복 방지를 위해 레코드 ID가 무엇인지 알려줘야 합니다. 소스가 각 레코드에 대해 고유 ID를 제공할 때 커넥터는 파이프라인에서 셔플을 사용하여 중복 항목을 삭제합니다. ID가 동일한 레코드가 필터링됩니다. Pub/Sub를 소스로 사용할 때 Dataflow가 단 한 번 처리를 구현하는 방법에 대한 예시는 Pub/Sub로 스트리밍 페이지에서 정확히 한 번 처리 섹션을 참조하세요.
파이프라인의 일부로 커스텀 DoFn
을 실행할 때 Dataflow는 이 코드가 레코드당 한 번만 실행되도록 보장하지 않습니다. 작업자 실패 시 한 번 이상 처리를 보장하기 위해 Dataflow는 제공된 레코드를 변환을 통해 여러 번 실행하거나 동일한 레코드를 여러 작업자에서 동시에 실행할 수 있습니다. 파이프라인에 외부 서비스 연락 등을 수행하는 코드를 포함하면 제공된 레코드에 대해 작업이 한 번 넘게 실행될 수 있습니다.
비확정적 처리를 확정적으로 만들려면 체크포인트를 사용합니다. 체크포인트를 사용하면 다음 단계로 전달되기 전에 변환의 각 출력이 고유 ID가 있는 안정적인 스토리지로 체크포인트됩니다. Dataflow의 셔플 전송을 재시도하면 체크포인트된 출력이 릴레이됩니다. 코드가 여러 번 실행될 수 있지만 Dataflow는 이러한 실행 중 하나의 출력만 저장되도록 보장합니다. Dataflow는 중복 항목이 안정적인 스토리지에 기록되지 않도록 방지하는 일관적인 저장소를 사용합니다.
단 한 번만 출력 제공
Apache Beam SDK에는 중복 항목을 생성하지 않도록 디자인된 기본 제공 싱크가 포함되어 있습니다. 가능하면 다음 기본 제공 싱크 중 하나를 사용하세요.
자체 싱크를 작성해야 할 경우 가장 좋은 접근 방법은 의도치 않은 부작용을 일으키지 않고 필요한 만큼 자주 재시도할 수 있도록 함수 객체를 멱등적으로 만드는 것입니다. 그럼에도 불구하고 싱크 기능을 구현하는 변환의 일부 구성요소는 비확정적인 경우가 많으며, 재시도 시 변경될 수 있습니다.
예를 들어 기간이 지정된 집계에서 해당 기간의 레코드 세트는 비확정적일 수 있습니다. 특히 해당 기간이 e0, e1, e2 요소로 실행을 시도할 수 있습니다. 기간 처리를 커밋하기 전에 하지만 해당 요소가 부작용으로 전송된 이후에 작업자가 충돌할 수 있습니다. 작업자가 다시 시작되면 기간이 다시 실행되고 늦은 요소인 e3가 도착합니다. 이 요소는 기간이 커밋되기 전에 도착하기 때문에 늦은 데이터로 계산되지 않으며, 따라서 다시 e0, e1, e2, e3 요소를 사용하여 DoFn
이 호출됩니다. 그런 다음 이러한 요소가 부작용 작업으로 전송됩니다. 이 시나리오에서는 서로 다른 논리적 레코드 세트가 매번 전송되기 때문에 멱등성이 도움이 되지 않습니다.
Dataflow에서 비확정성을 해결하기 위해서는 기본 제공되는 Reshuffle
변환을 사용합니다. Dataflow가 데이터를 셔플할 때 Dataflow는 셔플 발생 후 작업을 재시도할 경우 모든 비확정적으로 생성된 요소가 안정적이도록 데이터를 영구적으로 기록합니다. Reshuffle
변환을 사용하면 DoFn
의 출력 중 하나의 버전만 셔플 경계를 벗어나게 만들 수 있습니다.
다음 패턴은 부작용 작업이 항상 출력할 결정적 레코드를 수신하도록 보장합니다.
c.apply(Window.<..>into(FixedWindows.of(Duration.standardMinutes(1))))
.apply(GroupByKey.<..>.create())
.apply(new PrepareOutputData())
.apply(Reshuffle.<..>of())
.apply(WriteToSideEffect());
Dataflow 러너가 DoFn
을 실행하기 전에 요소가 안정적이어야 함을 알 수 있게 하려면 RequiresStableInput
주석을 DoFn
에 추가합니다.
자세히 알아보기
- 파이프라인 스트리밍 모드 설정
- Pub/Sub로 스트리밍
- 스트리밍 엔진: 높은 확장성, 지연 시간이 짧은 데이터 처리를 위한 실행 모델
- Apache Beam 실행 모델 자세히 알아보기
- 람다 이후: Dataflow에서 단 한 번 처리, 1부
- 람다 이후: Dataflow에서 단 한 번 처리, 2부(짧은 대기 시간 보장)
- 람다 이후: Dataflow에서 단 한 번 처리, 3부(소스와 싱크)