Skip to content

Feat/benchmark job node affinity - #52

Open
amh1k wants to merge 8 commits into
mainfrom
feat/benchmark-job-node-affinity
Open

amh1k wants to merge 8 commits into
mainfrom
feat/benchmark-job-node-affinity

Conversation

@amh1k

@amh1k amh1k commented Oct 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Allow users to configure node affinity for benchmark runner Jobs, including required label rules and weighted preferences.

Runs without affinity use normal Kubernetes scheduling. Runs that remain unschedulable for 60 seconds fail with the scheduler’s explanation, allowing users to correct placement rules and submit again.

Closes #40.

Changes

  • Add an optional Job placement editor with required AND/OR groups and weighted preferred groups.
  • Validate label keys, operators, values, and preference weights.
  • Apply per-run affinity to the runner Job and retain an independent snapshot in run history.
  • Display configured affinity alongside run status and results.
  • Detect persistent scheduling failures, clean up Jobs and credential Secrets, and release run capacity.
  • Document the scheduling grace period and its interaction with the overall execution timeout.

Validation

  • All frontend tests and the frontend build pass.
  • Backend tests, API/coordinator race tests, and go vet pass.
  • Tests cover validation, snapshot isolation, Job configuration, scheduling recovery, timeout handling, and cleanup.

Screenshot not attatched since we have to resolve conflicts when #51 merges into main since it also modifies the UI

amh1k added 4 commits October 9, 2026 23:41
Validate required and preferred scheduling rules, apply them to runner Jobs, and retain affinity snapshots in run status responses. Add API, store, and coordinator tests.

Signed-off-by: amh1k <abdulmoizx97@gmail.com>
Add required and preferred rule editing, validate and serialize affinity, and display saved rules in run details. Cover validation, submission, status parsing, and UI behavior with frontend tests.

Signed-off-by: amh1k <abdulmoizx97@gmail.com>
Detect continuous scheduler rejection after a 60-second grace period, expose scheduling diagnostics, and clean up failed runs. Add regression tests and document the timeout policy.

Signed-off-by: amh1k <abdulmoizx97@gmail.com>
@amh1k
amh1k requested a review from spron-in October 12, 2026 05:10
@spron-in spron-in mentioned this pull request Oct 13, 2026

@spron-in spron-in left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Main concern: the 60s scheduling failure applies to every run (see jobs.go). Also some test-tooling and typing nits; suggestions inline.

Comment thread backend/internal/coordinator/jobs.go
Comment thread backend/internal/coordinator/scheduling.go Outdated
Comment thread package.json Outdated
"dev": "vite",
"build": "tsc --noEmit && vite build",
"test:form": "tsc src/benchmarkFormValidation.ts --target ES2020 --module ESNext --moduleResolution Bundler --outDir dist/form-test && node tests/benchmarkForm.test.mjs",
"test:form": "tsc src/BenchmarkForm.tsx --jsx react-jsx --target ES2020 --module ESNext --moduleResolution Bundler --esModuleInterop --skipLibCheck --outDir dist/form-test && node tests/benchmarkForm.test.mjs && node tests/benchmarkNodeAffinity.test.mjs && node tests/benchmarkNodeAffinityForm.test.mjs",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

To render components under node:test, this now compiles the whole BenchmarkForm.tsx graph with tsc (--jsx, --esModuleInterop, --skipLibCheck). That's why value imports in src/ now need .js extensions while type imports don't. The mixed style will spread to every component a test touches.

Vite is already the build tool, so Vitest + @testing-library/react (jsdom environment) would run the .tsx directly, with no compile step and no import-extension changes. Proposed split: keep the pure-logic tests on the existing runner and move the component test to Vitest:

"test:form": "tsc src/benchmarkFormValidation.ts --target ES2020 --module ESNext --moduleResolution Bundler --outDir dist/form-test && node tests/benchmarkForm.test.mjs && node tests/benchmarkNodeAffinity.test.mjs",
"test:components": "vitest run",

Then revert the .js suffixes in src/, port benchmarkNodeAffinityForm.test.mjs to a *.test.tsx, and add npm run test:components to ci.yml. (Not a one-click suggestion on purpose: applying the script change alone would silently drop the component test.)

Comment thread package.json Outdated
Comment on lines +28 to +31
"jsdom": "24.1.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-test-renderer": "18.3.1",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit: jsdom and react-test-renderer are pinned exactly while every other devDependency uses ^, and together they add ~800 lines to package-lock.json. react-test-renderer is also deprecated upstream; React recommends @testing-library/react. Ideally both get replaced by vitest + @testing-library/react (see the test:form comment). If the current runner stays, at least align the ranges:

Suggested change
"jsdom": "24.1.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-test-renderer": "18.3.1",
"jsdom": "^24.1.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-test-renderer": "^18.3.1",

Comment thread src/BenchmarkForm.tsx Outdated
Comment thread src/benchmarkFormValidation.ts Outdated
Comment thread backend/internal/coordinator/runs_test.go Outdated
@spron-in

Copy link
Copy Markdown
Contributor

Follow-up suggestion: tolerations. Dedicated benchmark node pools are usually tainted (e.g. dedicated=benchmark:NoSchedule) to keep other workloads off. Node affinity alone can't place the runner there. With a required rule, the run would sit Unschedulable and fail.

Not a blocker for this PR, but could we track it in a follow-up issue? Rough scope:

  • per-run tolerations (key/operator/value/effect/tolerationSeconds), validated like affinity
  • possibly an operator-set allowlist of toleration keys, since a toleration lets a user schedule onto nodes the cluster admin reserved
  • a section in the Job placement editor plus the run summary
  • README note that affinity + tolerations together are needed for tainted pools

Signed-off-by: amh1k <abdulmoizx97@gmail.com>
@amh1k

amh1k commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

Follow-up suggestion: tolerations. Dedicated benchmark node pools are usually tainted (e.g. dedicated=benchmark:NoSchedule) to keep other workloads off. Node affinity alone can't place the runner there. With a required rule, the run would sit Unschedulable and fail.

Not a blocker for this PR, but could we track it in a follow-up issue? Rough scope:

  • per-run tolerations (key/operator/value/effect/tolerationSeconds), validated like affinity
  • possibly an operator-set allowlist of toleration keys, since a toleration lets a user schedule onto nodes the cluster admin reserved
  • a section in the Job placement editor plus the run summary
  • README note that affinity + tolerations together are needed for tainted pools

Yes we definitely implement this. I have opened up issue #54 for this. Will open up a pr shortly for this

amh1k added 2 commits October 14, 2026 20:26
…form state

Disable early scheduling failure by default, wire configuration through Helm, and strengthen diagnostic lookup assertions.

Signed-off-by: amh1k <abdulmoizx97@gmail.com>
…ng Library

Signed-off-by: amh1k <abdulmoizx97@gmail.com>
@amh1k
amh1k requested a review from spron-in October 15, 2026 13:34

@spron-in spron-in left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks! The backend validation, the scheduling-failure tracking, and cleanup look solid. Requested changes below. Most of this is removing code; the backend and API contract stay as they are.

Needs to change

  1. Admin placement in Helm. Add runner.nodeSelector and runner.tolerations, applied to every runner Job. Affinity alone can't reach tainted, dedicated benchmark nodes, which is the usual setup for #40. Use nodeSelector rather than a Helm-level affinity: Kubernetes applies it together with the per-run nodeAffinity, so the two never need merging.
  2. Simplify the per-run editor. Replace the AND/OR, weights, and Gt/Lt editor with a list of key = value rows, sent as a single required rule using In. Keep the nodeAffinity API field so a richer editor can come later without an API change. Drop most of the TypeScript copy of the Kubernetes label rules in benchmarkNodeAffinity.ts; basic checks are enough, and the backend's 400 messages can be shown for the rest.
  3. Default schedulingGracePeriod to something like 2m, not 0. With 0, a run with an impossible rule stays pending for the full 5-minute timeout and holds one of the 2 run slots.
  4. Hide scheduler messages from users. Messages like 0/5 nodes are available: ...had untolerated taint reveal cluster topology to users who may have no Kubernetes access. Return a generic "no node matches the placement rules" and log the details on the backend.

Minor

  • The PR description still says unschedulable runs fail after 60 seconds; the code currently disables early failure by default.
  • A blank label value submits In [""], which only matches nodes whose label value is empty. Treat it as a form error.
  • Use stable IDs as React keys for groups and conditions instead of the array index.
  • Show validation errors only after a field has been touched.
  • The affinity is stored twice (Run.Request.NodeAffinity and Run.NodeAffinity); keep only the prepared copy.
  • Document that pending time counts against the execution timeout.

- Add runner.nodeSelector and runner.tolerations Helm values, validated at
  startup and applied to every runner Job, so Jobs can reach tainted,
  dedicated benchmark nodes. Per-run nodeAffinity is applied on top.
- Replace the AND/OR, weighted, Gt/Lt affinity editor with label key=value
  rows sent as a single required term. The nodeAffinity API is unchanged;
  the UI keeps only basic checks and relies on backend validation.
- Default schedulingGracePeriod to 2m so impossible placement rules don't
  hold a run slot for the whole execution timeout.
- Return a generic message for unschedulable runs and keep the scheduler
  diagnostic, which reveals cluster topology, in the backend log.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Sergey Pronin <sp@solanica.io>
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.

Affinity for jobs

2 participants