Skip to content
isdnetworks
Go back

누구 책임인지를 나타내는 값을 정의했다

연동 실패가 쌓이는데 우리 문제인지 상대 문제인지 매번 열어 봐야 알았다.

Table of contents

Open Table of contents

증상 — 이유가 문자열로만 있었다

fail_reason 으로 묶어 봤다.

SELECT fail_reason, COUNT(*) FROM sync_log WHERE result='FAIL' GROUP BY fail_reason;
Connection timed out                      412
Invalid parameter: courseId is required    88
SQLSTATE[23000]: Duplicate entry           41
cURL error 6: Could not resolve host        22

메시지가 제각각이라 GROUP BY 로 묶이지 않았다.

어느 것이 누구 몫인지도 한눈에 안 보였다. 상대 쪽 메시지가 바뀌면 그동안 묶어 두던 규칙이 한꺼번에 깨진다는 것도 있었다.

조치 — 책임 소재를 값으로 뒀다

sync_log 에 컬럼을 하나 더했다.

ALTER TABLE sync_log ADD COLUMN fault_side varchar(10);
OURS      우리 코드나 데이터 문제
THEIRS    상대 시스템 문제
NETWORK   중간 구간 문제
UNKNOWN   판단 못 함

fault_side 에 네 값 중 하나가 들어간다.

$side = match (true) {
    $e instanceof ValidationException => 'OURS',
    $e instanceof HttpServerError     => 'THEIRS',
    $e instanceof ConnectException    => 'NETWORK',
    default                           => 'UNKNOWN',
};

예외 종류로 판단했는데 메시지 문자열이 아니라 클래스로 봐야 안 흔들린다.

결과 — 분류별 건수

같은 기간을 fault_side 로 다시 묶었다.

SELECT fault_side, COUNT(*) FROM sync_log WHERE result='FAIL' AND at >= ? GROUP BY fault_side;
NETWORK   412
OURS       88
THEIRS     41
UNKNOWN    22

NETWORK 가 가장 많아서 우리 코드를 아무리 봐도 안 나올 것이었다.

어디를 봐야 하는지가 이 숫자로 갈렸다. 우리 쪽으로 분류된 것은 우리가 고칠 수 있고 상대 쪽은 알려야 하는 것이라 해야 할 일이 다르다.

대응 — 받는 사람별 알림

fault_side 별로 알림도 나눠 보냈다.

if ($ourFails > 10) {
    notify('개발팀', "우리 쪽 원인 연동 실패 {$ourFails}");
}
if ($theirFails > 50) {
    notify('운영팀', "상대 시스템 오류 {$theirFails}건. 상대에 문의 필요");
}

받는 사람이 달랐는데 전에는 전부 한 곳으로 가서 대부분 그냥 넘어갔다.

UNKNOWN 은 처음 22건이었고 열어 보면서 분류를 늘렸다.

if (str_contains($msg, 'Duplicate entry')) { return 'OURS'; }
if (str_contains($msg, 'rate limit'))      { return 'THEIRS'; }

몇 주에 걸쳐 UNKNOWN 이 0에 가까워졌고 새 UNKNOWN 이 나오면 알림이 가게 했다.

THEIRS 를 상대에게 알릴 근거도 생겼다.

2019-10-01 ~ 2019-10-15
  상대 시스템 5xx 응답: 412건
  주요 시각: 매일 02:00~03:00

숫자와 시각을 정리해 보내니 가끔 안 된다보다 이야기가 빨랐고 상대 쪽 배치가 그 시간에 돌면서 응답이 느려지는 것이었다.

예방 — 보내기 전에 거르기

OURS 로 분류된 것부터 줄였다.

OURS 88건
  필수 항목 누락  61
  중복 전송       19
  형식 오류        8

필수 항목 누락이 대부분이라 보내기 전에 확인을 넣었다.

$missing = array_diff($this->requiredFields(), array_keys(array_filter($payload)));
if ($missing) {
    throw new ValidationException('필수 항목 없음: ' . implode(',', $missing));
}

requiredFields 로 먼저 걸리니 실패가 아니라 안 보내는 것이 됐다.

상대에게 부담도 안 주고 88건이 12건이 됐다. 재시도 정책도 갈렸는데 우리 문제는 다시 보내도 안 되고 코드를 고쳐야 한다.

$retryable = in_array($side, ['NETWORK', 'THEIRS'], true);

전에는 전부 재시도해서 같은 것을 다섯 번씩 실패하고 있었다.

주의 — 틀릴 수 있는 판단

Exception 종류로 판단하니 대부분 맞았지만 틀리는 경우가 있었다.

상대가 400 을 주는데 실제로는 우리 요청이 아니라 상대 쪽 규격이 바뀐 것이었다.

if ($status === 400) {
    // 우리 요청이 잘못됐거나 상대 규격이 바뀌었다
    return $this->recentSuccessRate() > 0.9 ? 'THEIRS' : 'OURS';
}

조금 전까지 잘 되던 것이 갑자기 400 이면 상대가 바뀐 쪽에 가깝다.

계속 400 이었으면 우리 문제다. 완벽하지는 않지만 처음 볼 곳을 정하는 데는 충분했고 틀렸을 때 값을 다시 고칠 수 있게 뒀다.

정리


Share this post on:

Previous Post
열 개였던 쿼리가 하나였다
Next Post
단방향 동기화와 반대편의 0