U-대기열 — Code Generation 결과
CP2 가 초록이다. 계획
aidlc-docs/construction/plans/u-queue-code-generation-plan.md 의 단계
열아홉을 순서대로 돌렸고 체크박스가 전부 [x] 다.
이 유닛은 밖에서 보이는 표면을 하나도 안 늘린다 — 새 라우트 0 · 새 열 0 ·
새 패키지 0 · 새 Go 의존 0 이다. 바뀌는 것은 이미 있는 POST /v1/runs 가
409 대신 202 를 낼 수 있다는 것 하나이고, 그 뒤에 승격이 붙는다.
1. 만든 파일
internal/api/queue.go enqueue (202 조립) · wake (깨우기 조각). 74줄
internal/api/queue_test.go 제출 갈래와 깨우기의 API 테스트 아홉
2. 고친 파일
아홉이다. 계획 2절의 행렬 그대로다.
internal/store/store.go LeaseTTL 필드 · insertSteps 추출 · CreateQueuedRun
internal/store/queue.go UnavailableNodes · promote · WakeQueued · ErrNoLeaseTTL
internal/store/list.go Label — internal/api 의 label 을 옮겨 받았다
internal/store/reap.go 실제로 놓았을 때만 끝에서 한 번 깨운다
internal/store/queue_test.go store 테스트 열하나를 이어 붙였다
internal/api/api.go submit 갈래 둘 · busy 합침 · 깨우기 셋 · label 삭제
internal/api/api_test.go 409 기대 넷 · newServerFast 에 LeaseTTL
internal/api/list_test.go 409 기대 둘 · FAILED 필터 하나
cmd/mediator/main.go LeaseTTL 배선 · 기동 시 한 번
internal/store/release.go 를 안 고쳤다. Functional Design 이 잡아낸
정정이 여기서 값을 한다 — applyRelease 가 ReportStep 의 트랜잭션 안이라
그 자리에서는 못 깨우고, 자리는 커밋 뒤인 postResult 다. 그 한 번이 완료와
부분 반납 둘을 덮는다. 계획이 만질 파일을 여섯에서 다섯으로 줄인 근거이고
실제로 그렇게 끝났다.
internal/match/match.go 도 안 고쳤다 (NFR-I4). 승격은 매처를 다시 부르기만
한다 — 매처는 자기가 받은 busy 가 점유인지 회수 정책인지 모른다.
3. 계획에서 갈린 것 셋
3.1 조기 해제 테스트의 단언 모양이 바뀌었다
계획 3.2절은 api_test.go:1870 을 「409 기대를 202 로」 한 줄로 셌다.
한 줄로 안 끝난다 — 그 테스트가 해제를 증명하던 방식이 대기열과 겹친다.
예전 rel-before 제출이 409 로 막힌다 (아직 안 놓았다)
해제 뒤 rel-after 제출이 201 을 받는다 (놓았다)
지금 rel-before 제출이 202 로 서고 QUEUED 다 (아직 안 놓았다. 아무것도 안 쥔다)
해제 뒤 rel-before 가 스스로 RUNNING 이 된다 (놓았다)
rel-after 로는 이제 증명이 안 된다. 놓인 자원을 기다리던 rel-before 가
그 자리에서 승격되므로 새 제출은 다시 202 를 받는다. 단언을 넓힌 것이
아니라 같은 사실을 대기열 뒤의 어휘로 옮긴 것이고, 기다리던 쪽이 실제로
갔다는 점에서 더 강하다.
3.2 테스트를 하나 더 냈다 — 배선 누락 갈래
계획 Step 15 는 API 테스트 여덟을 적었고 아홉을 냈다. 더한 하나는
TestWake_AnUnwiredQueueDoesNotBreakTheReport 다.
근거 — 설계가 「LeaseTTL 이 0 이면 승격을 안 하고 오류를 낸다」를 못 박았는데
(domain-entities.md §3.1), internal/api 쪽에서 그 갈래를 밟는 테스트가
하나도 없었다. 커버리지 프로파일로 확인했다: internal/api/queue.go 의
wake 오류 갈래 세 블록이 전부 count 0 이었다. R16(깨우기 실패가 응답을
안 바꾼다)을 API 표면에서 확인하는 자리가 그것 하나라 비워 둘 수 없다.
3.3 nonce 는 안 옮겼다 — 계획 3.1절 그대로다
Functional Design 요약의 「label 과 nonce 를 옮긴다」 중 뒤는 옮길 것이
없었다. internal/store/acquire.go:243 에 같은 구현이 이미 있고 승격이 그것을
그대로 부른다. internal/api/api.go 의 nonce() 는 제자리이고 crypto/rand ·
encoding/hex 임포트도 안 빠졌다. 계획이 미리 적어 둔 그대로라 실행 중에
놀란 자리가 아니다.
4. CP2 — 돌린 결과
명령을 적은 것이 아니라 돌려서 확인했다.
go build ./... exit 0
go vet ./... exit 0
go test ./... 17개 패키지 전부 ok. 스킵 0건 (-json 으로 셌다)
glyphscan 86 파일 무결
golangci-lint run ./... (v2.13.2) 20건 — 기준선 그대로. 새 파일과
고친 파일에는 0건
커버리지 게이트 16개 패키지 전부 하한 80% 통과, 미달 0개
go test -v ./internal/boundary 2 checked, 2 skipped, 4 total — 안 바뀌었다
경계 검사기의 숫자가 안 바뀐 것이 맞다. 새 패키지가 0 이므로 바뀌었다면
안 만들 것을 만든 것이다. CP1 에서 1 checked 가 2 checked 로 올라간 것과
반대 방향의 확인이다.
4.1 커버리지
internal/api 82.8% -> 81.8% (369/451). 하한 80% 위
internal/store 82.0% (1412/1721)
전체 87.5% -> 87.2% (5604/6427)
internal/api 가 내려갔다. 계획 Step 16 은 「승격의 오류 갈래 넷이 덮이면
여유가 생긴다」고 적었는데 그 넷은 internal/store 쪽이고, internal/api 에는
새 파일 하나(18 문장)가 통째로 들어왔다. 하한은 넘지만 예측과 방향이 반대라
적어 둔다.
파일별로 얇은 자리는 internal/api/queue.go 61.1% (11/18) 다. 안 덮인 것은
enqueue 의 오류 갈래 둘 — 같은 run_id 가 동시에 들어와 유일 제약에 걸리는
경우다. 그 경쟁은 API 테스트에서 결정적으로 못 만든다.
4.2 장면 다섯 — 살아 있는 Mediator 로 돌렸다
스크래치 DB 에 Mediator 를 띄우고 runctl 과 노드 왕복을 실제로 돌렸다.
runctl submit a.json 201 · RUNNING
runctl submit b.json (같은 노드) 202 · QUEUED
runctl status <b> QUEUED (runctl 을 한 줄도 안 고쳤다)
a 가 끝난 뒤 runctl status <b> RUNNING (보고가 그 자리에서 깨웠다)
runctl submit c.json (없는 능력) 422 · FAILED (영구 거절은 그대로다)
runctl 이 그대로 도는 것을 실행으로 확인했다 — client.go:70 이
StatusCode/100 == 2 로 받아 202 가 통과하고, Terminal 이 QUEUED 를
종료로 안 본다.
기동 시 승격 (FR-10) — QUEUED 행이 있는 DB 로 Mediator 를 재기동하니
그 Run 이 RUNNING 이 됐다. 로그에 msg="queued run promoted" 가 한 줄
남았다. 자리를 만들 때 임대를 DB 에서 직접 지워 깨우기 경로를 안 거치게
했다 — 그래야 기동 경로 하나만 보는 것이 된다.
4.3 라우트 18개 회귀 0
살아 있는 Mediator 에 열여덟을 다 쳤다. mux 의 404 page not found 는
하나도 없었다 (핸들러의 404 와 본문으로 갈랐다). 없어야 하는 자리도
확인했다 — GET /v1/nope 만 mux 404 이고 DELETE /v1/runs 는 405 다.
POST /v1/runs 200/201/202 같은 라우트가 셋을 낸다
POST /v1/runs/dry-run 200 draining 이 있어도 답이 안 바뀐다
GET /v1/runs?state=QUEUED 200 대기 행이 실제로 나온다
GET /ui/ 200 토큰 없이 온다. 한 줄도 안 고쳤다
5. 화면 검증 — 브라우저에서 실제로 돌렸다
scene-gates.md 2.1절이 CP2 에 건 것이다. API 검증과 따로 돌렸다 — API 로
통과되는 어휘는 화면 없이도 초록이 된다. Playwright 로 헤드리스 크로미움을
띄웠다.
S1 승격 전 Run 목록에 gerrit-4519-ps1 이 QUEUED 배지로 실제 행을 낸다
부제가 「노드 미배정 · 12:53:44 접수」다
카드 셋(ws-alpha · ws-beta · lab-01) 어디에도 그 Run 이 없다
QUEUED 칩을 누르면 그 행 하나만 남는다
S1 승격 후 같은 Run 이 칩에서 빠지고 ws-alpha 카드의 임대 줄에 붙는다
목록의 배지가 RUNNING 이 되고 「노드 미배정」이 사라진다
끝난 Run 은 「ws-alpha · 12:54:20 완료」로 내려간다
콘솔 오류 0건 · CSP 위반 0건
U-관측API 시점에는 그 칩이 빈 목록이었다. 이 유닛이 한 일은 칩을 만든 것이
아니라 그 칩에 들어갈 행을 실제로 만든 것이다.
화면 코드를 한 줄도 안 고쳤다. internal/api/ui/ 아래 아홉 파일의 수정
시각이 전부 이 작업 이전이다. 고쳤다면 이 유닛의 범위를 넘은 것이다.
6. 뒤 유닛에 내주는 접점
U-drain 광고 처리에 여섯째 깨우기 지점을 단다. s.wake(ctx, why) 가
internal/api/queue.go 에 있고 그 조각을 부르기만 하면 된다
DrainingNodes 가 UnavailableNodes 안에 들어가 있다 — 정책이
붙으면 제출과 승격이 같은 자리에서 함께 빠진다
U-제어판 없다. 이 유닛은 라우트도 화면도 안 만든다
6.1 뒤 유닛이 알아야 하는 것 셋
① 제출 경로에 409 가 하나도 안 남았다. POST /v1/runs 가 내는 코드는
200 · 201 · 202 · 400 · 422 · 503 이다. 매처의 일시 거절과
ErrNodeTaken 이 둘 다 enqueue 로 간다.
② store.WakeQueued 는 Store.LeaseTTL 이 없으면 ErrNoLeaseTTL 을 낸다.
새로 store 를 배선하는 자리(테스트 헬퍼 포함)는 그 필드를 함께 넣어야 한다.
internal/api/api_test.go 의 newServerFast 가 이미 넣고, newServer 는
일부러 안 넣는다 — 승격을 보는 테스트는 store 를 손에 쥐어야 한다.
③ 깨우기는 사건이지 주기가 아니다. Reap 도 postNodes 도 실제로
무언가를 놓았을 때만 깨운다. 새 깨우기 지점을 달 때 같은 규칙을 지켜야
한다 — 무조건 부르면 그 자리가 주기 폴링이 되고 R14 를 깬다.
7. 열린 창 하나 — 사라졌다 돌아온 노드
business-rules.md 5절이 적어 둔 그대로 열려 있다. 대기 중인 Run 의 후보
노드가 광고 만료로 사라졌다가 돌아오는 경우, 광고가 다시 오는 것은 깨우기
지점이 아니므로 그 Run 은 함대 어디선가 임대가 풀리거나 Mediator 가 재기동할
때까지 기다린다. 활동이 있는 함대에서는 곧 닫히고 조용한 함대에서는 열린 채로
있다.
이 유닛에서 안 닫았다. 닫는 자리는 postNodes 인데 광고는 노드마다 주기로
오므로 무조건 깨우면 R14 를 깬다. 「이전에 만료돼 있던 노드가 돌아왔다」만
골라 깨우면 규칙을 안 깨지만, 깨우기 지점은 requirements.md FR-9 가 여섯으로
고정했고 일곱째를 이 유닛이 스스로 더하지 않는다. U-drain 이 같은 핸들러에
여섯째(drain 해제 감지)를 달고 UpsertAdvert 가 이미 이전 값을 돌려주는
구조라, 닫기로 한다면 그 자리가 자연스럽다.
8. 정본에 아직 없다
POST /v1/runs 의 202 는 enode-design 의 protocol/mediator-api.md 에
아직 없다. ADR-064 도 착지 전이다. canon.md 3절이 정본 개정을 이 저장소의
일이 아니라고 못 박았으므로 enode-design/ 을 안 건드렸다. design/ 도
README.md 도 안 고쳤다 — START.md 5절이 진행자의 몫으로 둔 자리다.
9. 안 만든 것
READY 상태 NFR-I2. QUEUED 하나만 실제 값이 됐다
대기열 테이블 runs.state 가 대기열이다
우선순위 · 정책 틀 조건은 도착 순서 하나다
새 라우트 제출도 조회도 이미 있었다
대기 상한 없다. Reap 이 QUEUED 를 안 쓸어간다
깨우기 일곱째 지점 FR-9 가 여섯으로 고정했다. 7절의 창은 열어 두었다
store 의 새 공개 표면 Label · CreateQueuedRun · UnavailableNodes · WakeQueued ·
ErrNoLeaseTTL · LeaseTTL 필드. nonce 를 안 내보냈다
화면의 변경 한 줄도
10. 규칙이 요구하지만 이 유닛에 없는 것
없다고 조용히 빠지면 다음에 읽는 사람이 빠뜨린 것인지 없는 것인지 모른다.
Project Structure Setup 해당 없음. 브라운필드이고 구조가 이미 있다
Frontend Components 해당 없음. 이 유닛에 화면이 없다. S1 의 QUEUED
칩은 U-관측API 가 그렸고 이 유닛은 그 행을 만든다
Database Migration 해당 없음. 새 열 0 · 새 테이블 0 · 새 인덱스 0
API Layer 신규 라우트 해당 없음. 라우트를 하나도 안 만들었다.
202 는 기존 POST /v1/runs 의 새 응답 코드다
Deployment Artifacts 해당 없음. 새 실행파일 없음 · packaging/ 무변경
11. 보안 확장 대조
security-baseline 이 켜져 있다. 이 유닛은 새 표면을 하나도 안 열었다.
SECURITY-04 전송 헤더 N/A. 새 HTML 표면이 없다
SECURITY-05 입력 검증 N/A. 새 입력 파라미터가 없다. 제출 본문의 검증은
기존 c.Validate() 가 그대로 진다
SECURITY-07 세션 N/A
SECURITY-08 접근 제어 N/A. POST /v1/runs 가 기존 auth 미들웨어 뒤에 있고
이 유닛이 그 배치를 안 바꿨다
SECURITY-12 자격증명 준수. 202 본문은 runView 이고 nonce 도 principal 도
안 싣는다. 라우트 회귀에서 본문을 글자로 확인했다
SECURITY-15 오류 노출 준수. 호출자에게 나가는 새 문구가 없다. 승격 실패는
호출자가 없는 자리라 로그로만 나가고, 로그 문구에
내부 경로도 스택도 SQL 도 안 싣는다
blocking 없음.
12. 커밋
안 했다. 진행자만 커밋한다. 테스트 실행이 건드린
cmd/enodectl/probe.lock 은 되돌렸다.