Rails 6.0 create_or_find_by

사승준·2022년 3월 7일
1
post-thumbnail

rails 6.0 버전부터 create_or_find_by라는 메서드가 추가됐다. 해당 메서드에 사용 방법에 대해서 알아보자!

create_or_find_by란?

레코드를 먼저 생성하려고 시도하고 이미 기존에 있는 데이터가 있다며 기존 데이터를 반환하는 메서드다. 구현된 소스 코드를 살펴보니 생성하는 과정에서 RecordNotUnique 예외가 발생하면 find_by 메서드를 실행하는 것 같다.

def create_or_find_by(attributes, &block)
  transaction(requires_new: true) { create(attributes, &block) }
rescue ActiveRecord::RecordNotUnique
  find_by!(attributes)
end

해당 기능을 사용하려면 RecordNotUnique 예외를 발생하기 위해서 데이터베이스의 유니크 제약을 걸어줘야 한다. migrate 파일 기준으로 작성하였습니다.

def change
  create_table :users do |t|
    t.string :name, index: { unique: true } 
  end
end

find_by_create_by보다 나은점

레코드가 존재하지 않으면 새로운 레코드를 생성해야 할 때 find_or_create_by를 사용한다. 이 경우 select -> insert 사이에 시차로 인해 여러 스레드 및 프로세스 환경에서 실행 시 race condition이 발생할 수 있다. 하지만 create_or_find_by의 경우 insert를 먼저 시도하고 데이터베이스 유니크 제약을 통해 흐름을 제어함으로써 race condition의 확률을 줄인다.

데이터베이스 유니크 제약이 걸린 상황에서 find_or_create_by를 사용한다고 했을 때 race_condition이 발생하면 RecordNotUnique 예외를 반환하기 때문에 애플리케이션에 오류가 발생할 것이다. create_or_find_by는 예외가 발생할 때 해당 예외를 catch하고 find_by를 하므로 오류가 발생하지 않는다.

단점

  • 데이터가 기존에 존재해서 생성이 실패하더라도 기본 키는 계속 늘어난다. 데이터가 엄청 많아졌을 때 정수 부족 문제가 가속화될 수도 있음.
  • 레이스 컨디션의 문제를 최소화할 수는 있지만 예외 처리를 통해 흐름을 제어하기 때문에 성능 면에서 느릴 수 있다. find_or_create_by보다 4배 정도 느리다는 것 같다.
  • 완전히 경쟁 조건이 사라지는 게 아니다. insert -> select 하는 과정에서 다른 프로세스에서 delete가 실행 시 race_conditon이 발생할 수 있다. 다만 이 확률은 매우 낮다고 하다.

참고

profile
Ruby on Rails 개발하고 있는 서버 개발자입니다.

0개의 댓글