Quantcast
Channel: Charsyam's Blog
Viewing all articles
Browse latest Browse all 122

[입 개발] Kafka 와 Spark Structured Streaming 에서 checkpoint 에서 아주 과거의 Offset이 있으면 어떻게 동작할까?

$
0
0

최근에 Kafka와 Spark Structured Streaming 코드를 작성할 일이 있어서 작업을 하는데, 이상한 에러를 만났습니다. 그렇습니다. 여러분 저는 Spark Structured Streaming 이 처음이라… 흑 초초보의 슬픔이네요.

에러는 다음과 같았습니다. empty 인 DataFrame을 쓸 수 없다는 것인데요.

21/03/05 07:14:04 ERROR MicroBatchExecution: Query [id = c80a0c8a-b6fe-4961-ae6c-a79a880cb369, runId = e9993a73-6579-4cd9-9ab4-fdbd2995ae6d] terminated with error
org.apache.spark.sql.AnalysisException: 
Datasource does not support writing empty or nested empty schemas.
Please make sure the data schema has at least one or more column(s).
         ;
	at org.apache.spark.sql.execution.datasources.DataSource$.validateSchema(DataSource.scala:950)
	at org.apache.spark.sql.execution.datasources.DataSource.planForWriting(DataSource.scala:595)
	at org.apache.spark.sql.DataFrameWriter.saveToV1Source(DataFrameWriter.scala:437)
	at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:421)
	at org.apache.spark.sql.DataFrameWriter.save(DataFrameWriter.scala:294)
	at org.apache.spark.sql.DataFrameWriter.parquet(DataFrameWriter.scala:884)

분명히 카프카에는 데이터가 계속 들어오고 있는 상황인데, 이유를 알 수 없어서, 몇 시간을 날렸는데… 바꾼 다른 코드의 문제인가 싶어서 이것저것 바꿔보고, 컴파일 And 실행의 반복만…(플랫폼을 EMR에서 Databricks로 그리고 Spark 2.x 에서 3.0.1로 바꾸고 Scala 버전도 2.11 에서 2.12로 바뀌면서 이래저래 이슈가 좀 있었는데… 그 탓인줄…)

그러다가 우연히… CheckPoint를 지우고 나니… 정상적으로 실행이 잘 되는것이었습니다. 그 때 든 생각은 아, 뭔가 Offset에 문제가 있구나라는 생각이 들었습니다. 사실 이 문제가 발생하는 조건을 분석한 다음에 보면, 어떻게 보면 당연한 문제인데요. 문제가 되었던 상황은 같은 토픽으로 checkpoint 가 만들어졌는데, 제가 게을러서 Kafka Retention 기간이 지나서 다시 실행했던 것이었습니다.

즉 문제 상황은, 이미 checkpoint 에는 그 시점의 Offset 이 들어가 있는데, Kafka에서는 이미 Retention 정책으로 인해서 그 부분의 데이터는 없어진 상황인거죠.(checkpoint는 지정한 위치의 offsets 디렉토리 안에 batchId가 생기고 그 안에 있습니다.)

그런데 기본적으로 Spark에서 Kafka가 Offset 을 아주 과거로 주면, earlist 로 동작하게 되어있습니다. (소스는 보다가 아직 이해가 안되서 이걸 정확하게 설명할 정도로 보지는 못했습니다. 나중에 이해되면 추가할께요 관심이 있으시면, 다음 코드들을 보시면 좋을듯 합니다.





sql/core/src/main/scala/org/apache/spark/sql/execution/streaming/StreamExecution.scala
external/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaOffsetReaderConsumer.scala
external/kafka-0-10-sql/src/main/scala/org/apache/spark/sql/kafka010/KafkaMicroBatchStream.scala

그런데 이게 CheckPoint 안에 들어가 있는 Offset 은 그냥 그대로 처리하는 것으로 보입니다. 그럼 결론부터 생각하면 이게 문제가 되는가? 라고 하면, 큰 문제는 아닙니다. 위에서 에러가 발생한 것은 데이터가 0인 DataFrame 을 쓸려고 해서 생긴문제입니다.

다시 문제로 돌아가서 그렇다면 위와 같은 상황에서 데이터는 어떻게 처리가 될까요? 일단 조건으로 백프레셔 설정은 없다고 가정합니다. 그리고 checkpoint 에 저장되어 있던 값이 10001 이고 Kafka Log Retention 에 의해서 중간에 10001~70000 까지가 사라지고,현재 earliest가 70001 이라고 가정합니다. 그리고 현재 latest 는 100000 이라고 하겠습니다. 그러면 다음과 같이 MicroBatch 가 발생하게 됩니다. 그러면 다음과 같이 처리가 되게 됩니다.

BatchIDCount비고
N0존재하지 않는 10001~70000 까지의 처리량
N+130000earliest 부터 현재 시간까지 들어온 양 30000
N+2들어오는 만큼원래 스트리밍에서 처리해야 할 양

다행인 부분은 첫 부분에서 0으로 처리가 되기는 하지만, 그 다음 부터는 다시 정상(?)적으로 커버가 되게 된다는 점입니다.

다시 결론부터 얘기하자면, 위의 상황은 정상적으로 생각하면 발생하면 안되는 상황입니다. Kafka Retention 정책보다 뒤에 처리 스트리밍 코드를 돌린다는 것은 결국 Data Loss 상황인데, 이건 어떻게 보면 심각한 문제입니다. 저 같은 경우에는 개발 과정에서, 휴가 다녀온 다음에 실행해서 발생한 이슈였습니다. 물론 지식이 부족하기도 하지만요. 그래도 덕분에 카프카와 Spark Structured Streaming 에서 offset 이 어떻게 처리되는지 살짝 속내를 볼 수 있었네요.(휴, 왜 저만 이런일들이 벌어지는지…)


Viewing all articles
Browse latest Browse all 122

Trending Articles