오류는 하나도 안 났는데 시각이 54초씩 어긋나 있었음. REAL이 SQLite에서는 8바이트, PostgreSQL에서는 4바이트라서.
설정 한 줄로 전환
부하를 재 보니 쓰기가 섞이면 워커를 늘려도 소용이 없었음. SQLite가 쓰기를 한 줄로 세우기 때문. 그래서 PostgreSQL로 옮기기로 했는데, 한 번에 갈아 끼우면 문제가 생겼을 때 돌아갈 길이 없음.
그래서 설정 한 줄로 갈리게 만들었음. DATABASE_URL이 비어 있으면 SQLite, 채워져
있으면 PostgreSQL. 되돌리려면 그 줄만 지우면 됨. 데이터베이스를 부르는 코드가 274군데
있는데, 그쪽은 지금 어느 쪽에 붙어 있는지 모름. 데이터베이스 층 한 곳에서 자리표시자, 방금 넣은
줄의 id, 결과 읽는 방식을 양쪽에 맞춰 줌.
걸린 것들
| 자리 | 문제 | 해결 |
|---|---|---|
? 자리표시자 | PostgreSQL 드라이버는 %s | 따옴표 밖의 ?만 바꿈 |
글자 그대로의 % | LIKE '%abc%'를 자리표시자로 읽음 | %%로 바꿈 |
lastrowid | PostgreSQL에 없음 | INSERT에 RETURNING id |
INSERT OR IGNORE | SQLite 문법 | ON CONFLICT DO NOTHING으로 |
MAX(a, b) | PostgreSQL에서는 집계 함수 | CASE WHEN으로 (양쪽 공통) |
datetime('now') | SQLite 전용 | 시각은 파이썬에서 만들어 값으로 넣음 |
여기까지는 오류가 나서 어디가 틀렸는지 바로 보였음.
오류 없이 틀린 REAL
시각을 유닉스 초(1970년부터 센 초)로 REAL 칸에 넣고 있었음. SQLite의 REAL은
8바이트 실수인데, PostgreSQL에서 REAL은 4바이트 실수임. 4바이트 실수는
유효숫자가 일곱 자리 남짓이라 17억 단위인 지금 시각을 넣으면 끝자리가 뭉개짐. 실제로 시각이
54초씩 어긋나 있었음.
이건 아무 오류도 안 남. 저장도 되고 읽기도 됨. 대신 시각을 비교하는 기능이 조용히 틀림. 매칭, 투표, 인증번호가 전부 시각을 봄. 겉으로는 앱이 가끔 이상하게 구는 것처럼만 보임.
스키마를 옮길 때 REAL을 DOUBLE PRECISION으로 바꿔 번역하게 했음.
이러면 양쪽 모두 8바이트.
데이터 옮기기
- 옮기는 도구는 비어 있는 PostgreSQL에만 부음. 이미 뭔가 있으면 멈춤.
- 표마다 줄 수를 원본과 대조해서, 하나라도 다르면 실패로 봄.
- 자동 증가 번호를 옮긴 데이터 뒤로 밀어 둠. 안 밀면 다음에 쓰는 글이 id 1로 시작해서 바로 부딪힘.
테스트가 먼저 깨짐
옮기고 나서 테스트가 무더기로 실패했음. 앱이 고장 난 줄 알고 한참 헤맸는데, 테스트 코드가 데이터를 확인할 때 SQLite 파일을 직접 열고 있었음. 앱은 PostgreSQL에 쓰고, 테스트는 빈 SQLite를 보고 있었던 것. 테스트도 앱과 같은 연결 도구 하나를 거치게 바꿨음.