Skip to content

feat(evaluation) 9/15: run start, dispatch and cancellation - #820

Open
Ahmath-Gadji wants to merge 2 commits into
eval/08-runner-portfrom
eval/09-run-lifecycle
Open

feat(evaluation) 9/15: run start, dispatch and cancellation#820
Ahmath-Gadji wants to merge 2 commits into
eval/08-runner-portfrom
eval/09-run-lifecycle

Conversation

@Ahmath-Gadji

@Ahmath-Gadji Ahmath-Gadji commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Part 9 of 15 of the split of #811. Targets eval/08-runner-port (#819).

What

The run half of EvaluationServicestart_run, cancel_run, and the provisioning around them — dispatched through the EvaluationRunner port so the orchestrator never imports Ray.

Setup and teardown of a run's identity live here rather than in the worker, because creating users and partitions is orchestration the API layer already owns. The worker receives a partition it may write to and a token it may use, and nothing else about the system.

Notable

The ordering inside start_run is the whole design. Each step exists because of a specific failure, and each has a regression test:

  1. Ping the runner before claiming anything. Dispatch is fire-and-forget, so an unreachable worker would otherwise strand the run in QUEUED with a partition and a token provisioned for nobody. Surfaces as 503. → test_start_run_refuses_when_the_runner_cannot_be_reached
  2. Insert the run row before provisioning. The partial unique index from part 6 is the mutual exclusion. A read-then-insert would let two racing requests both regenerate the shared eval user's token, the second revoking the credentials the first is still indexing with. The loser gets its 409 before touching anything. → test_a_second_start_is_refused_before_the_token_is_regenerated asserts regenerated == 0.
  3. A failed provision releases the row and drops the partition. A run left in an active status holds the lock and would block every later run. → test_a_failed_provision_releases_the_run_lock

Auth. Runs authenticate as one long-lived non-admin service user, __openrag_eval__, whose token is regenerated at the start of every run — a single service account in the DB, and no usable plaintext token at rest. It gets file_quota=-1 because a corpus is re-uploaded on every run and a quota would fail the second one for reasons unrelated to the eval.

Cancellation asks the worker first. A worker that owns the run writes its own terminal status, including the metrics — so the service must not overwrite it. False means nobody owns the run (an actor restart orphaned it); the row is reaped here instead, because nothing else would ever move it out of an active status. An unreachable runner is logged, not raised — an orphan still has to be reapable.

Testing

10 unit tests, all against in-memory fakes — no Ray, no actor, no worker process, which is exactly what the port bought. Covers the three orderings above, both cancellation paths, the already-finished 409, and that api_base_url comes from Settings rather than the environment. ruff, format check and the layer-import guard pass.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@EnjoyBacon7, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 59 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: a59d4aff-94c0-4d92-9516-37c53e55432d

📥 Commits

Reviewing files that changed from the base of the PR and between 5acd338 and cc3263c.

📒 Files selected for processing (2)
  • openrag/services/orchestrators/evaluation_service.py
  • tests/unit/services/orchestrators/test_evaluation_service.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eval/09-run-lifecycle

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Ahmath-Gadji

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 57 minutes.

@Ahmath-Gadji
Ahmath-Gadji force-pushed the eval/08-runner-port branch from 03026f3 to 224a71c Compare July 27, 2026 14:32
@Ahmath-Gadji
Ahmath-Gadji force-pushed the eval/09-run-lifecycle branch from f15c899 to 5400f80 Compare July 27, 2026 14:32
@Ahmath-Gadji
Ahmath-Gadji force-pushed the eval/08-runner-port branch from 224a71c to b0f87a9 Compare July 27, 2026 15:14
@Ahmath-Gadji
Ahmath-Gadji force-pushed the eval/09-run-lifecycle branch from 5400f80 to ea0f369 Compare July 27, 2026 15:14
@Ahmath-Gadji
Ahmath-Gadji force-pushed the eval/08-runner-port branch from b0f87a9 to 58ad1d0 Compare July 27, 2026 15:31
@Ahmath-Gadji
Ahmath-Gadji force-pushed the eval/09-run-lifecycle branch from ea0f369 to 43925aa Compare July 27, 2026 15:31
The run half of `EvaluationService`, dispatched through the
`EvaluationRunner` port so the orchestrator stays Ray-free.

Setup and teardown of a run's identity live here rather than in the worker,
because creating users and partitions is orchestration the API layer
already owns. The worker receives a partition it may write to and a token
it may use, and nothing else about the system.

Ordering in `start_run` is deliberate and each step is regression-tested:

1. Ping the runner first. Dispatch is fire-and-forget, so an unreachable
   worker would otherwise strand the run in QUEUED with a partition and a
   token provisioned for nobody. It surfaces as 503.
2. Insert the run row before provisioning anything. The partial unique
   index is the mutual exclusion; a read-then-insert would let two racing
   requests both regenerate the shared eval user's token, the second
   revoking the credentials the first is still indexing with. The loser
   gets a 409 before touching anything.
3. On a failed provision, release the row and drop the partition. A run
   left in an active status holds the lock and would block every later run.

Runs authenticate as one long-lived non-admin service user
(`__openrag_eval__`) whose token is regenerated at the start of every run,
so no usable plaintext token is ever stored at rest.

Cancellation asks the worker first. A worker that owns the run writes its
own terminal status, including metrics. `False` means nobody owns it — the
run was orphaned by an actor restart — so the row is reaped here instead,
otherwise it would block every subsequent run forever.
`__eval_*` is now rejected on the public creation path (part 1/15), so a
run has to opt in to the namespace it owns. Without this the first run
fails at `create_partition` with RESERVED_PARTITION_NAME.

The unit tests did not catch it: `FakePartitionService` stands in for the
whole service, so it accepted a name the real one refuses. The fake now
asserts the flag, which is the only thing that keeps the two in step.
@EnjoyBacon7
EnjoyBacon7 force-pushed the eval/08-runner-port branch from 58ad1d0 to 5acd338 Compare July 28, 2026 10:25
@EnjoyBacon7
EnjoyBacon7 force-pushed the eval/09-run-lifecycle branch from 43925aa to cc3263c Compare July 28, 2026 10:25
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.

2 participants