Skip to content
isdnetworks
Go back

다시 받다 끊겼는데 원래 있던 것도 없어졌다

분류 자료를 새벽에 다시 받는 작업이 있다. 어느 날 아침에 분류가 전부 사라져 있었다. 상품은 그대로인데 분류가 없어서 목록이 안 나왔다.

Table of contents

Open Table of contents

원인 — 지우고 나서 받고 있었다

코드는 이랬다.

$this->db->trans_start();

$this->db->empty_table('category');

foreach ($fetched as $c) {
    $this->db->insert('category', $c);
}

$this->db->trans_complete();

empty_table 로 전부 지우고 받은 것을 넣는다. 트랜잭션 안에 있으니 실패하면 되돌아갈 것 같다.

로그를 보니 이랬다.

[분류동기화] 시작
[분류동기화] 원본 조회 실패: cURL error 28: Operation timed out

조회에서 실패해 $fetched 가 빈 배열이 됐다. 빈 배열로 반복문을 돌면 아무것도 안 넣고 그건 오류가 아니다.

트랜잭션은 정상 종료된다. empty_table 은 성공하고 insert 는 0건이다. 지우는 것과 넣는 것의 순서가 사고의 크기를 정하고 있었다.

받아서 확인한 뒤에 지웠다

순서를 바꿨다.

$fetched = $this->fetchCategories();

if (empty($fetched)) {
    log_message('error', '분류 조회 0건 - 동기화 중단');
    return false;
}

$this->db->trans_start();
$this->db->empty_table('category');
foreach ($fetched as $c) { $this->db->insert('category', $c); }
$this->db->trans_complete();

fetchCategories 로 받아서 확인한 뒤에 지운다. 0건이면 아예 시작을 안 하니 실패해도 원래 자료가 남는다.

여기서 걸리는 것이 있었다. 분류가 정말 0개가 되는 경우도 이론상 있는데 지금까지 200개 안팎이었으니 0개는 정상 상황이 아니다.

$prev = $this->db->count_all('category');
$now  = count($fetched);

if ($prev > 0 && $now < $prev * 0.5) {
    log_message('error', "분류 급감: {$prev}{$now}. 동기화 중단");
    return false;
}

count_all 로 이전 개수를 보고 절반 이하로 줄면 멈춘다. 기준을 절반으로 잡은 것은 임의지만 아무 기준도 없는 것보다 낫다.

지우지 않는 쪽으로 바꿨다

더 나은 방향은 지우지 않는 것이었다.

foreach ($fetched as $c) {
    $this->db->replace('category', $c);   // 있으면 갱신, 없으면 삽입
}

replace 로 받은 것만 갱신하거나 넣으면 기존 것은 그대로 있다. 다만 이러면 삭제된 분류가 남는 문제가 생겨서 표시 여부 컬럼으로 처리했다.

// 전부 미표시로 두고
$this->db->update('category', ['use_yn' => 'N']);

// 받은 것만 표시로
foreach ($fetched as $c) {
    $c['use_yn'] = 'Y';
    $this->db->replace('category', $c);
}

use_ynY 인 것만 화면에 보인다. 이것도 중간에 끊기면 전부 미표시로 남으므로 둘을 한 트랜잭션에 넣었다.

행이 지워지지 않으니 되돌리기도 쉽다. 지우는 동작 자체를 없애니 실패했을 때의 최악이 훨씬 가벼워졌다.

참조하는 쪽과 어제 상태

분류를 지우면 그 분류를 쓰는 상품이 붕 뜬다.

SELECT COUNT(*) FROM product p
LEFT JOIN category c ON c.category_no = p.category_no
WHERE c.category_no IS NULL;

FOREIGN KEY 가 걸려 있었으면 애초에 못 지웠을 텐데 그게 없었다. 그래서 동기화 뒤에 LEFT JOIN 으로 확인하게 했고 0이 아니면 목록으로 남긴다.

지워진 분류를 쓰던 상품은 어느 목록에도 안 나온다. 오류가 아니라서 눈에 안 띈다.

동기화 전 상태도 남겼다.

DROP TABLE IF EXISTS category_bak;
CREATE TABLE category_bak AS SELECT * FROM category;

문제가 생기면 되돌린다.

TRUNCATE category;
INSERT INTO category SELECT * FROM category_bak;

category_bak 은 하루치만 들고 있는다. 그 이상은 원본에서 다시 받는 것이 낫고 남기는 비용보다 없을 때의 비용이 훨씬 컸다.

정리


Share this post on:

Previous Post
같은 파일을 둘이 동시에 고쳤다
Next Post
장비마다 필요한 계정이 달랐다