본문 바로가기

MY개발생각

[개발생각] Transactional(readOnly=true)

마스터-레플리카 구성의 디비에서 마스터는 Read-Write로 사용하고,

레플리카는 Read-Only로 사용하고 싶었다.

(데이터베이스의 부하 분산 측면으로 레플리카를 조회성으로 적극활용하고 싶었기 때문이다.)

 

RoutingDataSource을 확장하여, Transactional의 readOnly를 이용해서,

마스터 <-> 레플리카로 DataSource를 스위칭하도록 설정을 하였다.

 

당연히 기대한 결과는,

1) Hibernate에서 ReadOnly=true의 기본동작인 DirtyCheck를 하지않는것 (조회 성능 최적화)

2) 해당 트랙잭션안에서는 레플리카 디비를 사용함에 따른 save, delete, update등의 쿼리를 사용하면, 예외를 발생하는것

 

위 2가지를 기대했다.

 

1번은 기대했던대로 동작을 하는것으로 확인됬고,

 

2번이 생각한것과는 다르게 동작했다.

Transactional(readOnly=true) 블록안에서 save를 해도, 예외가 발생하지 않고, 해당 쿼리는 skip되었다.

 

의아하게 생각되어 찾아보니,

readOnly에서는 flush를 하지않기에, 쿼리자체가 DB쪽으로 날아가지를 않기에 쿼리 예외또한 발생하지 않는다는것이였다.

(당연히, 쿼리가 DB에 호출이 안됬으니, 에러날 기회조차 못얻은거지..단 @Update쿼리는 자체 Flush를 설정하면, 에러가 발생함.)

 

readOnly속성말고 커스텀 속성으로 동작하게 해볼까도 생각했지만,

사실 조회성에서 중요한것중에 하나가 DirtyChecking을 하지않는것또한 성능측면에서는 중요한 요인이기에,

readOnly속성을 사용하는건 우선 맞는 선택으로 생각이 되었다.

 

RoutingDataSource의 Datasource 스위칭시, flushMode를 AUTO로 강제로 바꾸어주는 방법도 있긴하지만,

그다지 끌리지는 않는 방법이긴했다.(뭔가 찝찝해..)

하지만, 해당 기능을 Production에 적용하지않고, Local, Test, Stage쪽에서만 동작하게 만들어두면,

좀더 깔끔하게 잘사용할수있겠다는 생각을 했다.

 

가장 큰 문제가 개발단계에서 Save를 ReadOnly블록안에서 잘못 넣어 코딩한경우를 개발자가 알아챌수없는 것이기에,

해당 문제를 Production이외의 환경에서는 예외를 발생하게 하여, 인지할수있도록 하고 수정할수있게 환경을 만들어주고,

Production환경에서는 기본 취지에 맞게, ReadOnly를 사용하는게 자연스러운 사용법이라고 결론을 냈다.

 

내일가면...그렇게 설정하자고 해야지 ㅋ

 

당연하다고 생각한게 당연하게 동작하지 않는것에서 당황스러웠고, 다르게 동작하는 기능이 '잘못됬구나' 라고 섣부르게 판단했지만,

깊게 고민해보고, 판단해보니,

해당 기능이 틀리지않은 동작이였다는걸 확인하고 나서는..

음...역시 코드마다 작성한 사연과 이유가 있는것 아닐까?

 

하나 배운것 같다.