Operating a Quarantine: Accumulate, Break, Requeue
한국어 원문으로 표시합니다.
목표
기대치를 파일로 적어 두고, 드롭을 실행마다 줄 단위로 걸러 sqlite 의 사실 표와 격리 표로 나눠 쌓는 문지기 qgate.py 를 만든다. 너무 많이 틀린 실행은 통째로 끊고, 고친 줄을 다시 넣되 이중 계산이 안 나게 하고, 나이 든 격리를 잡아내고, 실행 상태와 자료 상태를 갈라 보고한다.
왜 중요한가
못 쓰는 줄을 버리지 않고 격리에 남기는 것까지는 대개 한다. 문제는 그다음이다. 격리는 실행마다 다시 쌓이고, 반년 뒤에 열어 보면 4만 줄이 들어 있고, 그동안 집계는 그만큼 빠진 채로 나가고 있었다. 파이프라인은 매일 성공했다 — 성공은 오류 없이 끝났다는 뜻이지 넣어야 할 것을 다 넣었다는 뜻이 아니다. 줄 하나가 틀린 것과 파일 전체가 틀린 것도 다른 사건이다. 상류가 칼럼 순서를 바꿔 보내면 거의 모든 줄이 어긋나는데, 그때 줄 단위로 격리하면 격리 표에 만 줄이 들어가고 집계는 빈 채로 나간다. 그런 파일은 한 줄도 넣지 않고 실행을 끊는 편이 낫다. 가르는 기준은 비율이고, 그 비율은 둥근 수가 아니라 평소 실패 비율에서 정한다. 그리고 고친 줄을 다시 넣는 고리가 필요하다. 재투입은 사람이 손으로 돌리는 일이라 반드시 여러 번 실행되고, 그때 사실 표에 두 번 들어가거나 이미 닫힌 격리를 또 닫아 "3건 해결" 이 두 번 보고되기 쉽다. 채점기는 여러분이 적어 낸 문구를 믿지 않는다. 임시 디렉터리에 채점기가 만든 드롭과 기대치를 차려 놓고 여러분의 문지기를 실제로 실행한 뒤, 만들어진 sqlite 파일을 직접 열어 사실과 격리와 실행 기록을 채점기가 직접 센 값과 대조한다. 임계치와 상호와 금액은 실행마다 바뀐다.
단계
- /root/quarantine/gen_drops.py 를 만들어 실행해 /root/quarantine/drops 아래 드롭 넷과 /root/quarantine/expectations.json 을 만드세요.
- /root/quarantine/qgate.py 에
check를 만들어 기대치로 세어만 보게 하세요. run을 더해 sqlite 에 사실과 격리와 실행 기록을 쌓게 하세요.run --max-fail-ratio를 더해 너무 많이 틀린 실행을 통째로 끊게 하세요.requeue를 더해 고친 줄을 다시 넣되 두 번 넣어도 숫자가 안 변하게 하세요.aging을 더해 나이 든 격리를 잡아내게 하세요.status를 더해 실행 상태와 자료 상태를 갈라 보고하게 하세요.- 자기 드롭 넷을 차례로 돌려 /root/quarantine/pipeline.db 를 만들고 /root/quarantine/status.json 과 /root/quarantine/quarantine_report.md 를 쓰세요.
참고
- 실행 계약:
python3 /root/quarantine/qgate.py <명령> .... 답은 JSON 한 덩어리로 표준출력에 냅니다. 성공하면 종료 코드 0, 파일이 없으면 3, 사용법이 틀리면 2, 실행이 끊기면 5 입니다. - 드롭과 재투입 파일의 머리글은
order_id,shop,qty,amount,status입니다. 재투입 파일은 아무 데나 두어도 됩니다 — 5단계 정답지는/tmp/fixes.csv에 만듭니다. - 기대치 파일은
{"version": 1, "rules": [...]}이고 규칙마다name·column·type이 있습니다.type은 다섯 가지입니다 —not_null,regex(pattern),range(min·max, 경계 포함),enum(values),unique(그 파일 안에서 처음 나온 값만 통과). - 규칙 적용 규칙: 값은 앞뒤 공백을 떼고 봅니다. 빈 값은
not_null말고는 전부 실패입니다. 정규식은 값 전체와 맞아야 합니다. 유일성은 파일의 앞줄부터 봅니다. check응답:{"drop": 이름, "rows": 정수, "passed": 정수, "failed": 정수, "fail_ratio": 소수, "by_rule": {규칙이름: 정수}}.failed는 한 규칙 이상을 어긴 줄의 수이고by_rule은 규칙마다 어긴 줄의 수라 합이 다를 수 있습니다.by_rule에는 기대치의 모든 규칙 이름이 0 이어도 들어갑니다.fail_ratio는 소수점 여섯째 자리에서 반올림합니다.- sqlite 표는 셋입니다.
runs(run_id, drop_name, status, rows, passed, failed, loaded, quarantined),facts(order_id, shop, qty, amount_cents, run_id),quarantine(q_id, order_id, rule, detail, raw, first_run_id, resolved_run_id).run_id와q_id는 자동 증가이고order_id는 사실 표의 기본 키입니다.amount_cents는 금액을 정수 센트로 바꾼 값입니다. run응답:check의 키에run_id·status·loaded·quarantined·max_fail_ratio가 더해집니다.status는loaded또는broken입니다.- 격리는 같은 줄의 같은 사유가 다시 와도 한 번만 엽니다(열려 있는 같은
order_id·rule이 있으면 새로 넣지 않습니다). 한 줄이 두 규칙을 어기면 격리는 두 줄이 됩니다. --max-fail-ratio기본값은 1.0 이라 끊지 않습니다.fail_ratio가 임계치를 넘으면(같으면 통과) 실행을 끊습니다. 끊긴 실행은runs에broken으로 남기고 사실도 격리도 한 줄도 넣지 않으며 종료 코드 5 입니다.requeue응답:{"run_id": 정수, "status": "requeued", "rows": 정수, "fixed": 정수, "still_failing": 정수, "resolved": 정수, "facts": 정수}. 재투입도 실행이므로runs에requeued로 한 줄 남습니다. 통과한 줄은 사실 표에 갱신으로 넣고, 그order_id의 아직 열려 있는 격리만 닫습니다.facts는 넣은 뒤의 사실 표 건수입니다.aging응답:{"max_age_runs": 정수, "latest_run_id": 정수, "open": 정수, "aged": 정수, "by_rule": {규칙이름: 정수}, "oldest": {"q_id": 정수, "order_id": 문자열, "rule": 문자열, "age": 정수} 또는 null, "alert": 참거짓}. 나이는latest_run_id - first_run_id이고max_age_runs이상이면 나이 든 것입니다.by_rule에는 나이 든 격리가 있는 규칙만 담고,oldest는 열려 있는 격리 중first_run_id가 가장 작은 것(같으면q_id가 작은 것)입니다.status응답:{"runs": {"total": 정수, "loaded": 정수, "broken": 정수, "requeued": 정수, "last_status": 문자열}, "data": {"facts": 정수, "open": 정수, "resolved": 정수, "aged": 정수}, "runs_ok": 참거짓, "data_ok": 참거짓}.runs_ok는 실행이 있고 마지막 실행이broken이 아닌가이고,data_ok는 열린 격리가 0 이고 나이 든 격리가 0 인가입니다.--max-age-runs기본값은 3 입니다.- 8단계의 보고서는
## 이번 실행## 격리에 무엇이 쌓였나## 재투입과 이중 계산## 임계치를 무엇으로 정했나네 절로 쓰고, 사실 건수와 열린 격리 건수를 숫자로 적습니다. - 공식 문서: sqlite3 모듈 · UPSERT · re.fullmatch · csv
- 데이터베이스를 눈으로 보려면
sqlite3 /root/quarantine/pipeline.db 'select * from runs'를 쓰세요. - 흔한 실수: 격리를 실행마다 다시 넣어 같은 줄이 쌓이기, 재투입을 삽입으로 넣어 사실이 두 배가 되기, 이미 닫힌 격리를 또 닫기, 임계치를 평소 값이 아니라 둥근 수로 정하기.
드롭과 기대치를 파일로 두기
/root/quarantine/gen_drops.py 를 만들어 실행해 /root/quarantine/drops 아래 드롭 넷과 /root/quarantine/expectations.json 을 만드세요. 기대치는 다섯 가지 type 을 모두 쓰고, 드롭 하나는 절반이 넘게 기대치를 어겨야 합니다.
기대치를 코드가 아니라 파일로 두면 상류와 그 파일을 놓고 이야기할 수 있고, 어긴 규칙의 이름이 그대로 격리 사유가 됩니다. 드롭에는 상태값이 목록에 없는 줄, 음수 금액, 범위를 넘는 수량, 빈 상호, 규격을 안 지킨 전표 번호, 앞줄과 같은 전표 번호를 섞으세요. 셋은 어기는 줄이 절반 아래여야 하고 하나는 절반을 넘어야 합니다.
기대치로 세어만 보기
/root/quarantine/qgate.py 에 check --drop <파일> --expect <파일> 을 만들어 drop·rows·passed·failed·fail_ratio·by_rule 을 JSON 으로 내게 하세요.
failed 는 한 규칙 이상을 어긴 줄의 수이고 by_rule 은 규칙마다 어긴 줄의 수라 합이 다릅니다 — 한 줄이 두 규칙을 어길 수 있기 때문입니다. by_rule 에는 아무도 안 어긴 규칙도 0 으로 넣으세요. 값이 비어 있으면 not_null 말고는 전부 실패로 봅니다.
사실과 격리를 나눠 쌓기
run --drop <파일> --db <파일> --expect <파일> 을 더해 통과한 줄은 facts 에 갱신으로 넣고, 어긴 줄은 어긴 규칙마다 quarantine 에 열고, 실행을 runs 에 남기게 하세요. 같은 줄의 같은 사유가 다시 와도 격리는 한 번만 엽니다.
표 셋의 칼럼 이름과 순서는 참고 절에 있습니다. 사실 표는 order_id 가 기본 키이므로 갱신으로 넣습니다. 격리를 열기 전에 같은 order_id 와 rule 로 아직 열려 있는 줄이 있는지 보세요 — 안 보면 실행마다 같은 줄이 쌓입니다.
너무 많이 틀린 실행은 통째로 끊기
run --max-fail-ratio R 을 더해 fail_ratio 가 임계치를 넘으면 실행을 끊게 하세요. 끊긴 실행은 runs 에 broken 으로 남기고 사실도 격리도 한 줄도 넣지 않으며 종료 코드 5 입니다.
줄 하나가 틀린 것과 파일 전체가 틀린 것은 다른 사건입니다. 임계치는 둥근 수가 아니라 평소 실패 비율에서 정합니다 — check 로 드롭 넷의 비율을 재어 보고 정하세요. 끊을 때 절반만 넣고 멈추면 다음 사람이 어디까지 들어갔는지 판단해야 합니다.
고친 줄을 다시 넣기
requeue --db <파일> --fixes <파일> --expect <파일> 을 더해 고친 줄을 기대치로 다시 보고, 통과하면 사실 표에 갱신으로 넣고 그 전표의 아직 열려 있는 격리만 닫게 하세요. 같은 재투입을 두 번 실행해도 사실 건수와 닫힌 격리 수가 늘지 않아야 합니다.
재투입은 사람이 손으로 돌리는 일이라 반드시 여러 번 실행됩니다. 삽입으로 넣으면 사실이 두 배가 되고, 닫을 때 열림 조건을 안 걸면 '3건 해결' 이 두 번 보고됩니다. 두 번째 실행의 resolved 는 0 이어야 합니다. 전표 번호 자체가 망가진 줄은 재투입으로 못 고칩니다 — 번호를 고치면 다른 줄이 되기 때문입니다.
나이 든 격리를 잡아내기
aging --db <파일> [--max-age-runs N] 을 더해 열려 있는 격리의 나이를 latest_run_id - first_run_id 로 재고, max_age_runs 이상인 것을 세어 open·aged·by_rule·oldest·alert 로 내게 하세요.
건수만 보면 안 줄어드는 것을 못 봅니다. 나이는 시각보다 실행 횟수로 재는 편이 다루기 쉽습니다 — 파이프라인이 안 돌면 나이도 안 먹는 편이 사람의 감각과 맞습니다. oldest 는 열려 있는 것 중 first_run_id 가 가장 작은 것입니다.
성공했다와 맞다를 갈라 보고하기
status --db <파일> [--max-age-runs N] 을 더해 실행 상태(runs)와 자료 상태(data)를 갈라 내고 runs_ok·data_ok 두 값으로 답하게 하세요.
실행이 성공했다는 것과 자료가 맞다는 것은 다른 축입니다. 한 칸에 뭉치면 둘 다 못 봅니다. runs_ok 는 실행이 있고 마지막 실행이 끊기지 않았는가이고, data_ok 는 열린 격리와 나이 든 격리가 모두 0 인가입니다 — 둘 다 참인 날은 드뭅니다.
자기 드롭으로 한 주를 돌리고 보고하기
자기 드롭 넷을 날짜순으로 돌려 /root/quarantine/pipeline.db 를 만들고, status 의 답을 /root/quarantine/status.json 에 남기고, /root/quarantine/quarantine_report.md 를 네 절로 쓰세요. 중단 임계치는 평소 실패 비율을 재어 정하세요.
임계치를 평소 값에서 정하면 절반이 넘게 어긴 그날치만 끊깁니다. status.json 은 손으로 쓰지 말고 status 명령의 답을 그대로 저장하세요 — 채점기는 데이터베이스를 직접 열어 대조합니다. 보고서에는 사실 건수와 열린 격리 건수를 숫자로 적으세요.