Skip to content

feat(tenants): add dedicatedCPU quota sugar for pinned-CPU workloads - #2435

Closed
Andrei Kvapil (kvaps) wants to merge 1 commit into
mainfrom
feat/tenant-quota-dedicated-cpu
Closed

Andrei Kvapil (kvaps) wants to merge 1 commit into
mainfrom
feat/tenant-quota-dedicated-cpu

Conversation

@kvaps

@kvaps Andrei Kvapil (kvaps) commented Apr 21, 2026 •

Copy link
Copy Markdown
Member

What this PR does

Adds an optional dedicatedCPU key to tenant resourceQuotas (via the cozy-lib resource sanitizer). Its value is added 1:1 to both limits.cpu and requests.cpu, bypassing the cpuAllocationRatio divisor.

Problem

The library chart divides tenant requests.cpu by cpuAllocationRatio (default: 10), which matches how KubeVirt sizes virt-launcher pods for regular VMs. Instance types with dedicatedCPUPlacement (the cx* series, and other Guaranteed-QoS workloads) do not follow that ratio: their pods have requests.cpu == limits.cpu. As a result, a single dedicated-CPU VM exhausts the tenant's requests.cpu quota N times faster than its limits.cpu quota, shrinking the usable tenant capacity for CX-series workloads to cpu / cpuAllocationRatio.

Solution

Operators can now declare dedicated capacity separately:

resourceQuotas:
  cpu: 10          # oversubscribable (burstable) CPU budget
  dedicatedCPU: 2  # plus 2 cores of dedicated physical CPU

At the default ratio of 10 this produces limits.cpu=12, requests.cpu=3 — both sides of the ResourceQuota stay in lockstep regardless of which instance-type series a workload uses.

Existing tenant configurations (cpu only) are unaffected: the sanitizer output is byte-identical when dedicatedCPU is absent.

Release note

feat(tenants): add `dedicatedCPU` key to tenant `resourceQuotas` for correctly accounting CPU-pinned workloads (KubeVirt CX-series instance types and other Guaranteed-QoS pods). The value is added 1:1 to both `limits.cpu` and `requests.cpu`, bypassing `cpuAllocationRatio`.

Summary by CodeRabbit

  • New Features

    • Added support for dedicatedCPU resource quota allocation, which applies directly to both CPU limits and requests without ratio calculations. Can be combined with standard cpu allocation.
  • Documentation

    • Updated resource quota documentation with examples demonstrating dedicatedCPU usage and combined configurations.
  • Tests

    • Added test coverage for dedicatedCPU quota functionality, including standalone and combined allocation scenarios.

Introduce an optional `dedicatedCPU` key in the cozy-lib resource sanitizer
(and therefore in tenant `resourceQuotas`). Its value is added 1:1 to both
`limits.cpu` and `requests.cpu`, bypassing the `cpuAllocationRatio` divisor.

This fixes the quota mismatch between regular VMs (where KubeVirt divides pod
`requests.cpu` by `cpuAllocationRatio`) and instance types with
`dedicatedCPUPlacement` (where pod `requests == limits`): previously, a single
dedicated-CPU VM could exhaust the tenant's `requests.cpu` quota while the
`limits.cpu` quota stayed largely unused, effectively shrinking the usable
tenant capacity for CX-series workloads to `cpu / cpuAllocationRatio`.

With the new key, operators can declare dedicated capacity explicitly:

    resourceQuotas:
      cpu: 10          # oversubscribable (burstable) CPU budget
      dedicatedCPU: 2  # plus 2 cores of dedicated physical CPU

This yields `limits.cpu=12, requests.cpu=3` at the default ratio of 10, so
both sides of the ResourceQuota stay in lockstep regardless of which
instance-type series a workload uses.

Co-Authored-By: Claude <[email protected]>
Signed-off-by: Andrei Kvapil <[email protected]>
@coderabbitai

coderabbitai Bot commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The changes introduce support for a new dedicatedCPU quota parameter that applies 1:1 CPU allocation to both requests and limits, bypassing the standard cpuAllocationRatio behavior. Updates include documentation, template logic for processing the parameter, and corresponding test cases.

Changes

Cohort / File(s) Summary
Documentation
packages/apps/tenant/README.md
Added dedicatedCPU quota key documentation explaining 1:1 allocation behavior and provided YAML example combining cpu and dedicatedCPU with expected ResourceQuota output.
Template Implementation
packages/library/cozy-lib/templates/_resources.tpl
Extended cozy-lib.resources.sanitize to handle dedicatedCPU flat-map input, converting it to float and directly merging into both limits.cpu and requests.cpu without ratio conversion.
Test Cases
packages/tests/cozy-lib-tests/tests/quota_test.yaml
Added two new test assertions validating dedicatedCPU-only and combined cpu + dedicatedCPU scenarios with expected quota values.
Test Fixtures
packages/tests/cozy-lib-tests/tests/quota_values_dedicated.yaml, packages/tests/cozy-lib-tests/tests/quota_values_combined.yaml
Added test fixture files with configurations for dedicatedCPU-only and combined CPU quota scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A swift hare bounds with dedicated zeal,
CPU quotas get a cleaner deal!
One-to-one, no ratios bind,
Cleaner resource logic to find! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding a dedicatedCPU quota feature for pinned-CPU workloads, which directly corresponds to all modified files and the PR's core objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/tenant-quota-dedicated-cpu

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 and usage tips.

@kvaps Andrei Kvapil (kvaps) added the kind/backport Categorizes issue or PR as requiring a backport to the current release line label Apr 21, 2026
@kvaps
Andrei Kvapil (kvaps) marked this pull request as ready for review April 21, 2026 12:51
@dosubot dosubot Bot added the size/M This PR changes 30-99 lines, ignoring generated files label Apr 21, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances tenant resource management by providing a mechanism to accurately account for CPU-pinned workloads. By allowing operators to specify dedicated CPU capacity separately from oversubscribable CPU, the system ensures that resource quotas remain balanced for Guaranteed-QoS pods, preventing premature exhaustion of tenant capacity.

Highlights

  • New dedicatedCPU parameter: Introduced an optional dedicatedCPU key to tenant resourceQuotas to support workloads requiring pinned physical cores, such as KubeVirt CX-series instance types.
  • Resource calculation logic: Updated the resource sanitizer to add dedicatedCPU values at a 1:1 ratio to both limits.cpu and requests.cpu, bypassing the standard cpuAllocationRatio divisor.
  • Documentation and testing: Updated documentation with usage examples and added new test cases to verify correct quota calculation for dedicated CPU workloads.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@dosubot dosubot Bot added the kind/feature Categorizes issue or PR as related to a new feature label Apr 21, 2026
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 21, 2026

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces support for dedicatedCPU in resource quotas, allowing for workloads that require pinned physical cores. The cozy-lib library's sanitize function was updated to add the dedicatedCPU value to both CPU limits and requests at a 1:1 ratio, ensuring these resources are not oversubscribed. Documentation and examples were added to the tenant README, and new test cases were included to verify the calculation logic. All review comments were filtered out as they primarily validated the implementation or provided praise without suggesting actionable improvements; therefore, I have no feedback to provide.

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/apps/tenant/README.md (1)

95-139: ⚠️ Potential issue | 🟡 Minor

Move resourceQuotas documentation to values.yaml and regenerate README.

The README is generated from values.yaml by the cozyvalues-gen tool (see Makefile: cozyvalues-gen ... -r README.md). Manual edits to README.md will be overwritten when make generate runs.

Add the detailed cpu, dedicatedCPU, memory, ephemeral-storage, storage, and quota documentation as comments in the resourceQuotas parameter section of values.yaml, then run make generate to regenerate the README.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/apps/tenant/README.md` around lines 95 - 139, The detailed
resourceQuotas documentation in README.md needs to be moved into values.yaml and
the README regenerated: copy the explanations for resourceQuotas (including cpu,
dedicatedCPU, memory, ephemeral-storage, storage, and the object count quotas
like pods, services.loadbalancers, etc.) as commented docs under the
resourceQuotas parameter in values.yaml, ensure the comment formatting matches
other parameter comments so cozyvalues-gen will pick it up, then run the
documented generation step (cozyvalues-gen via the Makefile target — run make
generate or the exact cozyvalues-gen ... -r README.md command) to regenerate
README.md so it is not overwritten by future runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/apps/tenant/README.md`:
- Around line 95-139: The detailed resourceQuotas documentation in README.md
needs to be moved into values.yaml and the README regenerated: copy the
explanations for resourceQuotas (including cpu, dedicatedCPU, memory,
ephemeral-storage, storage, and the object count quotas like pods,
services.loadbalancers, etc.) as commented docs under the resourceQuotas
parameter in values.yaml, ensure the comment formatting matches other parameter
comments so cozyvalues-gen will pick it up, then run the documented generation
step (cozyvalues-gen via the Makefile target — run make generate or the exact
cozyvalues-gen ... -r README.md command) to regenerate README.md so it is not
overwritten by future runs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: bf50111c-3596-4722-ba8e-ec5940238736

📥 Commits

Reviewing files that changed from the base of the PR and between 7287b02 and e01b449.

📒 Files selected for processing (5)
  • packages/apps/tenant/README.md
  • packages/library/cozy-lib/templates/_resources.tpl
  • packages/tests/cozy-lib-tests/tests/quota_test.yaml
  • packages/tests/cozy-lib-tests/tests/quota_values_combined.yaml
  • packages/tests/cozy-lib-tests/tests/quota_values_dedicated.yaml

@lllamnyp

Timofei Larkin (lllamnyp) commented Apr 21, 2026 •

Copy link
Copy Markdown
Member

Thanks for the careful writeup, but I'd like to close this PR without merging. Reasoning below, roughly in order of weight.

The premise that this is a bug is debatable. The motivating complaint — "my requests.cpu is exhausted while I have limits.cpu headroom" — is the system working as designed. We run with cpuAllocationRatio=10 because regular VMs and burstable pods are ~10× overcommittable. Dedicated-CPU workloads pin physical cores and are ~10× more expensive to the platform than burstable ones. Charging them 10× more quota is the correct signal. What the tenant is experiencing is a UX/communication problem ("why is this full?"), not a quota-math problem.

The PR doesn't actually make limits/requests "stay in lockstep." It just shifts where the mismatch breaks. With cpu: 10, dedicatedCPU: 2 → limits.cpu=12, requests.cpu=3: a tenant running only dedicated VMs hits requests.cpu=3 first (at 3 cores); a tenant running only burstable hits limits.cpu=12 first (at 12 cores). The ratio is still wrong for any mix that doesn't match the admin's forecast — the admin is just pre-baking a guess about the tenant's dedicated-vs-burstable split into the quota. That is already achievable today by handing the tenant a different cpu: number; we don't need a second knob for the same degree of freedom.

dedicatedCPU doesn't reserve anything for dedicated workloads. A tenant granted cpu: 10, dedicatedCPU: 2 can spend the entire budget on burstable VMs. The name implies a capacity reservation the implementation does not deliver.

It leaks into every app that uses cozy-lib.resources.sanitize. The template is shared between tenant quotas and pod-level resources: across ~25 apps (postgres, redis, kafka, kubernetes, mariadb, …) via defaultingSanitize. A user writing resources: { cpu: "2", dedicatedCPU: "1" } on a postgres cluster will have the key quietly accepted and folded into the pod's requests/limits math — but nothing at the pod level pins CPU (no CPU manager static policy is triggered, QoS isn't changed, no dedicatedCPUPlacement field is set). The name promises behavior that only happens at the KubeVirt VMI layer; here it's just number-shuffling. That's a footgun.

Sugar is lossy. The rendered ResourceQuota is just limits.cpu=12, requests.cpu=3. An operator inspecting live cluster state can't tell whether that came from cpu: 12 with a custom ratio, cpu: 10 + dedicatedCPU: 2, or hand-tuned values. The "dedicated" intent isn't preserved anywhere downstream — dashboards and any future quota-remaining surface can't render it.

API hygiene. resourceQuotas has so far been map[k8s resource name → quantity] (cpu, memory, storage, pods, services.loadbalancers, …). dedicatedCPU would be the first synthetic chart-only key in that map. Once shipped, removing it is a breaking change, and it sets the precedent that every future "tune how the sanitizer behaves" knob can ride in as a fake resource name. If we later decide the right answer is to redesign tenant CPU accounting, we'll have to keep dedicatedCPU alive forever alongside the new model.

Where I'd rather put the effort. The real user pain — "I don't understand why my requests quota is full while limits isn't" — is a presentation problem. I'd like to address it at the API/UI surface that shows a tenant their quota: render remaining capacity in two complementary views, e.g. "≈10 vCPU of burstable workload remaining, or ≈1 physical core of dedicated workload remaining." That resolves the confusion without committing the quota model to a new first-class concept. Happy to open an issue to scope that out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress kind/backport Categorizes issue or PR as requiring a backport to the current release line kind/feature Categorizes issue or PR as related to a new feature lgtm This PR has been approved by a maintainer size/M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants