Presto JDBC 사용시 TimeZone 주의
Presto의 native library나 jdbc 등과 같은 Client 모듈을 이용하여 데이터를 조회하는 경우 TimeZone 설정에 주의해야 합니다. 이것때문에 반나절 삽질했습니다.
(이 문서는 Presto 0.155 기준으로 작성되었습니다.)
TimeZone 문제
Presto Client에서 Presto 서버와의 접속을 유지하는 클래스가 ClientSession 인데 이 클래스의 생성자는 다음과 같이 정의되어 있습니다.
1 2 3 4
public ClientSession(URI server, String user, String source, String catalog, String schema, String timeZoneId, Locale locale, Map<String, String> properties, String transactionId, boolean debug, Duration clientRequestTimeout)
보시는 것 처럼 timeZoneId를 전달하도록 되어 있습니다. 직접 ClientSession 클래스를 생성하여 사용하는 경우라면 문제가 없지만 JDBC Driver를 이용하는 경우라면 아주 심각한 데이터 오류가 발생할 수 있습니다. Presto JDBC Driver의 PrestoConnection 클래스에는 다음과 같이 구현되어 있습니다.
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
PrestoConnection(PrestoDriverUri uri, String user, QueryExecutor queryExecutor) throws SQLException { requireNonNull(uri, "uri is null"); this.jdbcUri = uri.getJdbcUri(); this.httpUri = uri.getHttpUri(); this.schema.set(uri.getSchema()); this.catalog.set(uri.getCatalog()); this.user = requireNonNull(user, "user is null"); this.queryExecutor = requireNonNull(queryExecutor, "queryExecutor is null"); timeZoneId.set(TimeZone.getDefault().getID()); locale.set(Locale.getDefault()); } StatementClient startQuery(String sql) { String source = firstNonNull(clientInfo.get("ApplicationName"), "presto-jdbc"); ClientSession session = new ClientSession( httpUri, user, source, catalog.get(), schema.get(), timeZoneId.get() locale.get(), ImmutableMap.copyOf(sessionProperties), transactionId.get(), false, new Duration(2, MINUTES)); return queryExecutor.startQuery(session, sql); }
Connection 정보로 Timezone 관련 정보를 받지 못하게 되어 있으며 JVM의 기본 Timezone을 사용하고 있습니다.
최근 서비스는 국제화를 고려하여 서버 설정과 DateTime 타입의 데이터는 UTC로 많이 설정하고 있습니다. 그리고 개발자의 로컬 장비는 대부분 해당 국가의 Timezone을 따라 갑니다. 이때 개발자 장비에서 Presto에 접속하여 데이터를 조회하여 가공하게 되면 잘못된 데이터로 분석을 하게 됩니다. 이 문제는 특히 now() 와 같이 시간 계산 시 사용하는 시스템 시간을 가져오는 기능을 사용할 때 주로 발생합니다.
해결 방법
이를 해결하는 방법은 다음 두가지가 있습니다.
- JDBC 접속하는 프로그램에서 Default TimeZone을 변경
이것도 두가지 방법이 있는데 JVM 실행 시 "-Duser.timezone=UTC" 과 같이 명령 인자로 전달하는 방법과 프로그램 내에서 TimeZone.setDefault(TimeZone.getTimeZone("UTC")) 이렇게 설정할 수 있습니다.
- PrestoConnection 의 setTimeZoneId() 메소드 이용
Connection 객체를 PrestoConnection 으로 타입을 변환하여 setTimeZoneId() 메소드를 호출합니다.
((PrestoConnection)conn).setTimeZoneId("UTC");
그래도 리포팅 툴이나 NiFi와 같은 JDBC 드라이버를 이용하는 범용 프로그램에서 사용하는 경우에는 1번 방법밖에 사용할 수 없는데 이것은 전체 프로그램에 영향을 미치기 때문에 여전히 문제가 있습니다. Connection 시 URL에 TimeZone 정보를 전달할 수 있는 패치가 필요할 것 같습니다.
간단하게 패치 만들었습니다. 다음 브랜치 참고하세요.
https://github.com/babokim/presto/tree/version-0.155-jdbc-timezone