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로 선택된 사용자 레코드는 해당 트랜젝션이 완료될 때까지 다른 트랜잭션는 대기 상태로 만들어 동시성 문제를 방지하게 합니다.

좋아보이지만 과도한 사용은 데드락이나 성능에 이슈를 발생시켜 참고하여 사용해야겠습니다.