tasks: api: add virtual task support
Virtual tasks are supported by get_task_status, abort_task and wait_task. Task status returned by get_task_status and wait_task: - contains task_kind to indicate whether it's virtual (cluster) or regular (node) task; - children list apart from task_id contains node address of the task.
This commit is contained in:
@@ -202,6 +202,20 @@
|
||||
}
|
||||
],
|
||||
"models":{
|
||||
"task_identity":{
|
||||
"id": "task_identity",
|
||||
"description":"Id and node of a task",
|
||||
"properties":{
|
||||
"task_id":{
|
||||
"type":"string",
|
||||
"description":"The uuid of a task"
|
||||
},
|
||||
"node":{
|
||||
"type":"string",
|
||||
"description":"Address of a server on which a task is created"
|
||||
}
|
||||
}
|
||||
},
|
||||
"task_stats" :{
|
||||
"id": "task_stats",
|
||||
"description":"A task statistics object",
|
||||
@@ -266,6 +280,14 @@
|
||||
"type":"string",
|
||||
"description":"The description of the task"
|
||||
},
|
||||
"kind":{
|
||||
"type":"string",
|
||||
"enum":[
|
||||
"node",
|
||||
"cluster"
|
||||
],
|
||||
"description":"The kind of a task"
|
||||
},
|
||||
"scope":{
|
||||
"type":"string",
|
||||
"description":"The scope of the task"
|
||||
@@ -335,9 +357,9 @@
|
||||
"children_ids":{
|
||||
"type":"array",
|
||||
"items":{
|
||||
"type":"string"
|
||||
"type":"task_identity"
|
||||
},
|
||||
"description":"Task IDs of children of this task"
|
||||
"description":"Task identities of children of this task"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,14 +33,18 @@ tm::task_status make_status(tasks::task_status status) {
|
||||
::gmtime_r(&end_time, &et);
|
||||
::gmtime_r(&start_time, &st);
|
||||
|
||||
std::vector<std::string> tis{status.children.size()};
|
||||
std::vector<tm::task_identity> tis{status.children.size()};
|
||||
boost::transform(status.children, tis.begin(), [] (const auto& child) {
|
||||
return child.to_sstring();
|
||||
tm::task_identity ident;
|
||||
ident.task_id = child.task_id.to_sstring();
|
||||
ident.node = fmt::format("{}", child.node);
|
||||
return ident;
|
||||
});
|
||||
|
||||
tm::task_status res{};
|
||||
res.id = status.task_id.to_sstring();
|
||||
res.type = status.type;
|
||||
res.kind = status.kind;
|
||||
res.scope = status.scope;
|
||||
res.state = status.state;
|
||||
res.is_abortable = bool(status.is_abortable);
|
||||
|
||||
@@ -16,10 +16,12 @@ namespace tasks {
|
||||
using task_status_variant = std::variant<tasks::task_manager::foreign_task_ptr, tasks::task_manager::task::task_essentials>;
|
||||
|
||||
static future<task_status> get_task_status(task_manager::task_ptr task) {
|
||||
auto broadcast_address = task->get_module()->get_task_manager().get_broadcast_address();
|
||||
auto local_task_status = task->get_status();
|
||||
auto status = task_status{
|
||||
.task_id = local_task_status.id,
|
||||
.type = task->type(),
|
||||
.kind = task_kind::node,
|
||||
.scope = local_task_status.scope,
|
||||
.state = local_task_status.state,
|
||||
.is_abortable = task->is_abortable(),
|
||||
@@ -34,9 +36,19 @@ static future<task_status> get_task_status(task_manager::task_ptr task) {
|
||||
.entity = local_task_status.entity,
|
||||
.progress_units = local_task_status.progress_units,
|
||||
.progress = co_await task->get_progress(),
|
||||
.children = co_await task->get_children().map_each_task<task_id>(
|
||||
[] (const task_manager::foreign_task_ptr& task) { return task->id(); },
|
||||
[] (const task_manager::task::task_essentials& task) { return task.task_status.id; })
|
||||
.children = co_await task->get_children().map_each_task<task_identity>(
|
||||
[broadcast_address] (const task_manager::foreign_task_ptr& task) {
|
||||
return task_identity{
|
||||
.node = broadcast_address,
|
||||
.task_id = task->id()
|
||||
};
|
||||
},
|
||||
[broadcast_address] (const task_manager::task::task_essentials& task) {
|
||||
return task_identity{
|
||||
.node = broadcast_address,
|
||||
.task_id = task.task_status.id
|
||||
};
|
||||
})
|
||||
};
|
||||
co_return status;
|
||||
}
|
||||
@@ -47,40 +59,51 @@ static future<task_status> get_foreign_task_status(const task_manager::foreign_t
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static T get_virtual_task_info(task_id id, std::optional<T> info) {
|
||||
// A service associated with the virtual task may have stopped tracking the operation
|
||||
// before its info got retrieved.
|
||||
if (!info) {
|
||||
throw task_manager::task_not_found(id);
|
||||
}
|
||||
return *info;
|
||||
}
|
||||
|
||||
future<task_status> task_handler::get_status() {
|
||||
auto task = co_await task_manager::invoke_on_task(_tm.container(), _id, std::function([id = _id] (task_manager::task_variant task_v) -> future<task_manager::foreign_task_ptr> {
|
||||
return task_manager::invoke_on_task(_tm.container(), _id, std::function([id = _id] (task_manager::task_variant task_v) -> future<task_status> {
|
||||
return std::visit(overloaded_functor{
|
||||
[] (task_manager::task_ptr task) -> future<task_manager::foreign_task_ptr> {
|
||||
[] (task_manager::task_ptr task) -> future<task_status> {
|
||||
if (task->is_complete()) {
|
||||
task->unregister_task();
|
||||
}
|
||||
co_return std::move(task);
|
||||
return get_task_status(std::move(task));
|
||||
},
|
||||
[id] (task_manager::virtual_task_ptr task) -> future<task_manager::foreign_task_ptr> {
|
||||
throw task_manager::task_not_found(id); // API does not support virtual tasks yet.
|
||||
[id] (task_manager::virtual_task_ptr task) -> future<task_status> {
|
||||
auto id_ = id;
|
||||
auto status = co_await task->get_status(id_);
|
||||
co_return get_virtual_task_info(id_, status);
|
||||
}
|
||||
}, task_v);
|
||||
}));
|
||||
co_return co_await get_foreign_task_status(task);
|
||||
}
|
||||
|
||||
future<task_status> task_handler::wait_for_task() {
|
||||
auto task = co_await task_manager::invoke_on_task(_tm.container(), _id, std::function([id = _id] (task_manager::task_variant task_v) -> future<task_manager::foreign_task_ptr> {
|
||||
return task_manager::invoke_on_task(_tm.container(), _id, std::function([id = _id] (task_manager::task_variant task_v) -> future<task_status> {
|
||||
return std::visit(overloaded_functor{
|
||||
[] (task_manager::task_ptr task) {
|
||||
return task->done().then_wrapped([task] (auto f) {
|
||||
// done() is called only because we want the task to be complete before getting its status.
|
||||
// The future should be ignored here as the result does not matter.
|
||||
f.ignore_ready_future();
|
||||
return make_foreign(task);
|
||||
return get_task_status(std::move(task));
|
||||
});
|
||||
},
|
||||
[id] (task_manager::virtual_task_ptr task) -> future<task_manager::foreign_task_ptr> {
|
||||
throw task_manager::task_not_found(id); // API does not support virtual tasks yet.
|
||||
[id] (task_manager::virtual_task_ptr task) -> future<task_status> {
|
||||
auto status = co_await task->get_status(id);
|
||||
co_return get_virtual_task_info(id, status);
|
||||
}
|
||||
}, task_v);
|
||||
}));
|
||||
co_return co_await get_foreign_task_status(task);
|
||||
}
|
||||
|
||||
future<utils::chunked_vector<task_status>> task_handler::get_status_recursively(bool local) {
|
||||
@@ -133,8 +156,11 @@ future<utils::chunked_vector<task_status>> task_handler::get_status_recursively(
|
||||
.entity = task.task_status.entity,
|
||||
.progress_units = task.task_status.progress_units,
|
||||
.progress = task.task_progress,
|
||||
.children = boost::copy_range<std::vector<task_id>>(task.failed_children | boost::adaptors::transformed([] (auto& child) {
|
||||
return child.task_status.id;
|
||||
.children = boost::copy_range<std::vector<task_identity>>(task.failed_children | boost::adaptors::transformed([&tm = _tm] (auto& child) {
|
||||
return task_identity{
|
||||
.node = tm.get_broadcast_address(),
|
||||
.task_id = child.task_status.id
|
||||
};
|
||||
}))
|
||||
};
|
||||
res.push_back(status);
|
||||
@@ -160,7 +186,7 @@ future<> task_handler::abort() {
|
||||
task->abort();
|
||||
},
|
||||
[id] (task_manager::virtual_task_ptr task) -> future<> {
|
||||
throw task_manager::task_not_found(id); // API does not support virtual tasks yet.
|
||||
return task->abort(id);
|
||||
}
|
||||
}, task_v);
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gms/inet_address.hh"
|
||||
#include "tasks/task_manager.hh"
|
||||
#include "utils/chunked_vector.hh"
|
||||
|
||||
@@ -21,6 +22,7 @@ struct task_identity {
|
||||
struct task_status {
|
||||
tasks::task_id task_id;
|
||||
std::string type;
|
||||
task_kind kind;
|
||||
std::string scope;
|
||||
task_manager::task_state state;
|
||||
is_abortable is_abortable;
|
||||
@@ -35,7 +37,7 @@ struct task_status {
|
||||
std::string entity;
|
||||
std::string progress_units;
|
||||
task_manager::task::progress progress;
|
||||
std::vector<tasks::task_id> children;
|
||||
std::vector<task_identity> children;
|
||||
};
|
||||
|
||||
struct task_stats {
|
||||
|
||||
@@ -105,9 +105,9 @@ def test_repair_task_progress(cql, this_dc, rest_api):
|
||||
status = statuses[0]
|
||||
assert "children_ids" in status, "No child tasks created"
|
||||
|
||||
for child_id in status["children_ids"]:
|
||||
for child_ident in status["children_ids"]:
|
||||
# Check if task state is correct.
|
||||
child_status = get_task_status(rest_api, child_id)
|
||||
child_status = get_task_status(rest_api, child_ident["task_id"])
|
||||
assert child_status["state"] == "running", "Incorrect task progress"
|
||||
assert child_status["progress_completed"] * 2 <= child_status["progress_total"], "Incorrect task progress"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user