서비스가 직접 조회하는 Kudu 테이블에 대량 upsert·delete 배치가 돌 때, 서비스가 테이블을 직접 바라보지 않게 view 를 두고 물리 삭제 대신 soft delete 로 가는 방식이다. 필요한 컬럼이 모두 non-PK 라 Kudu 가 허용하는 스키마 변경(비-PK 컬럼 추가) 범위에서 테이블 재생성 없이 적용할 수 있다.
ALTER TABLE svc_item ADD COLUMN is_deleted BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE svc_item ADD COLUMN src_ver BIGINT NOT NULL DEFAULT 0;
ALTER TABLE svc_item ADD COLUMN batch_id BIGINT NOT NULL DEFAULT 0;
ALTER TABLE svc_item ADD COLUMN load_ts BIGINT NOT NULL DEFAULT 0;
DEFAULT 가 있으면 기존 행은 그 값으로 읽히고 기존 쿼리는 영향받지 않는다. BOOLEAN 은 RLE 인코딩이라 저장 비용이 거의 없다. src_ver 는 원천에 변경 시퀀스가 없으면 변경시각(마이크로초)으로 대체한다.
앱 수정 없이 가려면 테이블을 개명하고 원래 이름으로 view 를 만든다. rename 과 create view 사이 수 초간 쿼리가 실패하므로 저부하 시간대에 연속 실행한다.
ALTER TABLE svc_item RENAME TO svc_item_tbl;
CREATE VIEW svc_item AS
SELECT item_id, item_nm, price, eff_ts -- 컬럼 명시. SELECT * 금지
FROM svc_item_tbl
WHERE is_deleted = false;
진짜 무중단이 필요하면 v_svc_item view 를 먼저 만들고 앱을 그쪽으로 배포한 뒤 서비스 계정의 테이블 직접 권한을 회수한다. 어느 쪽이든 마지막에 원본 테이블 권한을 회수해야 이후 스키마 변경이나 테이블 스왑을 앱 수정 없이 할 수 있다.
UPSERT INTO feed_control (table_nm, batch_id, status, start_ts)
VALUES ('svc_item', ${BATCH_ID}, 'RUNNING', unix_micros(now()));
UPSERT INTO svc_item_tbl (item_id, item_nm, price, eff_ts, is_deleted, src_ver, batch_id, load_ts)
SELECT s.item_id, s.item_nm, s.price, s.eff_ts, false, s.src_ver, ${BATCH_ID}, unix_micros(now())
FROM stg_item_upd s
LEFT JOIN svc_item_tbl t ON s.item_id = t.item_id
WHERE t.item_id IS NULL OR s.src_ver > t.src_ver; -- 버전 역전 방지
UPDATE t SET t.is_deleted = true, t.src_ver = d.src_ver, t.batch_id = ${BATCH_ID}, t.load_ts = unix_micros(now())
FROM svc_item_tbl t JOIN stg_item_del d ON t.item_id = d.item_id
WHERE d.src_ver > t.src_ver; -- soft delete
SELECT COUNT(*) FROM svc_item_tbl WHERE batch_id = ${BATCH_ID};
UPDATE feed_control SET status = 'COMMITTED', commit_ts = unix_micros(now()) WHERE table_nm = 'svc_item' AND batch_id = ${BATCH_ID};
range 파티션 드롭이 불가능한 테이블(UNBOUNDED 하나뿐인 경우)은 DELETE FROM svc_item_tbl WHERE is_deleted = true AND load_ts < <기준> 배치가 유일한 공간 회수 수단이므로 반드시 스케줄에 넣는다. 삭제된 행의 공간은 tablet history maximum age(이 환경은 15분으로 단축)가 지나고 컴팩션이 돌아야 회수된다.
SELECT * 를 쓰면 이후 컬럼 추가 시 서비스 쪽 스키마가 바뀐다.feed_control 의 commit 시각을 서비스 화면의 "데이터 기준시각" 으로 노출하면 배치 중 반영 상태를 사용자에게 설명할 수 있다.