[Spark] issue 정리, java.lang.ExceptionInInitializerError 와 java.lang.IllegalStateException: SparkSession should only be created and accessed on the driver
scala언어로 spark object를 만들고 jar로 빌드하여 EMR에 제출할 때 나온 에러를 정리하겠습니다.
spark관련 정리는 오랜만이네요.
1. 원인 파악
분명 다른 object와 다른점 없고, jar파일 위치 이름, class 이름까지도 경로에 명시를 잘해 줬는데도 위 제목과 같은 에러가나와서 계속 헤메고 있던 때에 리서칭을 통해서 원인을 파악할 수 있었습니다.
scala object에서 다음과 같은 에러가 나온 경우, 주로 Apache Spark에서 SparkSession이 드라이버 노드가 아닌 워커 노드에서 생성되거나 접근될 때 발생된다고 합니다.
→ 이는 SparkSession이 조정 목적으로 드라이버에서 관리되어야 하는 싱글톤 객체이기 때문
java.lang.ExceptionInInitializerError
java.lang.IllegalStateException: SparkSession should only be created and accessed on the driver
SparkSession이라는 힌트를 얻었고, 관련해서 여러 시도를 해보던 중에 아래와 같은 글도 확인하였습니다.
변환(map,flatMap,filter등)이나 액션(foreach,collect등) 내에서 SparkSession을 인스턴스화하거나 사용하려고 하면 이 오류가 발생 → 이러한 함수들은 워커 노드에서 실행되기 때문
기본적으로 spark는 아래와 같은 특징을 갖고 있습니다
- 드라이버 노드 vs. 워커 노드:
- 드라이버 노드: 이 노드는 Spark 애플리케이션이 실행되는 주요 노드입니다. Spark 작업의 실행을 조정하고 SparkSession을 유지하는 역할
- 워커 노드: 이 노드들은 실제 데이터 처리 작업을 수행합(작업 실행, 변환 실행 등)
- SparkSession:
- SparkSession은 Spark와 상호작용하기 위한 통합 진입점으로, SQL, DataFrame, 스트리밍 등의 다양한 Spark 기능에 접근할 수 있게 해줍니다. 이는 한 번만 생성되어야 하며, 이 인스턴스는 드라이버 노드에 있어야 함
위의 내용들을 종합적으로 봤을 때, 내 코드가 문제가 될 만한 포인트를 찾아보니, sparksession을 object 내부에, function 외부에 정의를 해두었습니다.
그런데 같은 프로젝트의 다른 object에서도 동일하게 적용한 코드가 있는데, 걔내는 왜 에러가 안났지? 라는 의문이 들었습니다. 이 또한 위에서 힌트를 찾을 수 있었는데, rdd 레벨에서의 map과같은 함수가 문제가 됐던 것 같습니다.
2. 해결 방법 및 조치
해결방법은 원인을 알고 나니 의외로 간단했는데요.
object 내부 함수 외부에 정의된 SparkSession 정의를 object 내부 main함수 내부에 sparksession을 정의하는 것 이였습니다.
AS-IS 코드
object SparkExample {
val spark: SparkSession.builder().appName("Spark Application").getOrCreate()
def filterByAge() Unit = {
...
}
def main(args: Array[String]): Unit = {
...
}
}
TO-BE 코드
object SparkExample {
def filterByAge(spark: SparkSession) Unit = {
...
}
def main(args: Array[String]): Unit = {
val spark: SparkSession.builder().appName("Spark Application").getOrCreate()
...
}
}
아래 TO-BE코드와 같이 변경해주니, 에러없이 잘 수행 되었습니다.