Skip to content

feat(runner): add pull-dispatch coordination layer - #55

Merged
as535364 merged 3 commits into
mainfrom
feat/runner-coordination-layer
Jul 23, 2026
Merged

feat(runner): add pull-dispatch coordination layer#55
as535364 merged 3 commits into
mainfrom
feat/runner-coordination-layer

Conversation

@as535364

Copy link
Copy Markdown
Member

Normal-OJ/Normal-OJ#68(sandbox slice 2/4)

新協調層 runner/,pull-based dispatch 的 runner 側基礎。Dark PR——main.py 尚未接線,無任何呼叫者;不修改既有檔案。

內容

  • runner/client.pyBackendClientregister / heartbeat,契約見 spec §7.1–7.2)、RunnerIdentity(memory-only,token repr=False,ADR-0004)、RunnerConfig(parse register 回應的 config,缺 key 落 15/3/8 預設)
  • runner/registration.pyregister_with_backoff:backoff 1→2→4→8→16→30s(之後維持 30、無限重試);401 不重試直接 raise
  • runner/heartbeat.pyHeartbeatThread:interval 取自 register config(預設 15s)、body 夾帶 ActiveJobTracker 快照、連續 2 次 401 → on_fatal(非 401 結果歸零計數)、Event-based 可即時 stop()
  • runner/active_jobs.pyActiveJobTracker:thread-safe add/remove(冪等)/snapshot/len
  • runner/config.pyBACKEND_URLRUNNER_REGISTRATION_TOKENRUNNER_NAME(未設時落 hostname)等;不動舊 BACKEND_API

測試

31 個單元測試,無 docker/網路依賴(注入 fake session):backoff 序列逐值驗證、401 fail-fast/歸零矩陣、request shape(URL/Bearer header/body)、config 預設值、tracker 冪等與 thread-safety、token 不出現在 repr。

備註

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new runner/ coordination layer for the pull-based dispatch runner side, adding backend registration + heartbeat plumbing and a thread-safe active-job snapshot mechanism, with unit tests validating backoff, auth fail-fast, request shapes, and config defaults.

Changes:

  • Added BackendClient, RunnerIdentity, and RunnerConfig for runner↔backend registration/heartbeat HTTP calls.
  • Added register_with_backoff (infinite retry with capped backoff; 401 fail-fast) and HeartbeatThread (interruptible stop; consecutive-401 fail-fast).
  • Added ActiveJobTracker plus a comprehensive unit test suite for the new runner coordination components.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
runner/init.py Adds package docstring for the new coordination layer (currently inaccurate given existing legacy runner code in the same package).
runner/active_jobs.py Implements thread-safe active job tracking with snapshot and idempotent remove.
runner/client.py Adds backend HTTP client + dataclasses for identity/config parsing and API error modeling.
runner/config.py Introduces new env-driven config for the pull-based runner API (comment currently misleading vs existing legacy BACKEND_API).
runner/heartbeat.py Implements a daemon heartbeat thread with consecutive-401 fail-fast behavior.
runner/registration.py Adds registration retry loop with capped exponential backoff and injectable sleep for tests.
tests/test_active_jobs.py Unit tests for ActiveJobTracker behavior and thread-safety smoke test.
tests/test_runner_client.py Unit tests for client request shapes, URL normalization, error mapping, and config defaults parsing.
tests/test_runner_heartbeat.py Unit tests for heartbeat behavior, auth failure counting/reset, stop responsiveness, and interval selection.
tests/test_runner_registration.py Unit tests for backoff schedule, retry behavior, and 401 fail-fast behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread runner/config.py Outdated
Comment thread runner/__init__.py
@as535364

Copy link
Copy Markdown
Member Author

codex: 我看完了,有一個地方建議 merge 前先修:

HeartbeatThread.run() 會等 _beat() 完成後才開始 wait(interval_sec),因此 heartbeat 的實際間隔是「request 耗時 + 15 秒」,不是 spec 寫的每 15 秒一次。現在 request timeout 是 10 秒,lease TTL 是 30 秒;遇到慢回應或一次 timeout 時,續租的緩衝可能只剩約 5 秒,job 還在跑但 lease 已過期的風險會變高。

建議改成用固定的 monotonic deadline 排程,每次等待時扣掉 request 已花掉的時間。

另有一個不擋 merge 的小地方:test_runner_heartbeat.pytest_runner_registration.py 的 scripted-outcome test double 幾乎相同,可以考慮抽成共用 helper。不抽也不影響這次功能。

我在本機跑過這次新增的 31 個單元測試,全部通過。

BackendClient (register/heartbeat), register_with_backoff (1-2-4-8-16-30s,
401 no-retry), HeartbeatThread (interval from register config, carries
active_job_ids, 2x consecutive 401 fail-fast via on_fatal) and
ActiveJobTracker. Dark slice - nothing wires into main.py yet; unit tests
run without docker.

Normal-OJ/Normal-OJ#68 (sandbox slice 2/4); spec section 10/13, ADR-0004
@as535364
as535364 force-pushed the feat/runner-coordination-layer branch from 2c6f7db to c0a205b Compare July 22, 2026 13:57
as535364 added 2 commits July 22, 2026 22:02
Subtract the time the beat itself took from the next wait, so a slow
or timed-out request (up to 10s) cannot push the next beat past the
30s lease TTL. Without this, one timeout almost always dropped the
lease and forced a needless reclaim. Addresses review feedback.
BACKEND_API is not replaced yet; it still serves the old push path
until the keystone slice. Addresses review feedback.
@as535364

Copy link
Copy Markdown
Member Author

Claude: Heartbeat cadence fixed in 491eabc — the wait now subtracts the time the beat took (time.monotonic, clamped at 0), so a slow or timed-out request no longer pushes the next beat past the 30s lease TTL. Added two unit tests: a 10s beat shrinks the following wait to 5s, and a beat longer than the interval waits 0. On the test-double dedup: leaving it as is for now; will extract a shared helper if slice 3 (#69) needs a third copy.

@as535364
as535364 requested a review from Bogay July 22, 2026 14:28
Comment thread runner/client.py

@Bogay Bogay left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@as535364
as535364 merged commit de7b9c1 into main Jul 23, 2026
4 checks passed
@as535364
as535364 deleted the feat/runner-coordination-layer branch July 23, 2026 06:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sandbox: runner/ coordination layer — client/registration/heartbeat (slice 2/4)

3 participants