이슈노트 #11: PostgreSQL에서의 이상한 UPDATE 실패
PostgreSQL 환경에서 발생한 미묘한 문제였다.
문제
마이그레이션을 위해서 tasks라는 테이블에 그리 복잡하지 않은 UPDATE 쿼리를 날렸는데, 이해할 수 없는 이상한 오류가 발생했다.

SQL Error [23505]: ERROR: duplicate key value violates unique constraint "idx_tasks_unique_per_day"
Detail: Key (industry_id, campaign_id, task_date_kst(created_at))=(null, 200, 2026-02-26) already exists.
분명 유니크 조건에 속하지 않는 컬럼만 변경했는데, 뜬금없이 유니크 제약이 걸려서 실패가 발생한 것이다.
왜 이런 것이고, 어떻게 이럴 수 있는걸까?
문제 원인
문제는 2가지 요소가 중첩된 것이었다.
"타임존"과 "MVCC"다.
MVCC
PostgreSQL은 컬럼값을 하나만 변경하더라도, 전체 데이터를 새 버전으로 append하는 MVCC 의미론을 사용한다. 그래서 상관없는 컬럼을 변경하더라도, 전체 데이터 재구성 중에 유니크 제약을 위반한다면 전체 실패로 이어지는 것이다.
타임존
유니크 조건에 사용한 타임존 의존적인 로직도 문제였다.
원본 타임스탬프에 별도로 정의한 함수를 써서 날짜 기준으로 동적인 유니크를 거는데, 문제는 이것이 고정적인 변환이 아니라는 것이었다.
정확히 말하면 DB Client, 내가 접속한 세션의 타임존에 따라서 저 Date 변환값이 달라지는 것이다.
내 경우에는 DBeaver를 썼는데, 이게 기본으로 로컬 타임존을 따라서 "한국 타임존"으로 Date 변환을 했고, 기존 데이터는 서버 타임존이라서 "UTC"였다. 그래서 9시간의 차이로 인해서 Date Window가 달라지고, 재정렬로 인해서 충돌이 발생한 것이다.
개선안
당연히 저런 구성은 그다지 좋은 방식이라고 말하기는 어렵다.
가장 좋은 것은 저런 변환을 처리할때 타임존을 하드코딩해서 넣는 것이다.
이런 식으로 말이다.
그럼 어떤 세션에서 들어오든 동일한 값이 출력된다.
짧은 교훈
가변적인 것보다 불변이 좋다는 것은 어느 상황에나 통용되고, 이 경우에도 마찬가지다.
뭐가를 만들때는 가능하면 모든 변수를 통제한 채로 동작하게 만들자. 특히 DB 설정에서는 더.