PH0.3 — Worker skeleton: claim loop, task handler registry, jobs discovery, heartbeat #53
No reviewers
Labels
No labels
ready-for-agent
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
patrick/Polaris!53
Loading…
Reference in a new issue
No description provided.
Delete branch "feature/worker-skeleton"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
worker/claim.py:claim_next_task()—SELECT ... FOR UPDATE SKIP LOCKEDso several worker processes can poll thetasktable concurrently without double-claiming a row.worker/retry.py+worker/loop.py: exponential, capped backoff (MAX_ATTEMPTS = 5);process_one()claims + dispatches + records the outcome (done / retried / terminally failed) on the task row;run_claim_loop()wraps it in an injectable poll loop.worker/task_handlers.py+worker/handlers/:@task_handler("<kind>")decorator +pkgutil.walk_packagesdiscovery, same conflict-free seampolaris.apiuses for routers.handlers/noop.pyis the concrete proof, mirroringapi/routers/platform/health.py.worker/job_discovery.py+worker/jobs/: modules exporting aschedule(APScheduler trigger) +run(), discovered the same way.jobs/heartbeat.pyis the required real deliverable (AC1).worker/scheduler.py+worker/main.py+worker/__main__.py:build_scheduler()wires discovered jobs into aBackgroundScheduler;main()starts it alongsiderun_claim_loop(); runnable viapython -m polaris.worker.domain/tasks.py(newdomainpackage, this file only):enqueue()— the single helper other domain services use to push work onto thetasktable, via aTaskRepositoryprotocol it defines itself (polaris.domainmust not importsqlalchemy.orm.Sessiondirectly, import rules table indocs/12-implementation-plan.md).db/repositories/task.py:SqlAlchemyTaskRepository, the concrete implementation — an explicitly-scoped extension topolaris.dbcalled for by the ticket brief (PH0.2 didn't need a repository layer yet).Acceptance criteria (issue #6)
infra/compose/docker-compose.yml'sworkerservice runscommand: ["polaris", "worker"], but there is nocli/worker.pysubcommand (confirmed:docker run <image> worker→invalid choice: 'worker').backend/src/polaris/cli/**is sealed to this branch per my ticket brief. Filed as seam request #52 (cli/worker.py, ~15 lines mirroringcli/api.py, wrapspolaris.worker.main:main()). Everything else needed for this criterion is done and tested: handler/job discovery, the scheduler, and the heartbeat job itself all work (verified with a standalone smoke test and inside the built Docker image).Test plan
uv run ruff format --check ./uv run ruff check .— cleanuv run mypy --strict src— cleanuv run pytest— 60/60 passing (18 pure unit + 12 integration for worker/domain, rest pre-existing), including a genuine two-connectionSKIP LOCKEDconcurrency test (test_two_concurrent_claimants_get_the_task_at_most_once) against a real Postgres testcontainerdocker build backend/— succeeds;python -m polaris.workerimports cleanly inside the built imageNotes / seam requests
cli/worker.pyneeded fordocker compose up workerto actually start the process (see above).CLAUDE.md(one clock per account, shared across parallel sessions on this token) relayed mid-task.patrick, matching the convention used on the already-merged #4.developtip (pre-merge intomain); fast-forwarded ontoorigin/mainoncedevelopwas retired mid-task, so this PR targetsmainas instructed.Closes #6
🤖 Generated with Claude Code
https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
polaris workerCLI subcommand (cli/worker.py) #52