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:
Aleksandra Martyniuk
2023-12-08 19:21:28 +01:00
parent 20ba7ceff9
commit 5f7f403a15
5 changed files with 78 additions and 24 deletions

View File

@@ -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"
}
}
}

View File

@@ -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);

View File

@@ -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);
});

View File

@@ -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 {

View File

@@ -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"