Skip to content
isdnetworks
Go back

예외를 던지니 호출처가 롤백한다

유령 레코드가 나왔다.

하위 상품은 저장됨
이미지는 없음

Table of contents

Open Table of contents

증상 — 유령 레코드와 후보

중간에 죽은 흔적이라 v2CreateChildrenProduct 가 의심스러웠다.

public function v2CreateChildrenProduct($master)
{
    // 하위 상품 저장
    // 이미지 복사
}

v2CreateChildrenProduct 가 저장 뒤에 복사를 한다.

copy 가 실패하면 저장만 남아 유령 레코드가 되는 구조여서 여기에 트랜잭션을 넣어야 하나 싶었다.

판단 기준 — 실패가 무엇으로 오는가

트랜잭션을 넣기 전에 하나를 먼저 확인했다.

$disk->copy($from, $to);

copy 가 거짓을 반환하는지 예외를 던지는지가 갈린다.

[거짓 반환]  조용히 실패 → 다음 줄로 넘어감 → 유령 레코드
[예외]       중단 → 호출처로 전파

copy 의 이 한 가지로 결론이 정반대가 된다.

검증 — 소스를 직접 읽었다

의존 라이브러리의 copy 소스를 직접 열었다.

public function copy($path, $newpath)
{
    $this->assertPresent($path);   // 여기
    ...
}

assertPresent 로 존재 검사를 하고 없으면 예외를 던진다.

assertPresent 가 켜져 있는지도 확인했는데 설정 파일에 검사 비활성 항목이 아예 없었다. 항목이 없으면 기본값이 적용되고 그 기본값은 검사 활성이었다.

문서에는 copy 의 그 부분이 명확하지 않았고 설정에 따라 달라진다는 언급만 있었다. 그래서 동작을 확정하려면 소스를 직접 읽는 수밖에 없었다.

결과 — 호출처 넷 중 셋

copy 의 예외가 어디로 가는지 봤다.

grep -rn "v2CreateChildrenProduct" app/

v2CreateChildrenProduct 를 부르는 자리가 넷 나왔다.

// ProductController:2061
DB::beginTransaction();
$result = $service->v2CreateChildrenProduct($master);
if (!$result['result']) {
    DB::rollBack();
}

트랜잭션으로 감싸고 실패면 rollBack 을 부른다.

ProductController:2061         감쌈
ProductImportController:136    감쌈
ProductImportController:204    감쌈
ProductMigrationService:124    안 감쌈   ← 하나

넷 중 셋이 이미 감싸고 있었다.

결론 — 안 넣기로 했다

롤백 경로가 이미 성립하고 있었다.

복사 실패
    ↓ 예외
함수 안에서 catch → false 반환

호출처가 rollback

여기서 함수 안에 트랜잭션을 새로 넣었으면 중첩을 만들 뻔했다.

[전]  이 함수가 유령 레코드의 원인일 수 있음
[후]  호출처 네 곳 중 한 곳만 미보호

봐야 할 곳이 ProductMigrationService 한 곳으로 좁혀졌다.

교훈 — 용의자를 지우는 것

이번 유령 레코드가 ProductMigrationService 경로에서 나온 것인지도 확인했다.

아니었고 다른 경로에서 온 것이어서 이 함수는 이번 건의 원인이 아니었다.

[안 확인했으면]  여기에 트랜잭션 넣고 → 유령 레코드 또 나옴
[확인했으니]     여기를 지우고 다른 데를 봄

원인을 찾는 것과 원인이 아닌 것을 지우는 것이 둘 다 필요하다.

여기가 의심스러우니 일단 고쳐 두자로 가면 안 고쳐졌을 때 처음부터 다시 본다. 원인을 못 찾았어도 용의자를 지우는 것은 분명한 진전이었다.

정리


Share this post on:

Previous Post
저장소가 몇 개인지부터 적었다
Next Post
단계가 드러나는 폴더 구조