여러 Process 가 같은 Database 를 read/write 를 하는 경우 SQLITE_BUSY 가 return 되면서 원하는 동작을 하지 못하는 문제가 있었다.
어떻게 해결하였는지 기술하겠다.
우선 SQLITE_BUSY 가 어떤 상황에서 발생하는지 referece 를 참조하자.
친절하게 예시까지 쓰여져있다.
https://www.sqlite.org/rescode.html#busy
간단하게 말하자면 "A Process 가 write transaction 을 걸어놓은 상태에 B Process 가 새로운 Write Transaction 을 시도할때 SQLITE_BUSY 에러가 발생"할 수 있다. 라고 설명하고 있다.
(실 사용중에 발생한 case 는 A Process 에서 Write transaction 이 걸려있는 상태에서 B Process 가 Read 를 시도할 때 발생하였었다.)
해결책 1 - busy timeout 을 설정.
sqlite 에서 제공해주는 api 를 이용하여 waiting 시간을 늘려주는 방식이다.
즉 A Process 가 transaction 을 잡고 있더라도 특정 시간 만큼 기다려보는 동작이다.
값을 설정하기 위해서는 아래 function 을 사용한다.
int sqlite3_busy_timeout(sqlite3*, int ms);
//첫번째 인자 : db handle pointer
//두번째 인자 : 기다릴 시간 ( ms )
reference : https://www.sqlite.org/c3ref/busy_timeout.html
이 해결책에는 치명적인 오류가 존재한다.
설정한 시간 이상 기다리게 된다면 ? 결국 SQLITE_BUSY 가 똑같이 return 된다. 완전한 해결책이라고 보기 어렵다.
그래서 다른 해결책이 필요하다.
해결책 2 - call back 함수를 이용.
sqlite 에서 api 를 제공해준다. sqlite_busy 가 return 될 시에 동작할 함수를 개발자가 직접 구현하여 call back 형식으로 등록 할 수 있다.
int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
//첫번째 인자 : db handle pointer
//두번째 인자 : call back 함수 pointer.
//세번째 인자 : call back 함수 첫번째 인수.
// call back 함수의 첫번째 인자 : handler 의 세번째 인자
// call back 함수의 두번재 인자 : 몇번째 busy 인지에 대한 값,
https://www.sqlite.org/c3ref/busy_handler.html
위와 같은 해결책이 존재하지만 결국 이 문제를 100% 회피할 방법은 존재하지 않는다.
만약 A Process 에서 Transaction 잡아놓고 절때 풀지 않는다면 ? B Process 에서는 DB 접근을 할 수 없게 된다. 물론 A Process 쪽에서 수정이 되어야 하겠지만 개발하면서 느끼는 건데 아무도 믿으면 안되는것 같다.
그럼 어떻게 해야 할까?
"설계 할 때 실시간으로 여러 process 가 read / write 를 하는 DB를 만들지 말자"
이다.
결론이 아쉽지만 process 간 데이터의 공유가 필요하다면 Database 관리는 한쪽에서만 하고 공유 메모리를 사용하거나 혹은 IPC를 통해 주고 받는방향으로 처리를 하는게 위와 같은 문제를 피할 수 있을 것이다.