분류 자료를 crontab 으로 하루 한 번 받아 오는 작업이 있다. 마지막으로 받은 시각을 어딘가 적어 둬야 했는데 그 값이 api_config 에 들어 있었다.
Table of contents
Open Table of contents
계정마다 값이 갈라졌다
api_config 에 last_sync_date 컬럼이 붙어 있었다.
CREATE TABLE api_config (
config_no INT NOT NULL AUTO_INCREMENT,
account_name VARCHAR(50) NOT NULL,
api_key VARCHAR(100) NOT NULL,
last_sync_date DATETIME NULL,
PRIMARY KEY (config_no)
);
계정이 세 개라 행이 셋이고 last_sync_date 도 셋이다. 그런데 분류 자료는 계정과 무관하다. 어느 계정으로 받든 같은 자료인데 값만 계정마다 따로 있다.
한 계정으로 받으면 그 행만 UPDATE 되고 나머지는 옛 시각을 들고 있다. 마지막으로 언제 받았느냐를 물으면 답이 세 개가 나온다. 어느 것이 맞는지 판단할 근거가 없다.
이 값이 무엇에 달려 있나
값을 어디에 둘지 정할 때 먼저 물었어야 하는 것이 이것이었다. 계정마다 다른 값이면 계정 테이블에 두고 상품마다 다르면 상품 테이블에 두고 시스템 전체에 하나면 전역 자리에 둔다.
분류 자료 수신 시각은 세 번째다. 계정이 몇 개가 되든 시스템에 하나다. 컬럼이 붙어 있는 테이블이 그 값의 주인이 아니었던 것이다.
전역 자리를 만들었다
이런 값이 더 생길 것 같아서 app_meta 를 만들었다.
CREATE TABLE app_meta (
meta_key VARCHAR(50) NOT NULL,
meta_value VARCHAR(255) NULL,
upd_date DATETIME NOT NULL,
PRIMARY KEY (meta_key)
);
기존 세 행 중 무엇을 옮길지도 정해야 했다. MAX 를 쓰면 그 사이에 받았어야 할 것을 안 받는다. MIN 을 쓰면 이미 받은 것을 다시 받는다. 분류 자료는 덮어써도 되는 것이라 중복 수신이 문제가 안 되므로 MIN 을 골랐다.
INSERT INTO app_meta (meta_key, meta_value, upd_date)
SELECT 'last_category_sync', DATE_FORMAT(MIN(last_sync_date), '%Y-%m-%d %H:%i:%s'), NOW()
FROM api_config WHERE last_sync_date IS NOT NULL;
last_sync_date IS NOT NULL 로 값이 비어 있는 계정은 제외했다. MIN 은 NULL 을 무시하지만 조건에 두면 의도가 드러난다. 한 번도 안 받았다는 뜻이라 판단에 쓸 값이 아니었다.
옛 컬럼은 바로 DROP 하지 않았다. grep -rn last_sync_date 로 읽는 코드를 찾아 고치고 그 뒤에 지웠다. 남겨 두면 다음 사람이 그 컬럼을 보고 쓴다. 값이 안 갱신되는 컬럼이 남아 있는 쪽이 더 나쁘다.
키-값 테이블의 함정
편한 대신 조심할 것이 있었다. meta_value 가 VARCHAR 라 날짜도 문자열로 들어간다. 꺼내서 비교할 때 STR_TO_DATE 를 씌우거나 PHP 쪽에서 변환해야 한다.
키 이름에 오타가 나면 조용히 통과하는 것도 문제였다. 없는 meta_key 를 읽으면 빈 값이 나오고 오류는 안 난다. define 으로 상수에 모아 뒀다.
define('META_LAST_CATEGORY_SYNC', 'last_category_sync');
define('META_LAST_PRICE_SYNC', 'last_price_sync');
아무 값이나 들어갈 수 있어서 이 테이블이 쓰레기통이 되기 쉽다는 것도 봤다. 무엇이 들어 있고 각 키가 무슨 뜻인지 목록을 문서로 남겼다.
정리
- 대상과 무관한 값을 대상별 테이블에 두면 행 수만큼 값이 갈라진다
- 값을 어디 둘지는 이 값이 무엇에 달려 있는지로 정한다
- 시스템에 하나뿐인 값은
app_meta같은 전역 자리를 만들어 둔다 - 옮길 때
MIN과MAX중 무엇을 쓸지 정한다 — 다시 해도 되는 쪽이 대개 안전하다 - 옛 컬럼은
grep -rn으로 읽는 코드를 다 고친 뒤에DROP한다 meta_value가VARCHAR라 날짜도 문자열로 들어간다- 없는
meta_key는 오류가 아니라 빈 값이라define상수로 모은다