From 87d46ad5d66c080023d89a27decfa0eadb6c14eb Mon Sep 17 00:00:00 2001
From: Graham Christensen <graham@grahamc.com>
Date: Tue, 16 Mar 2021 16:09:36 -0400
Subject: [PATCH] hydra-queue-runner: --build-one: correctly handle a cached
 build

Previously, the build ID would never flow through channels which
exited.

This patch tracks the buildOne state as part of State and exits avoids
waiting forever for new work.

The code around buildOnly is a bit rough, making this a bit weird to
implement but since it is only used for testing the value of improving
it on its own is a bit questionable.
---
 src/hydra-queue-runner/builder.cc       | 14 +++++++-------
 src/hydra-queue-runner/dispatcher.cc    |  5 ++---
 src/hydra-queue-runner/queue-monitor.cc | 13 +++++++++++--
 src/hydra-queue-runner/state.hh         |  4 ++--
 4 files changed, 22 insertions(+), 14 deletions(-)

diff --git a/src/hydra-queue-runner/builder.cc b/src/hydra-queue-runner/builder.cc
index 7f8830f9..89aa7d15 100644
--- a/src/hydra-queue-runner/builder.cc
+++ b/src/hydra-queue-runner/builder.cc
@@ -148,7 +148,8 @@ State::StepResult State::doBuildStep(nix::ref<Store> destStore,
             localStore->printStorePath(step->drvPath), repeats + 1, machine->sshName, buildId, (dependents.size() - 1));
     }
 
-    bool quit = buildId == buildOne && step->drvPath == *buildDrvPath;
+    if (!buildOneDone)
+        buildOneDone = buildId == buildOne && step->drvPath == *buildDrvPath;
 
     RemoteResult result;
     BuildOutput res;
@@ -265,7 +266,7 @@ State::StepResult State::doBuildStep(nix::ref<Store> destStore,
         if (retry) {
             auto mc = startDbUpdate();
             stepFinished = true;
-            if (quit) exit(1);
+            if (buildOneDone) exit(1);
             return sRetry;
         }
     }
@@ -376,7 +377,7 @@ State::StepResult State::doBuildStep(nix::ref<Store> destStore,
         }
 
     } else
-        failStep(*conn, step, buildId, result, machine, stepFinished, quit);
+        failStep(*conn, step, buildId, result, machine, stepFinished);
 
     // FIXME: keep stats about aborted steps?
     nrStepsDone++;
@@ -386,7 +387,7 @@ State::StepResult State::doBuildStep(nix::ref<Store> destStore,
     machine->state->totalStepTime += stepStopTime - stepStartTime;
     machine->state->totalStepBuildTime += result.stopTime - result.startTime;
 
-    if (quit) exit(0); // testing hack; FIXME: this won't run plugins
+    if (buildOneDone) exit(0); // testing hack; FIXME: this won't run plugins
 
     return sDone;
 }
@@ -398,8 +399,7 @@ void State::failStep(
     BuildID buildId,
     const RemoteResult & result,
     Machine::ptr machine,
-    bool & stepFinished,
-    bool & quit)
+    bool & stepFinished)
 {
     /* Register failure in the database for all Build objects that
        directly or indirectly depend on this step. */
@@ -481,7 +481,7 @@ void State::failStep(
             b->finishedInDB = true;
             builds_->erase(b->id);
             dependentIDs.push_back(b->id);
-            if (buildOne == b->id) quit = true;
+            if (!buildOneDone && buildOne == b->id) buildOneDone = true;
         }
     }
 
diff --git a/src/hydra-queue-runner/dispatcher.cc b/src/hydra-queue-runner/dispatcher.cc
index 6dc7f700..8c497a66 100644
--- a/src/hydra-queue-runner/dispatcher.cc
+++ b/src/hydra-queue-runner/dispatcher.cc
@@ -374,7 +374,6 @@ void State::abortUnsupported()
             if (!build) build = *dependents.begin();
 
             bool stepFinished = false;
-            bool quit = false;
 
             failStep(
                 *conn, step, build->id,
@@ -385,9 +384,9 @@ void State::abortUnsupported()
                     .startTime = now2,
                     .stopTime = now2,
                 },
-                nullptr, stepFinished, quit);
+                nullptr, stepFinished);
 
-            if (quit) exit(1);
+            if (buildOneDone) exit(1);
         }
     }
 
diff --git a/src/hydra-queue-runner/queue-monitor.cc b/src/hydra-queue-runner/queue-monitor.cc
index 9c92f635..26d780b2 100644
--- a/src/hydra-queue-runner/queue-monitor.cc
+++ b/src/hydra-queue-runner/queue-monitor.cc
@@ -35,14 +35,17 @@ void State::queueMonitorLoop()
 
     unsigned int lastBuildId = 0;
 
-    while (true) {
+    bool quit = false;
+    while (!quit) {
         localStore->clearPathInfoCache();
 
         bool done = getQueuedBuilds(*conn, destStore, lastBuildId);
 
+        if (buildOne && buildOneDone) quit = true;
+
         /* Sleep until we get notification from the database about an
            event. */
-        if (done) {
+        if (done && !quit) {
             conn->await_notification();
             nrQueueWakeups++;
         } else
@@ -65,6 +68,8 @@ void State::queueMonitorLoop()
             processJobsetSharesChange(*conn);
         }
     }
+
+    exit(0);
 }
 
 
@@ -160,6 +165,7 @@ bool State::getQueuedBuilds(Connection & conn,
 
             /* Some step previously failed, so mark the build as
                failed right away. */
+            if (!buildOneDone && build->id == buildOne) buildOneDone = true;
             printMsg(lvlError, "marking build %d as cached failure due to ā€˜%s’",
                 build->id, localStore->printStorePath(ex.step->drvPath));
             if (!build->finishedInDB) {
@@ -231,6 +237,7 @@ bool State::getQueuedBuilds(Connection & conn,
             auto mc = startDbUpdate();
             pqxx::work txn(conn);
             time_t now = time(0);
+            if (!buildOneDone && build->id == buildOne) buildOneDone = true;
             printMsg(lvlInfo, "marking build %1% as succeeded (cached)", build->id);
             markSucceededBuild(txn, build, res, true, now, now);
             notifyBuildFinished(txn, build->id, {});
@@ -289,6 +296,8 @@ bool State::getQueuedBuilds(Connection & conn,
         for (auto & r : newRunnable)
             makeRunnable(r);
 
+        if (buildOne && newRunnable.size() == 0) buildOneDone = true;
+
         nrBuildsRead += nrAdded;
 
         /* Stop after a certain time to allow priority bumps to be
diff --git a/src/hydra-queue-runner/state.hh b/src/hydra-queue-runner/state.hh
index 57fd5a77..5a5c5f94 100644
--- a/src/hydra-queue-runner/state.hh
+++ b/src/hydra-queue-runner/state.hh
@@ -367,6 +367,7 @@ private:
 
     /* Specific build to do for --build-one (testing only). */
     BuildID buildOne;
+    bool buildOneDone = false;
 
     /* Statistics per machine type for the Hydra auto-scaler. */
     struct MachineType
@@ -485,8 +486,7 @@ private:
         BuildID buildId,
         const RemoteResult & result,
         Machine::ptr machine,
-        bool & stepFinished,
-        bool & quit);
+        bool & stepFinished);
 
     Jobset::ptr createJobset(pqxx::work & txn,
         const std::string & projectName, const std::string & jobsetName);