2021. 2. 5. 15:14ㆍ프로그래밍-Web/MongoDB
1. Upsert 동작 원리 및 문제 발생 상황
upsert는 filter에 정의된 Document를 확인한 후, filter에 해당하는 document가 존재하는 경우 update, 존재하지 않는 경우 insert를 수행하게 하는 옵션이다
Upsert의 수행과정을 나눠보면 조건확인 -> 쿼리실행의 순서로 진행된다. 이렇게 Step을 나누는 이유는 upsert가 모든 절차에 대해 Atomic하지 않고, 각 step마다 atomic하기 때문이다. 따라서 Upsert 옵션이 true인 여러개의 쿼리를 순차적으로 실행하는 경우, 1번 원소의 쿼리실행이 끝나지 않았음에도 2번 원소에 대한 쿼리가 실행될 수 있다.
예시 코드는 itemList에 대한 find를 수행한 후, 조건에 따라 itemList의 value를 업데이트하도록 되어있다. 1번 원소에 대한 쿼리가 실행되면서 itemList의 value를 변경한다면, 2번 원소에 대한 쿼리는 1번 원소에 대한 쿼리가 완전히 끝난 후 실행되어야 한다. 왜냐하면 2번 원소가 itemList를 find해오는 동안, 1번 원소가 itemList의 value를 바꿀 수 있기 때문이다.
이때 filter옵션과 update옵션이 동일한 key를 가리킨다면 'E11000 duplicate key error' 가 발생한다. 1번 원소에 대한 update가 끝나지 않았음에도 2번 원소가 filter조건으로 인해 동일한 key에 대한 find를 수행하기 때문이다.
이로 인해 index 키의 충돌이 발생한다. MongoDB 드라이버는 새로 생성되는 document id에 대해 자동으로 index를 생성하는데, 1번 원소에 대한 쿼리에서 index를 생성했음에도 2번 원소에 대한 쿼리는 그것을 인지하지 못하고 동일한 index를 생성하기 때문이다.
2. 해결책
다양한 방식으로 해결이 가능하지만 크게 네가지 방법으로 정리된다
1) updataMany를 하는 경우(또는 여러개의 updateOne을 Loop나 bulkwrite를 통해 연속으로 진행하는 경우) upsert 옵션을 사용하지 않는다
2) 부득이하게 upsert를 사용하는 경우 filter의 조건은 쿼리에 의해 변하지 않는 key들로 설정해야 한다.
3) 두 가지 옵션 모두 사용할 수 없는 경우, 하나의 업데이트가 완전히 끝난 후 다음 업데이트 구문이 진행될 수 있도록 로직을 만들어야 한다.
4) 인덱스가 자동으로 생성되지 않도록 설정할 수 있으나 권장되는 바는 아니다.
'프로그래밍-Web > MongoDB' 카테고리의 다른 글
[aggregate] 기본적인 aggregate 사용 방식 & 배열이 존재하는 경우 (0) | 2021.02.10 |
---|---|
[정리] Mongoose (2) 도큐먼트와 쿼리 (0) | 2021.02.09 |
[정리] Mongoose (1) 스키마와 모델 (0) | 2021.02.08 |
[이슈] 몽고DB 드라이버 비동기 처리 이슈 (0) | 2021.01.14 |
[자료정리] push vs addToset, 그리고 배열의 update조작 (0) | 2021.01.11 |