|
@@ -1,6 +1,7 @@
|
|
|
#include "execution_controller.hpp"
|
|
#include "execution_controller.hpp"
|
|
|
#include "../webserver_service.hpp"
|
|
#include "../webserver_service.hpp"
|
|
|
#include "logging/logger.hpp"
|
|
#include "logging/logger.hpp"
|
|
|
|
|
+#include "common/time_utils.hpp"
|
|
|
#include "proto/runner.grpc.pb.h"
|
|
#include "proto/runner.grpc.pb.h"
|
|
|
#include <grpcpp/grpcpp.h>
|
|
#include <grpcpp/grpcpp.h>
|
|
|
#include <algorithm>
|
|
#include <algorithm>
|
|
@@ -296,7 +297,7 @@ void ExecutionController::cancelExecution(const httplib::Request& req, httplib::
|
|
|
proto::CancelExecutionRequest grpc_req;
|
|
proto::CancelExecutionRequest grpc_req;
|
|
|
grpc_req.set_execution_id(id);
|
|
grpc_req.set_execution_id(id);
|
|
|
|
|
|
|
|
- proto::Empty grpc_res;
|
|
|
|
|
|
|
+ proto::CancelExecutionResponse grpc_res;
|
|
|
::grpc::ClientContext grpc_ctx;
|
|
::grpc::ClientContext grpc_ctx;
|
|
|
grpc_ctx.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(15));
|
|
grpc_ctx.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(15));
|
|
|
|
|
|
|
@@ -306,6 +307,31 @@ void ExecutionController::cancelExecution(const httplib::Request& req, httplib::
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // The runner answered, but it has never heard of this execution. A runner
|
|
|
|
|
+ // keeps what it is running in memory, so a restart loses it - and the record
|
|
|
|
|
+ // is left saying "running" with nobody left to finish it. Reporting
|
|
|
|
|
+ // "cancelling" here is how one sat at 14 hours: the button worked, the
|
|
|
|
|
+ // request arrived, and nothing could ever act on it.
|
|
|
|
|
+ if (!grpc_res.was_running()) {
|
|
|
|
|
+ const nlohmann::json patch = {
|
|
|
|
|
+ {"status", "cancelled"},
|
|
|
|
|
+ {"error", "The runner was no longer running this - it restarted while the "
|
|
|
|
|
+ "execution was in flight"},
|
|
|
|
|
+ {"finishedAt", common::TimeUtils::nowMs()}
|
|
|
|
|
+ };
|
|
|
|
|
+ auto updated = storage_.update("executions", id, patch, 0, true);
|
|
|
|
|
+ if (updated.failed()) {
|
|
|
|
|
+ sendError(res, "Could not cancel: " + updated.error().message(), 500);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ LOG_WARN("Execution {} was not running on {}; marked cancelled in the database", id, runner_id);
|
|
|
|
|
+ ws_server_.broadcast("executions." + id + ".cancelled", {{"executionId", id}});
|
|
|
|
|
+ sendJson(res, {{"success", true}, {"status", "cancelled"},
|
|
|
|
|
+ {"note", "The runner is no longer running this - it restarted while the "
|
|
|
|
|
+ "execution was in flight - so the record was closed"}});
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Deliberately not writing the status here. Cancellation is honoured at the
|
|
// Deliberately not writing the status here. Cancellation is honoured at the
|
|
|
// next node boundary, and the runner writes the finished record itself - a
|
|
// next node boundary, and the runner writes the finished record itself - a
|
|
|
// status written now would be overwritten moments later, and would claim
|
|
// status written now would be overwritten moments later, and would claim
|