Merge "nix-eval-jobs: reenable tests, rework waitpid handling" into main

This commit is contained in:
Maximilian Bosch
2025-03-15 13:56:23 +00:00
committed by Gerrit Code Review
3 changed files with 39 additions and 12 deletions
+1 -2
View File
@@ -295,8 +295,7 @@
# System tests.
tests = import ./tests/nixos { inherit lib nixpkgs nixpkgsFor; } // {
# this test is *incredibly* flaky, sometimes taking six tries in ci to unflake
# nix-eval-jobs = forAllSystems (system: self.packages.${system}.nix-eval-jobs.tests.nix-eval-jobs);
nix-eval-jobs = forAllSystems (system: self.packages.${system}.nix-eval-jobs.tests.nix-eval-jobs);
# This is x86_64-linux only, just because we have significantly
# cheaper x86_64-linux compute in CI.
+18 -5
View File
@@ -1,3 +1,4 @@
#include <chrono>
#include <lix/config.h> // IWYU pragma: keep
#include <lix/libexpr/eval-settings.hh>
@@ -35,6 +36,7 @@
#include <set>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <vector>
@@ -157,7 +159,7 @@ struct State {
std::map<std::string, nlohmann::json> jobs;
};
void handleBrokenWorkerPipe(Proc &proc, std::string_view msg) {
void handleBrokenWorkerPipe(Proc &proc, std::string_view msg, bool retry = true) {
// we already took the process status from Proc, no
// need to wait for it again to avoid error messages
pid_t pid = proc.pid.release();
@@ -165,10 +167,21 @@ void handleBrokenWorkerPipe(Proc &proc, std::string_view msg) {
int status;
int rc = waitpid(pid, &status, WNOHANG);
if (rc == 0) {
kill(pid, SIGKILL);
throw Error("BUG: while %s, worker pipe got closed but evaluation "
"worker still running?",
msg);
// If the worker dies (e.g. with a SIGSEGV due to an unnoticed infinite
// recursion), it closes the pipes first and then exits. Now it may happen
// that a read from the pipe happens when the process is still alive, but the
// pipes are closed.
// This is still a valid condition and shouldn't be reported as `BUG:`. Hence
// we wait a bit and then retry.
if (retry) {
std::this_thread::sleep_for(std::chrono::seconds(1));
handleBrokenWorkerPipe(proc, msg, false);
} else {
kill(pid, SIGKILL);
throw Error("BUG: while %s, worker pipe got closed but evaluation "
"worker still running?",
msg);
}
} else if (rc == -1) {
kill(pid, SIGKILL);
throw Error(
+20 -5
View File
@@ -68,17 +68,17 @@ def common_test(extra_args: List[str]) -> List[Dict[str, Any]]:
assert built_job["outputs"]["out"].startswith("/nix/store")
assert built_job["drvPath"].endswith(".drv")
assert built_job["meta"]["broken"] is False
check_gc_root(tempdir, built_job['drvPath'])
check_gc_root(tempdir, built_job["drvPath"])
dotted_job = results[1]
assert dotted_job["attr"] == '"dotted.attr"'
assert dotted_job["attrPath"] == ["dotted.attr"]
check_gc_root(tempdir, dotted_job['drvPath'])
check_gc_root(tempdir, dotted_job["drvPath"])
recurse_drv = results[2]
assert recurse_drv["attr"] == "recurse.drvB"
assert recurse_drv["name"] == "drvB"
check_gc_root(tempdir, recurse_drv['drvPath'])
check_gc_root(tempdir, recurse_drv["drvPath"])
substituted_job = results[3]
assert substituted_job["attr"] == "substitutedJob"
@@ -138,8 +138,23 @@ def test_recursion_error() -> None:
],
)
print(stderr)
assert "packageWithInfiniteRecursion" in stderr
assert "possible infinite recursion" in stderr
# Closing pipes and exiting is not atomic, so it is possible
# that the worker is still up when the collector notices that
# the pipe is closed already. We mitigate this condition a bit
# by waiting a second and checking the state of the worker again,
# but assuming the infrec error is still racy.
# Hence, we assert that one of the two possible outcomes actually happen.
assert (
(
"packageWithInfiniteRecursion" in stderr
and "possible infinite recursion" in stderr
)
or
(
"worker pipe got closed" in stderr
)
)
def test_constituents() -> None: