5.1.xx 에서 5.6.xx 로 업그레이드 할 일이 생겼다.

table partition 을 적용하는데 5.5 이후 버전에서 가능한 상황이었는데,
그래서 일단 사용중이던 VM 을 하나 새로 복사해서 테스트를 해봤다.

yum 으로 설치를 했는데,
일단 MySQL 용 yum repository 파일을 다운로드 받았다.




그리고 yum 의 repository 에 MySQL 5.6 을 추가했다.


For an EL6-based system, the command is in the form of:

shell> sudo yum localinstall mysql-community-release-el6-{version-number}.noarch.rpm  




기존 MySQL 도 삭제해야 하는데,
yum remove 명령어로, mysql 관련 패키지들을 모두 지웠다.

그리고 mysql-community-server 를 yum 으로 설치!

설치가 끝나고 나서 우여곡절이 좀 있긴 했지만, 어찌됐던 설치는 마무리 되었다.

우여곡절은.. yum 으로 설치하려는데 5.1 의 패키지와 충돌이 나는 문제가 있었는데,
yum remove 를 모두 해주지 않아서 생겼던 문제인 것 같기도 한 상태이나 정확한 원인은 모르겠다.

분명히 안됐었는데 갑자기 되기 시작했거든.. -_-;

원인 파악 안된 상태에서 해결됐다고 넘어가는거 안좋아하지만, 암튼 원인을 모르기때문에 어쩔수가 없네 ㅠ_ㅠ

여기까지 해서 mysqld 가 뜨기는 했다.



그리고 기존 데이터베이스를 통으로 덤프 떠서 넣고, 서버를 실행시켜서 클라를 붙였더니 (mysqldump --all-databases 옵션 사용)
이번에는 "Cannot load from mysql.proc." 에러가 발생했다.




mysql_upgrade 를 안했는데 되는게 당연히 안되는게 맞지.

그래서 mysql_upgrade 를 수행했는데, 이게 또 엄청 안되더라.

에러도 그냥 FATAL Error 라고 하면서 안되는 황당한 상황.

여기저기 찾아봤는데 --force 옵션을 주라는 말만 나오고, 답이 쉽게 나오지 않는 상태였는데,
결과적으로 이야기해보면, mysql_upgrade 로 준 옵션으로 mysql 서버에 연결이 안되서 발생한 문제였음

-h 로 서버 주소를 주고나서 괜찮아졌다.

원인을 바로 알려주지 않는 에러메시지는 너무 힘들다. 그래서 이렇게 남겨둔다!

끝!

상황


글로벌 서비스 준비중인데, 모든 시간을 KST 로 작업하고 있었다는..


그래서 모두 UTC 기준으로 변경을 하고 싶어서 프로그래머들끼리 가볍게 이야기를 나눠봤다.


여기서 전제조건!


1. 퍼블리셔는 OS 시간을 변경해줄 수 없음

2. 기존 Code 의 변경은 최소화 (로직을 변경하면서 버그를 만들 수 있는 상황이 아님)



우선, 기준이 되는 UTC 를 사용하려면, DB 에서 쓰고있던 Procedure base logic 들도

CURRENT_TIMESTAMP() -> UTC_TIMESTAMP() 로 변경 (1) 이 필요했고,

Java Code 에서 Calendar, Date, Timestamp 를 쓰는 코드를 모두 점검 (2) 해야 했다.


(1) 은 replace 가 용이한 상황이어서 난이도 하하하

(2) 는 일일이 봐야해서 난이도가 높다고 생각했지만 생각보다 코드가 많지 않아서 중하하


라고 판단함




수정 감행


시간이 그리 많지 않아서 신속하게 결정하고, 아래와 같이 진행했다.



1. TimeZone 기반으로 Calendar 를 하나의 함수를 통해 얻어오도록 수정


기존에는 Calendar.getInstance() 를 여기저기서 호출했는데,

특정 날짜관련 클래스를 하나 만들고 거기서 getNow() 를 통해 Calendar 를 얻어가도록 수정



2. DB 의 모든 CURRENT_TIMESTAMP() 를 UTC_TIMESTAMP() 로 수정


기존에는 CURRENT_TIMESTAMP() 를 사용하고 있어서

MySQL 의 서버 시간 설정에 의한 시간값을 얻어가고 있었는데

UTC_TIMESTAMP() 를 이용함으로써, 서버 시간설정에 영향을 받지 않도록 수정



그 후, 원하는대로 값이 저장되는지 확인해봤다.


그런데, 좀 이상했다..


분명, WAS Code 에서 2015-07-01 00:00:00 을 얻었는데,

DB 에 2015-07-01 09:00:00 이 저장됨


그리고 WAS Code 에서 그 값을 읽어서 클라이언트로 전달하면서

클라이언트는 그 시간이 UTC 라고 믿고 기기가 KST 를 표시해야 하니, +09:00 를 추가로 함 -_-


서버가 클라로 줘야하는 시간이 UTC 로 약속되어있기 때문에,

서버에서 DB 의 값을 읽을 때 UTC 로 변환해야할 필요가 있는것 처럼 느껴졌었는데,


Calendar 를 얻어가는 함수 내부에서 TimeZone 을 UTC 로 설정하고 있기 때문에

코드상의 문제는 아닌것 같았고, 좀더 debugging 해서 결국 문제점을 찾았다.




핵심 문제


핵심 문제는 Timestamp 를 사용해서 DB 쪽으로 인자를 전달하는 과정에서

Timestamp 값이 Calendar의 TimeZone 에 의해 변경되지 않고

JVM의 TimeZone 에 의해 결정된 시간으로 저장되는 것을 몰랐던 것이 문제였다.


깊이 몰랐던 것이 문제여서, 이번 기회에 정리를 해두려고 한다.



Calendar 의 속성


Calendar 는 setTimeZone 에 의해 설정된대로 충실히 동작한다.


예를 들어, 2015-07-01 00:00:00 KST 인 값도 TimeZone 만 변경하면

get(Calendar.HOUR_OF_DAY) 를 했을때 설정된 TimeZone 에 의한 값을 return 한다.


set(Calendar.HOUR_OF_DAY, hour) 도, 내부적으로 설정된 TimeZone 에 의한 시간으로 set 한다.


그래서 현재 Calendar 가 무슨 TimeZone 을 갖는지 확인하고,

비교하는 데이터가 동일한 TimeZone 의 값인지 조심해야 한다.



Date 의 속성


Date 는 TimeZone 과 Independent 하다.


Calendar.getInstance().getTime() 의 Date 는, 내부적으로 fastTime 을 갖고 있고,

이 값이 TimeZone 에 의해 해석되는 형식이다.


여담인데, CallableStatement 의 인자를 setDate 로 넘기면, 날짜만 넘긴다.. -_-



MySQL 의 속성


MySQL 에도 TimeZone 이 엄연히 존재하지만,

JDBC 의 setTimestamp 를 통해 전달된 인자를 변경 없이 충실히 저장해주기 때문에

MySQL 내부에서 UTC_TIMESTAMP() 를 사용하면, 사실상 TimeZone Independent 한 작업을 할 수 있을 것 같다.




결론


여하한 이유로 OS 의 시간을 변경할 수 없다면,

JVM 에서 UTC 를 사용하고, MySQL 에서 UTC_TIMESTAMP() 를 사용하는게 좋은 것 같다.


만약, JVM 에서 UTC 를 설정하기조차 할 수 없었다면,

너무 슬펐을 것 같다. -_-


마지막으로, Tomcat6 에서 TimeZone 설정 변경법




/etc/tomcat6/tomcat6.conf 


... 


# Cannot resolve user database reference - naming-factory-dbcp

# the real problem is a cnfe that is avoided by configuring 

# the -Djavax.sql.DataSource.Factory. This fixes the rpm install. 

JAVA_OPTS="${JAVA_OPTS} -Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourceFactory -Duser.timezone=GMT” 


... 



+ Recent posts