PHP
[Laravel] beginTransaction, lockForUpdate 활용 (일관성, 동시성) with. ORM
역발산기개세
2024. 8. 12. 17:49
[문제 상황]
특정 endPoint에 거의 동시간(1초 이내 시간) 통신요청이 들어와 DB에 중복적인 데이터가 처리됨
[문제 당시 코드]
단순한 upsert 코드였지만 1초 이내에 아래 로직을 실행 시 동일한 pk_id를 가진 행이 실행 수 만큼 생성이 되었습니다.
<?php
DB::beginTransaction();
try {
OrderTradeData::updateOrCreate(['pk_id' => $pkId], [$upsertVo]);
DB::commit();
} catch (Exception $ee) {
DB::rollBack();
}
transaction으로 해당 로직을 묶었지만 transaction은 일관성을 유지할 뿐 선택 된 행에 대해 동시성 문제를 해결하지 못하는 상황이 연출되었습니다.
[해결 코드]
<?php
DB::beginTransaction();
try {
OrderTradeData::lockForUpdate()->updateOrCreate(['pk_id' => $pkId], [$upsertVo]);
DB::commit();
} catch (Exception $ee) {
DB::rollBack();
}
lockForUpdate는 transaction안에서만 작동을 하며 lockForUpdate로 선택된 사용자 레코드는 해당 트랜젝션이 완료될 때까지 다른 트랜잭션는 대기 상태로 만들어 동시성 문제를 방지하게 합니다.
좋아보이지만 과도한 사용은 데드락이나 성능에 이슈를 발생시켜 참고하여 사용해야겠습니다.