add cpuList#1417
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
/run-security-scan |
alexcos20
left a comment
There was a problem hiding this comment.
AI automated code review (Gemini 3).
Overall risk: low
Summary:
The PR introduces a robust cpuList configuration feature to pin C2D compute containers to specific host CPU cores. The documentation, schema validation, and unit tests are comprehensive and well-executed. A few minor improvements are recommended, particularly around preventing unintended array mutations and ensuring fail-fast behavior on misconfiguration.
Comments:
• [WARNING][bug] The property this.configuredCpuList is assigned directly to allCores, which is then shared across environments. Since allocateCpus() dynamically assigns and removes free cores from this pool, it will unintentionally mutate the this.configuredCpuList class property. If the class property is read again later, it will yield the currently free cores instead of the configured cores. Consider cloning the array when assigning it to prevent state mutation.
- const allCores =
- this.configuredCpuList ?? Array.from({ length: physicalCpuCount }, (_, i) => i)
+ const allCores = this.configuredCpuList
+ ? [...this.configuredCpuList]
+ : Array.from({ length: physicalCpuCount }, (_, i) => i)• [INFO][other] Currently, returning false from resolveConfiguredCpuList causes updateEnvironmentResources to silently abort initialization, potentially leaving the engine in a broken, uninitialized state. Unless graceful degradation is explicitly desired by the calling code, consider throwing an Error to fail fast and explicitly prevent the node from starting with an invalid configuration.
- if (!this.resolveConfiguredCpuList(envConfig, sysinfo.NCPU)) {
- return
- }
+ if (!this.resolveConfiguredCpuList(envConfig, sysinfo.NCPU)) {
+ throw new Error(`Failed to initialize C2D engine: invalid cpuList configuration.`)
+ }• [INFO][security] While Number.isSafeInteger prevents overflow, it allows huge ranges (e.g., 0-9999999). If parseCpusetString expands this into an array in memory, it could cause an Out-Of-Memory (OOM) crash (Denial of Service). Assuming cpuList is only set by trusted node operators, this risk is low, but enforcing a realistic physical core limit (e.g., 8192) adds a good layer of defense.
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end)) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `cpuList range "${part}": core IDs are too large to be valid CPU IDs`,
path
})
continue
}
+ if (end > 8192) {
+ ctx.addIssue({
+ code: z.ZodIssueCode.custom,
+ message: `cpuList range "${part}": core ID exceeds maximum supported limit`,
+ path
+ })
+ }• [INFO][style] Excellent test coverage! The unit tests cover edge cases (e.g., out-of-order, duplicates, floats, overlaps) comprehensively. LGTM!
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@coderabbitai full review |
✅ Action performedFull review finished. Your plan includes PR reviews subject to rate limits. More reviews will be available in 42 minutes. |
Fixes #1414
Feature:
cpuList— restrict C2D CPU pinning to an explicit set of host coresSummary
The Docker C2D engine pins compute containers to physical cores (
CpusetCpus), but thecore pool was always
[0 .. NCPU-1], taken fromdocker.info(), andallocateCpus()always hands out the lowest-numbered free cores tracked in thenode's own in-memory allocation map. Consequently:
totalonly capped how much CPU could be allocated concurrently,never which core IDs were used — jobs always gravitated to the low-numbered cores;
the OS / other workloads, or partition the cores of a shared host between
independently-managed engines, which otherwise all pin to the same low cores).
This PR adds an explicit
cpuListoption on the connection-levelcpuresource inDOCKER_COMPUTE_ENVIRONMENTS, restricting the pinning pool to exactly the listed hostcore IDs.
[ { "socketPath": "/var/run/docker.sock", "resources": [{ "id": "cpu", "cpuList": "32-63" }], "environments": [ ... ] } ]What changed
src/@types/C2D/C2D.ts—ComputeResourcegains optionalcpuList?: string.src/utils/config/schemas.tsComputeResourceSchemaacceptscpuListand validates it strictly (see rulesbelow) via
superRefine, with a dedicated error message per violation.C2DDockerConfigSchemaenforces that a connection-levelcpuentry specifiesexactly one of
total/cpuList(both → error, neither → error), and rejectscpuListon non-cpu resources and inside env-level resource refs (same clean-breaktreatment as
init/driverVersion).❌ Invalid Ocean Node configurationissue list and exits (existingbuilder.tsbehavior — no new exit logic).src/components/c2d/compute_engine_docker.tsresolveConfiguredCpuList(): expands the (schema-validated) ranges at enginestart and checks them against the host's
NCPU. If any core ID doesn't exist on thehost, the engine logs an error and refuses to start (no envs created) instead of
silently pinning elsewhere.
cpuListwhen configured(previously always
[0 .. NCPU-1]).allocateCpus()therefore only ever hands outlisted cores; requests beyond the pool still return
nullrather than spilling tounlisted cores.
cpuList, the effective cputotal(andmaxcap) is the number of listedcores, so resource matching can never admit more CPU than the pinning pool holds.
containers, and restart recovery all consume the same pool/
CpusetCpuspaths.docs/env.md—cpuListdocumented under the connection-levelresourcesfields of
DOCKER_COMPUTE_ENVIRONMENTS.cpuListvalidation rulesFormat is deliberately strict; every violation is a startup error with its own message:
"0","3,5,7","0-1,3","0-15,32-47". A bare ID covers the single-cpu case ("0") and picking individualcores.
(
"15-15","20-10"→ rejected; write a single core as a bare ID instead)."0-8,4-12","8-11,0-3","0-3,2","3,3"→ rejected).total; the cpu entry must specify exactly one of the two.docker.info().NCPUatengine start; out-of-range IDs → engine is skipped with an explicit error).
Omitting
cpuListkeeps the current behavior (full host core range,totalsemanticsunchanged), so existing configs are unaffected.
Tests
src/test/unit/compute.test.ts:(
"0","3,5,7"), mixed ("0-1,3"), andtotal-only (regression);both/neither
total+cpuListrejected;cpuListon non-cpu resources and inenv-level refs rejected; 12 malformed/semantic cases rejected with the expected
message each.
resolveConfiguredCpuList()expansion (single range, multi-range,mixed ranges + bare IDs, single-cpu host
"0"), unrestricted default (nullpool), out-of-range abort;
resolveConnectionResourcePool()derives effective cputotal from the list length;
allocateCpus()only hands out listed cores, reusesreleased ones, and returns
nullwhen the restricted pool is exhausted.lint(0 errors) andtype-checkare clean.