유령 레코드가 나왔다.
복제 과정은 저장됨
첨부 파일은 없음
Table of contents
Open Table of contents
증상 — 유령 레코드와 후보
중간에 죽은 흔적이라 create_child_course 가 의심스러웠다.
public function create_child_course($master)
{
// 복제 과정 저장
// 첨부 파일 복사
}
create_child_course 가 저장 뒤에 복사를 한다.
복사가 실패하면 저장만 남아 유령 레코드가 되는 구조여서 여기에 트랜잭션을 넣어야 하나 싶었다.
판단 기준 — 실패가 무엇으로 오는가
트랜잭션을 넣기 전에 하나를 먼저 확인했다.
$fs->create_file_from_storedfile($record, $source);
create_file_from_storedfile 이 거짓을 반환하는지 예외를 던지는지가 갈린다.
[거짓 반환] 조용히 실패 → 다음 줄로 넘어감 → 유령 레코드
[예외] 중단 → 호출처로 전파
이 한 가지로 결론이 정반대가 된다.
검증 — 소스를 직접 읽었다
Moodle 의 lib/filestorage/file_storage.php 를 직접 열었다.
public function create_file_from_storedfile($filerecord, $fileorid) {
...
if (!$fs->file_exists(...)) {
throw new file_exception('storedfileproblem', 'File does not exist'); // 여기
}
원본이 없으면 file_exception 을 던지고 레코드의 contextid 나 filearea 가 틀려도 같은 예외다.
문서에는 반환값과 예외가 명확하지 않았다. 문서만 보는 것 대신 소스를 읽는 쪽을 골랐다. 이유는 동작을 확정하려면 던지는 자리를 직접 봐야 하고 그 자리는 문서에 없어서다.
결과 — 호출처 넷 중 셋
file_exception 이 어디로 가는지 봤다.
grep -rn "create_child_course" local/
create_child_course 를 부르는 자리가 넷 나왔다.
// course_controller.php:206
$tx = $DB->start_delegated_transaction();
try {
$service->create_child_course($master);
$tx->allow_commit();
} catch (Exception $e) {
$tx->rollback($e);
}
트랜잭션으로 감싸고 예외면 rollback 을 부른다.
course_controller.php:206 감쌈
course_import.php:136 감쌈
course_import.php:204 감쌈
course_migration.php:124 안 감쌈 ← 하나
넷 중 셋이 이미 감싸고 있었다.
결론 — 안 넣기로 했다
롤백 경로가 이미 성립하고 있었다.
복사 실패
↓ file_exception
함수를 빠져나감
↓
호출처가 rollback
여기서 함수 안에 트랜잭션을 새로 넣었으면 중첩을 만들 뻔했다.
[전] 이 함수가 유령 레코드의 원인일 수 있음
[후] 호출처 네 곳 중 한 곳만 미보호
봐야 할 곳이 course_migration.php 한 곳으로 좁혀졌다.
교훈 — 용의자를 지우는 것
이번 유령 레코드가 course_migration.php 경로에서 나온 것인지도 확인했다.
아니었고 다른 경로에서 온 것이어서 이 함수는 이번 건의 원인이 아니었다.
[안 확인했으면] 여기에 트랜잭션 넣고 → 유령 레코드 또 나옴
[확인했으니] 여기를 지우고 다른 데를 봄
원인을 찾는 것과 원인이 아닌 것을 지우는 것이 둘 다 필요하다. 여기가 의심스러우니 일단 고쳐 두자로 가면 안 고쳐졌을 때 처음부터 다시 본다. 원인을 못 찾았어도 용의자를 지우는 것은 분명한 진전이었다.
정리
- 실패가 거짓 반환인지 예외인지가 롤백 성립을 가른다
- 라이브러리 소스를 직접 읽어 확인한다
- 어떤 조건에서 어떤 예외가 나는지를 던지는 자리에서 본다
- 호출처를 전부 세어 각각의 보호 여부를 본다
- 이미 성립하면 안쪽에 트랜잭션을 새로 넣지 않는다
- 미보호 구간이 한 곳으로 좁혀진다
- 용의자를 지우는 것도 진전이다
- 의심만으로 고치면 안 고쳐졌을 때 처음부터 다시 본다