Ecto 연관 관계 업데이트 시 고유 제약 조건 문제
문제 발생 원인 분석
Ecto를 사용하여 연관된 레코드(단계)의 순서를 변경할 때 Ecto.ConstraintError가 발생했습니다. 이 오류는 steps_process_id_order_index라는 고유 제약 조건 위반으로 인해 발생합니다. 예를 들어 (step 1, order: 1)과 (step 2, order: 2)를 (step 1, order: 2)와 (step 2, order: 1)로 변경하려 할 때, Ecto는 다음 과정을 거쳐 개별적으로 레코드를 업데이트합니다.
step 1의order를2로 업데이트
이 시점에서 데이터베이스는 (step 1, order: 2)와 (step 2, order: 2)라는 중간 상태가 됩니다. 이는 process_id와 order 컬럼에 설정된 고유 인덱스(unique_index(:steps, [:process_id, :order]))를 즉시 위반하게 되어 오류가 발생합니다. 데이터베이스는 트랜잭션의 최종 상태가 아닌 현재 시점의 상태를 기준으로 제약 조건을 검사하기 때문에, 비록 다음 업데이트로 step 2의 order가 1로 변경될 예정이라 하더라도 중간 상태에서의 위반을 허용하지 않습니다.
해결책: PostgreSQL의 DEFERRABLE 제약 조건 활용
이 문제를 해결하기 위해 PostgreSQL의 DEFERRABLE INITIALLY DEFERRED 제약 조건을 활용합니다. 이 제약 조건은 고유성 검사를 즉시 수행하는 대신, 현재 트랜잭션이 커밋되는 시점까지 검사를 지연시킵니다. 이를 통해 중간 상태에서 발생하는 고유 제약 조건 위반을 회피할 수 있습니다.
기존의 create unique_index 대신 다음 SQL 명령어를 마이그레이션에 추가하여 제약 조건을 생성합니다:
sql
execute """
ALTER TABLE steps ADD CONSTRAINT unique_step UNIQUE (process_id, "order") DEFERRABLE INITIALLY DEFERRED
"""
이 ALTER TABLE 문은 steps 테이블에 process_id와 order 컬럼에 대한 고유 제약 조건을 추가하며, 이 제약 조건은 기본적으로 트랜잭션 커밋 시점까지 검사가 지연되도록 설정됩니다. 이 변경을 적용함으로써 Ecto의 연관 관계 업데이트 메커니즘이 중간 제약 조건 위반 없이 완벽하게 작동하게 됩니다.