본문 바로가기

Gossip about IT & Job

DB Table 간의 연동 작업을 어떻게 할 것인가?

  지금 진행하는 작업은 세 개의 테이블이 서로 얽히고 섥혀서 돌아가는 어플리케이션이다. 좀더 상세하게는, 특별한 복사본을 갖고있는 두 개의 테이블과, 테이블 간의 관계를 관리하는 테이블 한개를 포함해서 여섯개의 테이블이 맞물려 있다. 특히 어려운 점은, 이 테이블들이 동시에 설계된 것이 아니고 전혀 별개의 기능들을 구현하면서 설계된 테이블인데다 이 기능을 위한 특화된 테이블 들도 아니라는 점이다.

 이런 상황에서 문제를 해결하기 위해, 복잡한 알고리즘을 고안하기 보다는, 끊임없이 테이블간의 싱크를 맞추는 방법을 선택했다. 어떤 동작을 할 때 마다, 화면의 갱신이 있을 때 마다 가장 최신의 정보를 모든 테이블이 동일하게 갖을 수 있도록 쿼리를 짰다. DB를 사용하면 편한점이, 일일이 루프를 돌려서 경신할 필요가 없이 쿼리를 DB에 던져주는 것 만으로도 모든 열들의 갱신이 이루어진다는 점이다.

  내 설계는 DB의 갱신이 일어날 때 마다 모든 테이블의 상태를 일치시켜주는 방식으로 진행이 되었다. 이 방식의 장점은 명확한 동작과 기능간의 인터랙션 설계가 없는 상태에서 안정된 구현이 가능하다는 점이다. 각각의 기능 중 한쪽에만 수정 사항이 보이는 일은 일어나지 않고, 혹시나 놓질 수 몇몇 예외상황에 대해서도 곧 회복이 되어 정상동작으로 돌아간다는 점이다. 구현도 어렵지 않았고, 무엇보다 핵심 기능을 쿼리들이 수행해 주기 때문에, 나중에 컨셉이 바뀌거나 세부 요구사항이 변경될 때도 수정이 쉽다는 장점이 있다.

  문제는 이런 방식은 임베디드 디비에서 동작하기에는 부하가 크다는 점이다. 더구나 각각의 기능들의 엔트리가 Max 1000개 까지 지원되는 상황에서 이 기능들이 Full Sync가 일어나는 상황에서는 약 30초 정도 화면이 멈춘 듯이 보이기도 한다. (모래시계를 돌려야 한다.) 이를 방지하기 위해서 쿼리 최적화에 굉장히 공을 많이 들였지만, 한계가 있었다. 특히 날짜 계산에서 여러가지 예외상황과 추가 요구사항에 대응하기 위하여 더 복잡한 쿼리를 사용하게 되었다. (이 쿼리에 관한 내용은 다음에 글을 쓸 기회가 있을 것이다.)

  결론을 이야기 하면, 결국 대부분의 코드를 최근에 추가되거나 수정된 엔트리만 다른 테이블에도 업데이트하도록 수정을 할 수 밖에 없었다. 가장 큰 이유는 Full Sync 동작에서 메모리 부족으로 리셋이 나는 것이 발견되었고, 그 오류가 서드 파티 라이브러리 내부의 문제라서 내가 손을 댈 수 있는 영역 밖이었다. 둘째는 속도 문제였는데, 이 또한 라이브러리 내부에서 가비지 콜렉션이 부족해서 발생하는 것으로 실험 결과 증명되었다. 서드 파티 라이브러리를 쓰는 것이 이런 문제를 안고 가기 때문에 항상 쉽지 않다.

  결국, 내가 구현한 기능이 아닌 다른 기능의 흐름을 철저하게 분석할 수 밖에 없었고, 그 상황에서 필요한 동작에서 최소한의 업데이트만 수정하도록 쿼리와 함수를 고쳤다. 참고로, 난 다른 기능의 구현 상황을 전혀 알지 못했고, 더군다나 이번에 처음 분석을 한 기능은 두 가지였다.

  처음에는 Full Sync 방식이 쉽게 쉽게 가면서 동작에서의 오류를 최소할 수 있는 종은 방법이라고 생각했지만, 결과적으로 기능 분석을 통해 최소한의 부하를 갖는 방식으로 갈 수 밖에 없었다. 지금길로 간다고 생각했지만, 거대한 삽질을 했던 것에 불과하다. 3월 한달이 이 엄청난 튜닝작업에 소모되었다. 특히 막판 수정으로 이틀을 날렸다. 토요일에 풀타임 근무를 할 수밖에 없었다.

  교훈 : 쉬운 길이 문제가 아니다. 결국은 최고 효율을 낼 수 있는 제대로된 설계와 구현이 필요하다.

  더 큰 교훈 : 이 프로젝트의 시작에 요구사항 분석과 설계가 결여되어 있었을 뿐 아니라, 함께 연동되는 기능들 간에 의사소통이 너무 적었다. 젠장.