Operator limits: enforce limits and suspension at claim
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Newbie friendliness
- 20/100
- Issue type
- Feature
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- postgresql, sqlite, typescript
Research direction
Start in src/runner-api/claim.ts, after the guard and before credential creation, then read src/runs/error-copy.ts and the two READMEs named in the issue. The issue depends on #271 and #272, so those entitlement and usage-record pieces must already exist before this can be built. Done means the test table T01 to T13 passes on both SQLite and Postgres, including the race test T06 and the no-record-on-refusal check T07.
Written by the indexing model from the issue text.
Description
Part of #268. Depends on #271 and #272.
Problem
With entitlements (#271) and usage records (#272) in place nothing yet acts on them. A workspace with limits must be refused when it has used them up, a suspended workspace must not run reviews, and the check must be exact under concurrency (two runners claiming at once must not both pass the last unit) and must never leave a trace of consumption for a refused run.
Behavior
- The claim path (
src/runner-api/claim.ts, after the guard and before any credential is created) resolves the workspace's effective entitlements and decides, in this order:suspended: refuse with the stable codeworkspace_suspended.- A count limit for the job's kind (
diffRunsPerPeriod/dynamicRunsPerPeriod): the number of records of that kind in the workspace with statereservedorsettledsinceperiod_start; at or above the limit refuses withquota_exhausted(detaildiff_runsordynamic_runs). A limit of 0 means "none allowed".nullmeans unlimited. - The cost limit (
managedCostLimitMicroUsd) applies to jobs that run on the operator's key: the sum of settled managedcost_micro_usdsinceperiod_start; at or above the limit refuses withquota_exhausted(detailmanaged_cost). Records with unknown cost add nothing to the sum (they still count in 2). - Otherwise the check and the reservation of #272 happen in one transaction that holds the workspace lock (
BEGIN IMMEDIATEon SQLite; a row lock on the entitlements row, or on the workspace row when there is none, on Postgres), so concurrent claims never exceed a count limit.
- A refused job ends
failedwith that code, is not handed to a runner, creates no credential, and no usage record: nothing is consumed, nothing is owed. A requeued job after a lost lease keeps its existing record and is not counted again. - The cost limit is a soft cap: a run that starts below it may end above it by what the runs in flight at that moment cost. The overshoot is bounded by the number of concurrent runs times the per-run bounds, and is recorded as usage like any other. This is documented, not hidden.
- A key mode
managedwith the operator key missing is not a quota matter (#276) and keeps its own code. period_startmoving forward (a reset by the operator) takes effect on the next claim: records before it no longer count.- The Runs list and detail explain both codes in plain words (
src/runs/error-copy.tsrequires a sentence per code) without numbers or figures; no pull request message here (#281). - The check costs one indexed query per claim; unlimited workspaces skip it.
Scope
Claim path, a small enforce-entitlements.ts module, error codes and copy, indexes, docs (src/runner-api/README.md, src/jobs/README.md), tests.
Out of scope
Pull request notice (#281), usage page (#282), hard cost ceilings per run, anything about how limits are decided.
Test cases
| ID | kind | level | case | expected |
|---|---|---|---|---|
| T01 | regression | integration | an instance with no entitlements | claim path identical to before; the existing suites pass unchanged |
| T02 | behavior | integration | diffRunsPerPeriod = 2; third Diff job claimed |
first two run, third fails quota_exhausted / diff_runs, no runner gets it |
| T03 | behavior | integration | dynamicRunsPerPeriod = 0 |
every Dynamic job fails quota_exhausted / dynamic_runs; Diff jobs unaffected |
| T04 | behavior | integration | managed cost limit reached by settled records | next managed job fails quota_exhausted / managed_cost; a job on the workspace's own key is not blocked by it |
| T05 | behavior | integration | suspended = true |
every job fails workspace_suspended; clearing it lets the next claim pass |
| T06 | security | integration | two claims race for the last unit (SQLite and Postgres) | exactly one runs |
| T07 | behavior | integration | refused job | no usage_records row, no key handed out, no model call |
| T08 | behavior | integration | lost lease, job claimed again at the limit | it runs; one record |
| T09 | behavior | integration | period_start moved forward |
earlier records stop counting immediately |
| T10 | behavior | integration | a void record |
does not count |
| T11 | behavior | integration | unknown-cost settled record | counts in the run count, adds nothing to the cost sum |
| T12 | behavior | unit | error copy | one sentence per new code, no number or figure |
| T13 | security | integration | workspace A at its limit | workspace B unaffected |
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 1
- Avg merge
- 1h 16m
- Merged PRs (30d)
- 226
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from ExpidetGames/code-reviewer
-
area: app type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
ExpidetGames/code-reviewer#308 ·
Maintainers usually reply within 1 day
-
type: docs
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ExpidetGames/code-reviewer#94 ·
Maintainers usually reply within 1 day
-
area: ops type: feature
Difficulty 5/5 Over a week Newbie friendliness 30/100
ExpidetGames/code-reviewer#414 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
ExpidetGames/code-reviewer#406 ·
Maintainers usually reply within 1 day
-
Difficulty 4/5 3-5 days Newbie friendliness 25/100
ExpidetGames/code-reviewer#401 ·
Maintainers usually reply within 1 day
All issues in ExpidetGames/code-reviewer
Similar issues
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
Difficulty 1/5 Under an hour Newbie friendliness 90/100
lingdojo/kana-dojo#32663 · 1 comment · 5 reactions ·
Maintainers usually reply within 1 day
-
Admin: Title Separator field can't be cleared (input falls back to "|")Possibly taken @emdashbot claimed this today. Openbot:bug bot:in-review
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
emdash-cms/emdash#4236 · 3 comments ·
Maintainers usually reply within 1 day
-
PHP workflow streaming requests buffer the full SSE responsePossibly taken @Winoren claimed this today. Openreview: medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
langgenius/dify#44270 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
area:holodeck review-finding
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
hyperdrive-technology/holodeck#142 ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
code-yeongyu/senpi#3573 ·
Maintainers usually reply within 1 day