opencrab.pack.load 의 적재 함수들은 (ok, skip, err) 계열 튜플만 돌려주고, 호출자는 그것으로 "이번 적재가 온전했는가"를 판정할 수 없다.
왜 판정이 안 되는가
err 는 필요조건도 충분조건도 아니다.
- 필요조건이 아니다:
docs.upsert_source 예외가 except Exception: pass 에 삼켜지면 err=0 이 된다. 저장이 실패했는데 온전한 적재로 보인다. (load.py 의 삼킴 자리는 아래 재현 명령으로 전수 확인)
- 충분조건이 아니다:
id_map[node_id] 가 try 밖(load.py:417)이라 문법위반으로 저장되지 않은 노드도 endpoint 로 등록된다. 그 노드를 참조하는 엣지는 endpoint 검사(:564)를 통과하고 add_edge 에서 저장 실패해 err 를 올린다. 문법위반은 결정적이라 다음 실행에도 같은 결과다.
함께 없는 것
- 정리 잔여(pending cleanup): 타입 변경에서 구 graph 노드는 지워지는데 구 doc 행 삭제가 실패하면 그 사실이 어디에도 안 남는다. 다음 실행은
same 으로 끝나 재시도조차 안 한다.
- 프로세스 경계: 운영 파이프라인은 로더를 subprocess 로 돌리고 종료 코드만 본다. 결과 객체를 아무리 정교하게 만들어도 경계를 못 넘는다. (호출자 쪽은 opencrab-dump 리포에 별도 등록)
- readback: 삭제가 실제로 반영됐는지 확인하지 않는다.
설계 요건
- 어떤 identity 도
stored_ok | deterministic_skip | retryable_fail | unknown 중 정확히 하나에 속하고, 합이 입력과 다르면 그 자체가 unknown 이어야 한다.
- 완료를
ingest_complete / cleanup_allowed / cleanup_complete 로 가른다.
unknown 이 있으면 그 축의 정리를 허용하지 않는다.
- 정리 잔여를 durable 하게 남기고 다음 실행이
same 이어도 먼저 처리한다. write-first · fingerprint 조건부 삭제 · idempotent retry 를 만족해야 한다.
- 기계 판독 가능한 완료 리포트를 내서 subprocess 소비자가 종료 코드 외에 상태를 읽게 한다.
범위
이관(#93)이 만든 결함이 아니라 이관 전부터 있던 구조다. #93 PR 은 이관 충실성에 집중하므로 여기서 분리한다.
재현
PYTHONPATH=/Users/asdf/localcrab-migration python3 - <<'PY'
import ast, pathlib
src = pathlib.Path("opencrab/pack/load.py").read_text()
for n in ast.walk(ast.parse(src)):
if isinstance(n, ast.ExceptHandler) and len(n.body)==1 and isinstance(n.body[0], ast.Pass):
print("삼킴:", n.lineno)
PY
관련
#93 (이관), #160 (타입 변경 공존 창), #158 (영수증 미검사 호출자)
opencrab.pack.load의 적재 함수들은(ok, skip, err)계열 튜플만 돌려주고, 호출자는 그것으로 "이번 적재가 온전했는가"를 판정할 수 없다.왜 판정이 안 되는가
err는 필요조건도 충분조건도 아니다.docs.upsert_source예외가except Exception: pass에 삼켜지면err=0이 된다. 저장이 실패했는데 온전한 적재로 보인다. (load.py의 삼킴 자리는 아래 재현 명령으로 전수 확인)id_map[node_id]가 try 밖(load.py:417)이라 문법위반으로 저장되지 않은 노드도 endpoint 로 등록된다. 그 노드를 참조하는 엣지는 endpoint 검사(:564)를 통과하고add_edge에서 저장 실패해err를 올린다. 문법위반은 결정적이라 다음 실행에도 같은 결과다.함께 없는 것
same으로 끝나 재시도조차 안 한다.설계 요건
stored_ok | deterministic_skip | retryable_fail | unknown중 정확히 하나에 속하고, 합이 입력과 다르면 그 자체가unknown이어야 한다.ingest_complete/cleanup_allowed/cleanup_complete로 가른다.unknown이 있으면 그 축의 정리를 허용하지 않는다.same이어도 먼저 처리한다. write-first · fingerprint 조건부 삭제 · idempotent retry 를 만족해야 한다.범위
이관(#93)이 만든 결함이 아니라 이관 전부터 있던 구조다. #93 PR 은 이관 충실성에 집중하므로 여기서 분리한다.
재현
관련
#93 (이관), #160 (타입 변경 공존 창), #158 (영수증 미검사 호출자)