Grinding muh Gears! #672
No reviewers
Labels
No labels
accepted
bug
dependencies
docker
documentation
duplicate
enhancement
github-actions
github_actions
good first issue
gradle
hacktoberfest-accepted
help wanted
invalid
javascript
not-an-issue
npm
question
rejected
wontfix
Working on it
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Griefed/ServerPackCreator!672
Loading…
Reference in a new issue
No description provided.
Delete branch "develop"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The runtime image bakes in `USER 1000:1000` and `ContainerSpec.user` defaults to the same literal, but every container bind-mounts a directory the *host* process created. When the host identity is not uid 1000 -- which it stopped being the moment the daemon moved to `User=grinder` -- the container can read the pack and write nothing. Observed live 2026-08-23, with the cause 20 lines above the visible symptom: start.sh: line 568: ./.previousrun: Permission denied Warning: Failed to open the file ./server.jar: Permission denied start.sh: line 206: user_jvm_args.txt: Permission denied Error: could not open `user_jvm_args.txt' Both container paths are affected -- installs via DockerLoaderInstaller and mod boots via ContainerServerRunner -- so every verdict since the systemd migration is INCONCLUSIVE. Red: `ContainerUser` does not exist yet, and ContainerServerRunner takes no containerUser argument. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Audit iteration 22. P22-L1: `theWorkersGetTheRemainderOfTheWindowRatherThanASecondOne` asserted an absence -- `!contains("awaitStop(SHUTDOWN_GRACE)")` -- which passes for any spelling that is not that exact string. It now asserts what the code must do: take a deadline at entry and hand awaitStop the remainder. P22-M1: the TimeoutStopSec arithmetic hard-coded the batch size of 8 in a test, a unit comment and the README, so the promised single window silently became two at the deployed 10 workers. The guard now reads the real cap, which makes the relationship checkable rather than transcribed. Red: MAX_PARALLEL_STOPS is private to the engine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Two guards, both red at this commit. ContainerResourcesTest pins the cores -> CFS-quota arithmetic that SPC_GRINDER_CPUS is going to be, including the two ends nothing else would notice: a quota computed against an assumed period throttles a boot to a fraction of what was asked for (which then reads as a hanging mod, not as a misconfiguration), and a quota under 1ms is refused by the daemon at container-create time rather than at the knob. CpuLimitWiringTest pins the join `main` has to make, the same defect class ReportBindWiringTest exists for: every container-creating collaborator has accepted a ContainerResources since it existed, and `main` passes none -- so the cap is the hardcoded default and no environment can change it. Both call sites are asserted, the mod boot and the loader install. Red as committed: ContainerResourcesTest.kt:38:43 Unresolved reference 'forCpus'. ContainerResourcesTest.kt:55:64 Unresolved reference 'cpuPeriod'. (+6 more) -> compileTestKotlin FAILED Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Audit iteration 23, H1. forCpus reads its *computed* quota as the uncapped sentinel, so a count small enough to round to 0us returns quota 0 -- which is docker's "no limit", verified in the container's own cgroup as `max 100000`. A request for the smallest possible cap therefore yields none at all, the one direction a hardening knob must not fail, and against a KDoc promising the floor instead. Also pins the two values String.toDouble() accepts and the arithmetic cannot: infinity rounds to Long.MAX_VALUE (a quota so large it means uncapped) and NaN rounds to 0 (uncapped outright). Red as committed: aCapTooSmallToRoundIsStillACapAndNotUncapped expected: <1000> but was: <0> aNonFiniteCountIsRejectedRatherThanRoundedIntoNonsense Expected java.lang.IllegalArgumentException to be thrown, but nothing was thrown. L2 from the same audit: theDefaultIsUnchangedByTheKnobExisting was not a sentence; it asserts the shipped default is exactly two cores, so it now says that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Audit iteration 23, M1/M2/M4 — one concern, three surfaces, all the operator's view of the same knob. The startup line now says `cpus=2.0 cores (200000/100000µs)`, or `cpus=uncapped`, through ContainerResources.cpuCapDescription(). It used to print the derived quota, which answers in a unit nobody set, and at `SPC_GRINDER_CPUS=0` printed `cpuQuota=0/100000` -- "zero CPU" for the value that means the opposite. The raw pair rides along because it is what the kernel was given, so it can be compared against a container's own cpu.max when a boot looks throttled. Guard teeth checked by putting the old line back and watching theStartupLineStatesTheCapInTheOperators Unit fail; its matcher is bounded to the closing paren on its own line, since a lazy match to the first `)` stopped inside the call it asserts on. README: `### Capping CPU` had been inserted mid-section, leaving "Keep the host awake" -- a paragraph about suspends -- as the closing advice of the CPU section instead of the sizing one. Order restored, and the sizing opener ("a memory question rather than a CPU one") now points at the new section rather than contradicting it. deploy/install-grinder.sh listed "Three worth a decision rather than a default" and named WORKERS as the throughput lever without its CPU twin. Four now, with the cgroup caveat, because the installer is the operator's first surface and the one no guard test scans. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>SPC_GRINDER_MEMORY_GIB completes the per-container budget: cores and gibibytes now come from the environment through one ContainerResources.forLimits call, on exactly the rules the CPU cap established -- an exact 0 is uncapped, a smaller positive value is raised to the daemon's own floor ("Minimum memory limit allowed is 6MB") rather than refused by it, and negative or non-finite input throws. The default stays 3 GiB, so no existing install changes. It ships with a warning rather than as another throughput lever, because the default is load-bearing in three directions and only the third is obvious: - The packs the grinder builds leave `javaArgs` empty, so nothing passes -Xmx and the JVM derives the server's heap from the cgroup limit. Measured on Temurin 21: --memory=3g -> MaxHeapSize 805306368 (768 MiB, 25%), --memory=1g -> 268435456. Lowering the cap starves boots of heap. - It is the divisor in README §5's worker-sizing formula, so raising it without lowering SPC_GRINDER_WORKERS over-subscribes the host by exactly that factor. - Both failures are OOM kills scored INCONCLUSIVE -- they look like mods that hang, not like a misconfigured host, so the operator gets no signal that they did this. The startup line now reports both caps (`cpus=… memory=3.0 GiB`), the table row and unit comment carry the warning, and the sizing section gained the paragraph explaining why "grind faster" means WORKERS or CPUS and never this. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>