You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Postgres 백엔드에서 Idempotency-Key가 경고 없이 무시된다 — 드라이버 opt-in 미지원을 기동 시 감지하고 TCK에 멱등성 계약을 넣는다 #228
문제 (실측, main 9611855(v0.8.0 + 168커밋), 2026-10-11 실측)
impl/lnpl/wsgi.py:1961(요청 경로)과 :2121(이벤트 소비 경로)은 멱등성 클레임을 hasattr(repository, "idempotency_begin")로 드라이버 opt-in으로 건다. 지원하지 않는
드라이버면 클레임을 건너뛰고 워크플로를 그냥 실행한다 — 로그도, 진단도, 응답 헤더도 없다.
유일한 운영 DB 드라이버 lnpl-postgres(lnpl_postgres/driver.py, 2026-10-11 main)의 메서드
목록에는 idempotency_begin / idempotency_finish / idempotency_release가 없다(grep 0건).
즉 Postgres로 배치하면 같은 Idempotency-Key로 재시도한 POST가 두 번 실행되고, 운영자는
sqlite 개발 환경에서 통과한 테스트만 보고 이를 알 수 없다. docs/serving.md "멱등성" 절도
fake 백엔드만 예외로 적고 postgres는 언급하지 않는다.
impl/lnpl/testing.py의 RepositoryDriverTCK(line 72)는 멱등성 메서드를 전혀 검사하지 않으므로
"TCK 통과 = serve의 모든 기능이 동작" 이라는 기대가 성립하지 않는다.
범위
감지: build_app()/serve() 기동 시 repository_factory() 한 번 열어 능력을 확인하고, idempotency_begin이 없으면 (a) stderr 경고 1줄 + /-/readyz 본문에 capabilities로 노출,
또는 (b) --require-idempotency 플래그가 있으면 기동 거부. 요청 시점에는 Idempotency-Key가
왔는데 저장소가 미지원이면 501 idempotency-unsupported(problem+json)로 명시 실패 —
조용히 두 번 실행하는 것보다 낫다.
계약: RepositoryDriver에 supports_idempotency()(또는 lnpl_enforcement의 새 축)로
능력을 선언하게 하고, docs/backends.md §8에 "serve 전 기능에 필요한 선택 메서드" 표를 추가.
TCK: RepositoryDriverTCK에 멱등성 케이스(claim → in-progress 409 → done 재생 → release)를
추가하되, 미구현 드라이버는 skip이 아니라 명시적 declared-unsupported로만 통과.
드라이버 쪽 구현은 lnpl-postgres 이슈(lnpl-postgres#8 in lnpl-postgres)로 분리.
완료 기준
Postgres 드라이버 + Idempotency-Key 재전송 2회 → 두 번째는 재생(200, 같은 body) 또는 명시 501. 두 번 실행 0건.
RepositoryDriver.supports_idempotency() (introspection default: True iff idempotency_begin/finish/release are all callable; SqliteRepositoryDriver True, drivers lacking the methods False) replaces the hasattr(repository, "idempotency_begin") selection at both claim sites in wsgi.py.
On a RepositoryDriver whose predicate is False, a workflow request carrying Idempotency-Key (serving.md M22) and every POST /-/events/<slug> delivery (E8) is refused with 501 idempotency-unsupported (problem+json, correlation_id), the workflow does not run; requests without the header run as before; --backend fake is unchanged.
RepositoryDriverTCK gains declared-unsupported idempotency cases (no skipTest); docs/backends.md has the feature → optional-method → predicate table; docs/serving.md 멱등성/운영 표면 updated.
Deviations from the issue text (decided during the run)
The 501 detail and the boot line name the driver as module.qualname, not the backend scheme — no scheme string reaches wsgi.py on the lnpl serve path (cli.py passes a bare factory); swap to the scheme when cli.py is next touched.
Scope item 4 (driver-side implementation) is lnpl-postgres#8; its linkly-sync latest-main job now runs the new TCK cases through the declared-unsupported branch.
Activity
Merge pull request #256 from choiyounggi/orch/p0-idempotency-1011
choiyounggi commentedon Oct 15, 2026
Shipped in #256 (merged into
main).What landed
RepositoryDriver.supports_idempotency()(introspection default: True iffidempotency_begin/finish/releaseare all callable;SqliteRepositoryDriverTrue, drivers lacking the methods False) replaces thehasattr(repository, "idempotency_begin")selection at both claim sites inwsgi.py.RepositoryDriverwhose predicate is False, a workflow request carryingIdempotency-Key(serving.md M22) and everyPOST /-/events/<slug>delivery (E8) is refused with 501idempotency-unsupported(problem+json,correlation_id), the workflow does not run; requests without the header run as before;--backend fakeis unchanged.lnpl serveprints one boot line (lnpl serve: repository driver <module.qualname> -- idempotency: unsupported …), and theGET /-/readyz200 body carries"capabilities": {"idempotency": <bool>}(information only, never a 503 — the issue/-/healthz·/-/readyz·/-/metrics가 없어 k8s에 올릴 수 없다 — Trace.metric은 배열에 쌓였다 사라진다 (k8s probes / Actuator health groups / Prometheus) #110 closed list of five is unchanged).RepositoryDriverTCKgains declared-unsupported idempotency cases (noskipTest);docs/backends.mdhas the feature → optional-method → predicate table;docs/serving.md멱등성/운영 표면 updated.Deviations from the issue text (decided during the run)
module.qualname, not the backend scheme — no scheme string reacheswsgi.pyon thelnpl servepath (cli.pypasses a bare factory); swap to the scheme whencli.pyis next touched.lnpl_enforcementaxis; non-RepositoryDriverobjects (test doubles only — production can only produceRepositoryDriverinstances viaopen_repository, pinned by a test) keep the pre-Postgres 백엔드에서Idempotency-Key가 경고 없이 무시된다 — 드라이버 opt-in 미지원을 기동 시 감지하고 TCK에 멱등성 계약을 넣는다 #228 behaviour in an isolated legacy branch.linkly-synclatest-main job now runs the new TCK cases through the declared-unsupported branch.