iop/agent-task/m-edge-model-group-queue-scheduling/03+01_node_queue_simplify/PLAN-cloud-G07.md
toki dba289d0fa feat(edge): 모델 그룹 큐 스케줄링 구현
- 모델 그룹별 큐 기반 디스패팅 로직 추가
- ModelQueue 관리자로 모델 인스턴스 큐 처리
- OpenAPI chat 및 responses 핸들러 업데이트
- edge 서비스 테스트 코드 개선
2026-06-16 10:16:05 +09:00

10 KiB

Plan - REFACTOR Node Queue Simplify

이 파일을 읽는 구현 에이전트에게

CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증 명령을 실행하고 실제 출력, 구현 메모, 계획 대비 변경 사항을 채운 뒤 active 파일을 남긴 채 리뷰 준비 상태로 보고한다. 최종 판정, log rename, complete.log, archive 이동은 code-review-skill 전용이다. 구현 중 사용자만 결정할 수 있는 범위 변경, 외부 환경/secret, 또는 scope conflict가 생기면 chat에서 묻지 말고 review stub의 사용자 리뷰 요청 섹션에 근거와 재개 조건을 남긴다.

배경

현재 Node는 provider-scoped FIFO admission queue와 node-wide global gate를 소유한다. 현재 마일스톤의 목표는 queue owner를 Edge로 옮기는 것이므로, Node는 dispatch된 실행을 수행하고 상태를 보고하는 역할로 단순화되어야 한다. Edge queue core가 먼저 들어간 뒤 Node-local FIFO는 제거하거나 over-dispatch 안전장치 수준으로 축소한다.

사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 사용자 리뷰 요청 섹션에 기록한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하지 않는다. code-review가 해당 요청을 검증하고 필요할 때만 USER_REVIEW.md를 작성한다.

Roadmap Targets

  • Milestone: agent-roadmap/phase/inference-provider-extension/milestones/edge-model-group-queue-scheduling.md
  • Task ids:
    • node-simplify: Node-local provider FIFO queue가 제거되거나 Edge dispatch 이후의 실행 안전장치로 축소되어, 각 Node가 queue owner가 되지 않는다.
  • Completion mode: check-on-pass

분석 결과

읽은 파일

  • agent-test/local/rules.md
  • agent-test/local/node-smoke.md
  • agent-test/local/edge-smoke.md
  • agent-ops/rules/project/domain/node/rules.md
  • agent-ops/rules/project/domain/testing/rules.md
  • apps/node/internal/node/node.go
  • apps/node/internal/node/run_manager.go
  • apps/node/internal/node/node_test.go
  • apps/node/internal/node/node_concurrency_integration_test.go
  • apps/node/internal/runtime/types.go
  • apps/node/internal/router/router.go

테스트 환경 규칙

  • test_env: local.
  • 적용 profile: node-smoke, edge-smoke.
  • 필수 명령: go test -count=1 ./apps/node/internal/node.
  • Node 실행 요청/stream/cancel/status 경로에 닿으므로 최종 regression subtask에서 repo 내부 edge-node 진단과 full-cycle 기준을 이어서 수행한다.

테스트 커버리지 공백

  • 현재 Node tests는 Node-owned FIFO queue를 성공 조건으로 삼는 테스트가 많다.
  • Edge-owned queue 전환 뒤에는 Node가 queued status를 만들지 않아야 하므로 기존 TestAdmissionQueue_* 계열은 삭제, 이동, 또는 "over-dispatch immediate rejection/safety" 기준으로 전환해야 한다.
  • Node capabilities의 queued/in_flight 값은 Edge-owned snapshot과 충돌하지 않아야 한다. queued는 0 또는 safety-only 값으로 정리해야 한다.

심볼 참조

  • 제거/축소 후보: fifoGate, admissionManager, admissionTicket, ErrConcurrencyLimitExceeded 주변 queue_full/queue_timeout helper.
  • call site:
    • apps/node/internal/node/node.go:35 globalGate
    • apps/node/internal/node/node.go:36-37 adapterGates
    • apps/node/internal/node/node.go:98-213 admission enqueue/wait/launch flow
    • apps/node/internal/node/node.go:216-242 admission helper
    • apps/node/internal/node/node.go:332-344 capabilities in_flight/queued snapshot
    • apps/node/internal/node/node_test.go:1438+ admission queue tests
    • apps/node/internal/node/node_concurrency_integration_test.go:91+ queue overflow integration test

분할 판단

  • shared task group: m-edge-model-group-queue-scheduling.
  • 현재 subtask: 03+01_node_queue_simplify.
  • predecessor 01: active agent-task/m-edge-model-group-queue-scheduling/01_edge_queue_service/complete.log 없음. 구현 시작 전 01_edge_queue_service PASS complete.log가 필요하다.
  • snapshot/surface 조정은 02+01_surface_snapshot_contract, final regression은 04+01,02,03_regression_evidence로 분리한다.

범위 결정 근거

  • Node adapter provider probe, runtime adapter contracts, router lookup semantics는 바꾸지 않는다.
  • Edge scheduler 구현을 이 subtask에서 재설계하지 않는다.
  • 외부 OpenAI/A2A/console surface는 Node simplification 검증 범위가 아니므로 제외한다.

빌드 등급

  • cloud-G07: 기존 Node admission queue는 cancellation, background run, timeout, store status, provider snapshot에 걸쳐 있어 대량 테스트 전환과 concurrency risk가 크다.

구현 체크리스트

  • Node OnRunRequest에서 FIFO queue ownership을 제거하고 dispatch된 run을 즉시 실행 또는 safety reject로 처리한다.
  • Node capability snapshot의 queued/in-flight 의미를 Edge-owned queue와 충돌하지 않게 정리한다.
  • Node admission queue tests를 삭제/전환하고 over-dispatch safety, cancel, background, store lifecycle 테스트를 남긴다.
  • go test -count=1 ./apps/node/internal/node가 통과한다.
  • CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

[REFACTOR-1] OnRunRequest Queue Ownership 제거

문제

Node가 provider FIFO queue owner다.

// apps/node/internal/node/node.go:98
98  // Admission gate (foreground and background use the same path).
110 admission := n.admissionFor(spec.Adapter, caps)
128 ticket, err := admission.enqueue()
167 if err := ticket.wait(execCtx); err != nil {
177 defer ticket.release()

해결 방법

OnRunRequest는 router resolve, store insert, runManager register, adapter execute lifecycle만 맡긴다. capacity guard가 필요하면 queue 없는 non-blocking safety gate로 두고, full이면 즉시 error event와 rejected store status를 남긴다. queued store status는 Edge queue가 소유하므로 Node-local 대기 상태로 쓰지 않는다.

수정 파일 및 체크리스트

  • apps/node/internal/node/node.go: admission enqueue/wait 흐름 제거 또는 safety guard로 축소.
  • apps/node/internal/node/run_manager.go: FIFO queue helper 제거 또는 safety-only helper로 축소.

테스트 작성

  • 작성: TestOnRunRequest_DispatchedRunRunsImmediately, TestOnRunRequest_OverDispatchSafetyRejectsWithoutQueue 또는 기존 테스트 전환.

중간 검증

go test -count=1 ./apps/node/internal/node -run 'TestOnRunRequest'

[REFACTOR-2] Capability Snapshot 정리

문제

Node command capabilities가 Node-local gate의 in-flight/queued를 보고한다.

// apps/node/internal/node/node.go:332
332 inFlight := 0
333 queued := 0
334 if ok {
335     inFlight = gate.activeCount()
336     queued = gate.queuedCount()
337 }

Edge-owned snapshot과 섞이면 queued 의미가 충돌한다.

해결 방법

Node는 provider availability/capacity와 safety in-flight만 보고하고 queued는 0으로 고정하거나 safety queue가 없음을 명시한다. Edge-owned queued는 02의 service snapshot이 표현한다.

수정 파일 및 체크리스트

  • apps/node/internal/node/node.go: capabilities result/snapshot queued semantics 정리.
  • apps/node/internal/node/node_test.go: capabilities queued 테스트 전환.

테스트 작성

  • 작성/전환: TestOnCommandRequest_Capabilities, TestOnCommandRequest_Capabilities_InFlight, 기존 TestOnCommandRequest_Capabilities_Queued 삭제 또는 safety-only 이름으로 변경.

중간 검증

go test -count=1 ./apps/node/internal/node -run 'TestOnCommandRequest_Capabilities'

[REFACTOR-3] Admission Queue Test 전환

문제

apps/node/internal/node/node_test.go:1438+ 이후는 Node-owned FIFO를 전제로 한다. Edge queue 전환 뒤 이 테스트들이 그대로 남으면 목표와 반대 동작을 고정한다.

해결 방법

FIFO ordering, max_queue, queue_timeout, release promotion 테스트는 제거하거나 Edge service queue tests로 이동된 것을 확인한다. Node에는 cancel/background/store/error lifecycle과 over-dispatch immediate safety만 남긴다.

수정 파일 및 체크리스트

  • apps/node/internal/node/node_test.go: TestAdmissionQueue_* 계열 정리.
  • apps/node/internal/node/node_concurrency_integration_test.go: queue overflow integration test를 safety rejection 또는 제거로 전환.

테스트 작성

  • 작성: over-dispatch safety event가 Edge로 도착하는 통합 테스트가 필요하면 기존 TestQueueOverflow_RejectEventObservedByEdge를 재명명해 즉시 rejection 기준으로 유지한다.

중간 검증

go test -count=1 ./apps/node/internal/node -run 'Test(AdmissionQueue|ConcurrencyLimit|QueueOverflow|OnRunRequest|OnCommandRequest_Capabilities)'

의존 관계 및 구현 순서

  • 01_edge_queue_service complete.log가 먼저 필요하다.
  • 구현 전 확인 후보:
    • active: agent-task/m-edge-model-group-queue-scheduling/01_edge_queue_service/complete.log
    • archive: agent-task/archive/*/*/m-edge-model-group-queue-scheduling/01_edge_queue_service/complete.log

수정 파일 요약

파일 항목
apps/node/internal/node/node.go REFACTOR-1, REFACTOR-2
apps/node/internal/node/run_manager.go REFACTOR-1
apps/node/internal/node/node_test.go REFACTOR-1, REFACTOR-2, REFACTOR-3
apps/node/internal/node/node_concurrency_integration_test.go REFACTOR-3

최종 검증

go test -count=1 ./apps/node/internal/node

기대 결과: 통과. Go test cache는 허용하지 않는다.

모든 코드 변경 완료 후 반드시 CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.