Skip to content
isdnetworks
Go back

등록은 롤백되고 수정은 안 된다

이미지 업로드를 외부 서비스로 넘기는 작업을 했다. 넘기기 전에 물어야 할 것이 있었는데 업로드가 실패하면 DB 가 어떻게 되느냐였다.

Table of contents

Open Table of contents

사전 준비 — 실패를 주입했다

코드를 읽는 것만으로는 확신이 안 섰다.

https://.../does-not-exist.jpg

does-not-exist.jpg 를 보내면 검증은 통과하고 다운로드 단계에서 실패한다.

URL 검증이 호스트만 보기 때문에 가능한 방법이었다. 실패 지점을 정확히 고를 수 있으니 어느 단계에서 깨지는지가 갈린다.

검증 — 등록 경로

먼저 v2RegisterProduct 쪽을 봤다.

public function v2RegisterProduct($data)
{
    DB::transaction(function () use ($data) {
        $this->inner($data);      // 모든 행 저장
    });                           // 커밋

    $this->flush();               // 외부 업로드
}

DB::transaction 이 끝난 뒤에 flush 로 외부에 올린다.

실패를 주입하니 이렇게 됐다.

DB      전부 롤백
외부    아직 아무것도 안 올라감

ROLLBACK 으로 되돌아가 정리할 것이 남지 않았다.

결과 — 수정 경로는 달랐다

modifyProduct 는 순서가 달랐다.

public function modifyProduct($product, $data)
{
    $product->fill($data);
    $product->save();       // 트랜잭션 없음

    $this->flush();         // 외부 업로드
}

save 가 트랜잭션 없이 즉시 반영되고 그 뒤에 flush 가 돈다.

같은 실패를 주입했다.

미디어   보존 (롤백됨)
이름     바뀐 채로 남음   ← 여기

미디어는 되돌아갔는데 이름 같은 스칼라 필드가 바뀐 채로 남았다.

원인 — 트랜잭션 경계의 차이

v2RegisterProductmodifyProduct 를 나란히 놓으니 이유가 보였다.

[등록]  전부 저장 → 커밋 → 업로드
        업로드 실패 시 커밋 전이면 롤백

[수정]  save() 로 즉시 영속 → 업로드
        업로드 실패해도 save() 는 이미 끝남

flush 가 트랜잭션 안에 있느냐 밖에 있느냐가 갈랐다.

같은 서비스의 등록과 수정인데 원자성이 달랐다. 코드만 읽었으면 둘 다 트랜잭션이 있거나 둘 다 없을 것이라고 가정했을 것이다.

조치 — 한쪽만 손대기

결과가 갈렸으므로 할 일도 갈렸다.

[등록]  비이미지 실패는 구조상 이미 롤백으로 충족
        외부 정리 로직을 따로 만들 필요 없음

[수정]  시작 시점 스냅샷 → 업로드 실패 처리에서 복원

v2RegisterProduct 쪽은 손대지 않았다.

이미 되는 쪽을 함께 고치면 바꿀 이유가 없는 코드를 건드리게 되고 그만큼 위험이 는다. 무엇을 안 고칠지 정하는 것도 이 조사의 결과였다.

선택지 — 단일 게이트

외부 위임 여부를 정하는 자리는 코드 전체에 하나였다.

if (BflowImageUploader::enabled()) { ... }

enabled 하나가 등록과 수정과 하위 생성과 전파와 본문 등록의 진입점을 모두 가른다.

이것이 양날이었다.

[불가능]  일부 경로만 새 방식으로 (혼합 모드)
[보장]    끄면 전 경로가 옛 방식으로 떨어짐

단계적 전환은 못 하지만 되돌리는 길이 하나로 확실하다.

혼합이 되면 어떤 경로가 새 방식인지 추적해야 하고 문제가 나면 어느 쪽인지부터 가려야 한다. 이미지 경로가 여섯 곳이라 그 추적 비용이 커서 전환이 불편한 대신 상태가 단순한 쪽을 골랐다.

정리


Share this post on:

Previous Post
태그가 말해 주지 않는 베이스
Next Post
소스에서 거꾸로 스키마를 그렸다