/*
 * Copyright (C) 2020-present ScyllaDB
 */

/*
 * SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
 */
#include "service/raft/raft_group_registry.hh"
#include "raft/raft.hh"
#include "service/raft/raft_rpc.hh"
#include "service/raft/raft_timeout.hh"
#include "db/system_keyspace.hh"
#include "message/messaging_service.hh"
#include "gms/gossiper.hh"
#include "gms/i_endpoint_state_change_subscriber.hh"
#include "serializer_impl.hh"
#include "idl/raft.dist.hh"
#include "idl/raft_util.dist.hh"
#include "utils/composite_abort_source.hh"
#include "utils/error_injection.hh"
#include <seastar/core/shared_future.hh>

#include <chrono>
#include <seastar/core/coroutine.hh>
#include <seastar/core/when_all.hh>
#include <seastar/core/sleep.hh>

namespace service {

logging::logger rslog("raft_group_registry");

// {{{ direct_fd_proxy

class direct_fd_proxy : public raft::failure_detector, public direct_failure_detector::listener {
    std::unordered_set<raft::server_id> _alive_set;
    raft::server_id _my_id;

public:
    direct_fd_proxy(raft::server_id my_id)
            : _my_id(my_id)
    {
    }

    future<> mark_alive(direct_failure_detector::pinger::endpoint_id id) override {
        static constexpr auto msg = "marking Raft server {} as alive for raft groups";

        auto raft_id = raft::server_id{id};
        _alive_set.insert(raft_id);

        // The listener should be registered on every shard.
        // Write the message on INFO level only on shard 0 so we don't spam the logs.
        if (this_shard_id() == 0) {
            rslog.info(msg, raft_id);
        } else {
            rslog.debug(msg, raft_id);
        }

        co_return;
    }

    future<> mark_dead(direct_failure_detector::pinger::endpoint_id id) override {
        static constexpr auto msg = "marking Raft server {} as dead for raft groups";

        auto raft_id = raft::server_id{id};
        _alive_set.erase(raft_id);

        // As above.
        if (this_shard_id() == 0) {
            rslog.info(msg, raft_id);
        } else {
            rslog.debug(msg, raft_id);
        }

        co_return;
    }

    bool is_alive(raft::server_id srv) override {
        return srv == _my_id || _alive_set.contains(srv);
    }
};
// }}} direct_fd_proxy

raft_group_registry::raft_group_registry(
        raft::server_id my_id,
        netw::messaging_service& ms, direct_failure_detector::failure_detector& fd)
    : _ms(ms)
    , _direct_fd(fd)
    , _direct_fd_proxy(make_shared<direct_fd_proxy>(my_id))
    , _my_id(my_id)
{
}

void raft_group_registry::init_rpc_verbs() {
    auto handle_raft_rpc = [this] (
            const rpc::client_info& cinfo,
            const raft::group_id& gid, raft::server_id from, raft::server_id dst, auto handler) {
        constexpr bool is_one_way = std::is_void_v<std::invoke_result_t<decltype(handler), raft_rpc&>>;
        if (_my_id != dst) {
            if constexpr (is_one_way) {
                rslog.debug("Got message for server {}, but my id is {}", dst, _my_id);
                return make_ready_future<rpc::no_wait_type>(netw::messaging_service::no_wait());
            } else {
                throw raft_destination_id_not_correct{_my_id, dst};
            }
        }

        return container().invoke_on(shard_for_group(gid),
                [addr = netw::messaging_service::get_source(cinfo).addr, gid, handler] (raft_group_registry& self) mutable {
            auto& rpc = self.get_rpc(gid);
            // Execute the actual message handling code
            if constexpr (is_one_way) {
                handler(rpc);
                return netw::messaging_service::no_wait();
            } else {
                return handler(rpc);
            }
        });
    };

    ser::raft_rpc_verbs::register_raft_send_snapshot(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::install_snapshot snp) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, snp = std::move(snp)] (raft_rpc& rpc) mutable {
            return rpc.apply_snapshot(std::move(from), std::move(snp));
        });
    });

    ser::raft_rpc_verbs::register_raft_append_entries(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
        raft::group_id gid, raft::server_id from, raft::server_id dst, raft::append_request append_request) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, append_request = std::move(append_request), original_shard_id = this_shard_id(), gid] (raft_rpc& rpc) mutable {
            if (auto ignore_group_id = utils::get_local_injector().inject_parameter<std::string_view>("raft_drop_incoming_append_entries_for_specified_group"); ignore_group_id) {
                if (gid == raft::group_id{utils::UUID(*ignore_group_id)}) {
                    return;
                }
            }

            // lw_shared_ptr (raft::log_entry_ptr) doesn't support cross-shard ref counting (see debug_shared_ptr_counter_type),
            // so we are copying entries to this shard if it isn't equal the original one.
            rpc.append_entries(std::move(from),
                this_shard_id() == original_shard_id ? std::move(append_request) : append_request.copy());
        });
    });

    ser::raft_rpc_verbs::register_raft_append_entries_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::append_reply reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, reply = std::move(reply)] (raft_rpc& rpc) mutable {
            rpc.append_entries_reply(std::move(from), std::move(reply));
        });
    });

    ser::raft_rpc_verbs::register_raft_vote_request(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::vote_request vote_request) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, vote_request] (raft_rpc& rpc) mutable {
            rpc.request_vote(std::move(from), std::move(vote_request));
        });
    });

    ser::raft_rpc_verbs::register_raft_vote_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::vote_reply vote_reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, vote_reply] (raft_rpc& rpc) mutable {
            rpc.request_vote_reply(std::move(from), std::move(vote_reply));
        });
    });

    ser::raft_rpc_verbs::register_raft_timeout_now(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::timeout_now timeout_now) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, timeout_now] (raft_rpc& rpc) mutable {
            rpc.timeout_now_request(std::move(from), std::move(timeout_now));
        });
    });

    ser::raft_rpc_verbs::register_raft_read_quorum(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::read_quorum read_quorum) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, read_quorum] (raft_rpc& rpc) mutable {
            rpc.read_quorum_request(std::move(from), std::move(read_quorum));
        });
    });

    ser::raft_rpc_verbs::register_raft_read_quorum_reply(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::read_quorum_reply read_quorum_reply) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, read_quorum_reply] (raft_rpc& rpc) mutable {
            rpc.read_quorum_reply(std::move(from), std::move(read_quorum_reply));
        });
    });

    ser::raft_rpc_verbs::register_raft_execute_read_barrier_on_leader(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from] (raft_rpc& rpc) mutable {
            return rpc.execute_read_barrier(from);
        });
    });

    ser::raft_rpc_verbs::register_raft_add_entry(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst, raft::command cmd) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst, [from, cmd = std::move(cmd)] (raft_rpc& rpc) mutable {
            return rpc.execute_add_entry(from, std::move(cmd));
        });
    });

    ser::raft_rpc_verbs::register_raft_modify_config(&_ms, [handle_raft_rpc] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst,
            std::vector<raft::config_member> add, std::vector<raft::server_id> del) mutable {
        return handle_raft_rpc(cinfo, gid, from, dst,
            [from, add = std::move(add), del = std::move(del)] (raft_rpc& rpc) mutable {

            return rpc.execute_modify_config(from, std::move(add), std::move(del));
        });
    });

    ser::raft_util_rpc_verbs::register_raft_read_barrier(&_ms, [this] (const rpc::client_info& cinfo, rpc::opt_time_point timeout,
            raft::group_id gid, raft::server_id from, raft::server_id dst) -> future<> {
        if (_my_id != dst) {
            throw raft_destination_id_not_correct{_my_id, dst};
        }
        co_await container().invoke_on(shard_for_group(gid),
                [gid, timeout] (raft_group_registry& self) -> future<> {
            co_await self.get_server_with_timeouts(gid).read_barrier(nullptr, raft_timeout{.value = timeout});
        });
    });

    ser::raft_rpc_verbs::register_direct_fd_ping(&_ms,
            [this] (const rpc::client_info&, rpc::opt_time_point timeout, raft::server_id dst) -> future<direct_fd_ping_reply> {

        if (timeout && *timeout <= netw::messaging_service::clock_type::now()) {
            throw timed_out_error{};
        }

        if (_my_id != dst) {
            return make_ready_future<direct_fd_ping_reply>(direct_fd_ping_reply {
                .result = wrong_destination {
                    .reached_id = _my_id,
                },
            });
        }

        return make_ready_future<direct_fd_ping_reply>(direct_fd_ping_reply {
            .result = service::group_liveness_info{
                .group0_alive = _group0_is_alive,
            }
        });
    });
}

future<> raft_group_registry::uninit_rpc_verbs() {
    return when_all_succeed(
        ser::raft_rpc_verbs::unregister_raft_send_snapshot(&_ms),
        ser::raft_rpc_verbs::unregister_raft_append_entries(&_ms),
        ser::raft_rpc_verbs::unregister_raft_append_entries_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_vote_request(&_ms),
        ser::raft_rpc_verbs::unregister_raft_vote_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_timeout_now(&_ms),
        ser::raft_rpc_verbs::unregister_raft_read_quorum(&_ms),
        ser::raft_rpc_verbs::unregister_raft_read_quorum_reply(&_ms),
        ser::raft_rpc_verbs::unregister_raft_execute_read_barrier_on_leader(&_ms),
        ser::raft_rpc_verbs::unregister_raft_add_entry(&_ms),
        ser::raft_rpc_verbs::unregister_raft_modify_config(&_ms),
        ser::raft_rpc_verbs::unregister_direct_fd_ping(&_ms),
        ser::raft_util_rpc_verbs::unregister_raft_read_barrier(&_ms)
    ).discard_result();
}

void raft_group_registry::destroy_server(raft::group_id gid) {
    const auto it = _servers.find(gid);
    if (it == _servers.end()) {
        on_internal_error(rslog, format("destroy_server(): no server for group {}", gid));
    }
    if (!it->second.aborted || !it->second.aborted->available()) {
        on_internal_error(rslog, format("destroy_server(): the server for group {} is not aborted", gid));
    }
    _servers.erase(it);
}

seastar::future<> raft_group_registry::start() {
    // Once a Raft server starts, it soon times out
    // and starts an election, so RPC must be ready by
    // then to send VoteRequest messages.
    init_rpc_verbs();

    direct_fd_clock::base::duration threshold{std::chrono::seconds{2}};
    if (const auto ms = utils::get_local_injector().inject_parameter<int64_t>("raft-group-registry-fd-threshold-in-ms"); ms) {
        threshold = direct_fd_clock::base::duration{std::chrono::milliseconds{*ms}};
    }
    _direct_fd_subscription.emplace(co_await _direct_fd.register_listener(*_direct_fd_proxy, threshold.count()));
}

const raft::server_id& raft_group_registry::get_my_raft_id() {
    return _my_id;
}

seastar::future<> raft_group_registry::stop() {
    if (_servers.size() > 0) {
        on_internal_error(rslog, format("stop(): server for group {} is not destroyed", _servers.begin()->first));
    }
    co_await uninit_rpc_verbs();
    _direct_fd_subscription.reset();
}

raft_server_for_group& raft_group_registry::server_for_group(raft::group_id gid) {
    auto it = _servers.find(gid);
    if (it == _servers.end()) {
        throw raft_group_not_found(gid);
    }
    return it->second;
}

raft_rpc& raft_group_registry::get_rpc(raft::group_id gid) {
    return server_for_group(gid).rpc;
}

raft::server& raft_group_registry::get_server(raft::group_id gid) {
    auto ptr = server_for_group(gid).server.get();
    if (!ptr) {
        on_internal_error(rslog, format("get_server(): no server for group {}", gid));
    }
    return *ptr;
}

raft_server_with_timeouts raft_group_registry::get_server_with_timeouts(raft::group_id gid) {
    auto& group_server = server_for_group(gid);
    if (!group_server.server.get()) {
        on_internal_error(rslog, format("get_server(): no server for group {}", gid));
    }
    return raft_server_with_timeouts(group_server, failure_detector());
}

raft::server* raft_group_registry::find_server(raft::group_id gid) {
    auto it = _servers.find(gid);
    if (it == _servers.end()) {
        return nullptr;
    }
    return it->second.server.get();
}

std::vector<raft::group_id> raft_group_registry::all_groups() const {
    std::vector<raft::group_id> result;
    result.reserve(_servers.size());
    for (auto& [gid, _]: _servers) {
        result.push_back(gid);
    }
    return result;
}

raft::server& raft_group_registry::group0() {
    if (!_group0_id) {
        on_internal_error(rslog, "group0(): _group0_id not present");
    }
    return get_server(*_group0_id);
}

raft::group_id raft_group_registry::group0_id() const {
    if (!_group0_id) {
        on_internal_error(rslog, "group0_id(): _group0_id not present");
    }
    return *_group0_id;
}

raft_server_with_timeouts raft_group_registry::group0_with_timeouts() {
    if (!_group0_id) {
        on_internal_error(rslog, "group0(): _group0_id not present");
    }
    return get_server_with_timeouts(*_group0_id);
}

future<> raft_group_registry::send_raft_read_barrier(raft::group_id gid, raft::server_id dst) {
    auto timeout = raft_ticker_type::clock::now() + std::chrono::minutes(1);
    co_await ser::raft_util_rpc_verbs::send_raft_read_barrier(&_ms, locator::host_id{dst.uuid()}, timeout, gid, _my_id, dst);
}

future<> raft_group_registry::start_server_for_group(raft_server_for_group new_grp) {
    auto gid = new_grp.gid;

    const auto is_group0 = this_shard_id() == 0 && !_group0_id;

    if (_servers.contains(gid)) {
        on_internal_error(rslog, format("Attempt to add the second instance of raft server with the same gid={}", gid));
    }

    try {
        // start the server instance prior to arming the ticker timer.
        // By the time the tick() is executed the server should already be initialized.
        co_await new_grp.server->start();
        if (is_group0) {
            new_grp.server->register_metrics();
        }
    } catch (abort_requested_exception&) {
        throw;
    } catch (...) {
        on_internal_error(rslog, format("Failed to start a Raft group {}: {}", gid,
                std::current_exception()));
    }

    std::exception_ptr ex;
    raft::server& server = *new_grp.server;

    try {
        auto [it, inserted] = _servers.emplace(std::move(gid), std::move(new_grp));

        if (is_group0) {
            _group0_id = gid;
        }

        it->second.ticker->arm_periodic(raft_tick_interval);
    } catch (...) {
        ex = std::current_exception();
    }

    if (ex) {
        co_await server.abort();
        std::rethrow_exception(ex);
    }

    co_await container().invoke_on_all([is_group0, shard = this_shard_id(), gid] (raft_group_registry& rg) {
        if (is_group0) {
            rg._group0_is_alive = true;
        }
        rg._group_shards[gid] = shard;
    });
}

future<> raft_group_registry::abort_server(raft::group_id gid, sstring reason) {
    // abort_server could be called from on_background_error for group0
    // when the server has not yet been added to _servers.
    // In this case we won't find gid in the if below.
    if (const auto it = _servers.find(gid); it != _servers.end()) {
        auto& [gid, s] = *it;
        if (!s.aborted) {
            co_await container().invoke_on_all([is_group0 = (gid == _group0_id), gid] (raft_group_registry& rg) {
                if (is_group0) {
                    rg._group0_is_alive = false;
                }
                rg._group_shards.erase(gid);
            });
            s.aborted = s.server->abort(std::move(reason))
                .handle_exception([gid] (std::exception_ptr ex) {
                    rslog.warn("Failed to abort raft group server {}: {}", gid, ex);
                });
        }
        co_await s.aborted->get_future();
    }
}

unsigned raft_group_registry::shard_for_group(const raft::group_id& gid) const {
    auto it = _group_shards.find(gid);
    if (it == _group_shards.end()) {
        throw raft_group_not_found(gid);
    }
    return it->second;
}

shared_ptr<raft::failure_detector> raft_group_registry::failure_detector() {
    return _direct_fd_proxy;
}

raft_group_registry::~raft_group_registry() = default;

namespace {
    auto fmt_loc(const std::source_location& l) {
        return fmt::format("{}({}:{}) `{}`", l.file_name(), l.line(), l.column(), l.function_name());
    };
}

raft_server_with_timeouts::raft_server_with_timeouts(raft_server_for_group& group_server, shared_ptr<raft::failure_detector> fd)
    : _group_server(group_server)
    , _fd(fd)
{
}

template <std::invocable<abort_source*> Op>
std::invoke_result_t<Op, abort_source*>
raft_server_with_timeouts::run_with_timeout(Op&& op, const char* op_name,
    seastar::abort_source* as, std::optional<raft_timeout> timeout)
{
    if (!timeout) {
        co_return co_await op(as);
    }
    if (!timeout->value) {
        if (!_group_server.default_op_timeout_in_ms) {
            on_internal_error(rslog, ::format("raft operation [{}], timeout requested at [{}],"
                                              "but no value for it has been defined",
                op_name, fmt_loc(timeout->loc)));
        }
        timeout->value = lowres_clock::now() + std::chrono::milliseconds(_group_server.default_op_timeout_in_ms->get());
    }
    utils::composite_abort_source composite_as;

    abort_on_expiry<> expiry{*timeout->value};
    composite_as.add(expiry.abort_source());

    if (as) {
        composite_as.add(*as);
    }

    try {
        co_return co_await op(&composite_as.abort_source());
    } catch (const raft::request_aborted& e) {
        if (!expiry.abort_source().abort_requested() || (as && as->abort_requested())) {
            throw;
        }
        sstring quorum_message;
        {
            unsigned voters_count = 0;
            std::vector<raft::server_id> dead_voters;
            for (const auto& c: _group_server.server->get_configuration().current) {
                if (c.can_vote) {
                    ++voters_count;
                    if (!_fd->is_alive(c.addr.id)) {
                        dead_voters.push_back(c.addr.id);
                    }
                }
            }
            if (voters_count > 0 && dead_voters.size() >= (voters_count + 1) / 2) {
                std::string dead_voters_str;
                for (const auto id: dead_voters) {
                    fmt::format_to(std::back_inserter(dead_voters_str), ",{}", id);
                    if (dead_voters_str.size() > 512) {
                        dead_voters_str.append("...");
                        break;
                    }
                }
                quorum_message = ::format(", there is no raft quorum, total voters count {}, alive voters count {}, dead voters [{}]",
                    voters_count, voters_count - dead_voters.size(), std::string_view(dead_voters_str).substr(1));
            }
        }
        const auto message = ::format("group [{}] raft operation [{}] timed out{}",
            _group_server.gid, op_name, quorum_message);
        static thread_local logger::rate_limit rate_limit{std::chrono::seconds(1)};
        rslog.log(log_level::warn, rate_limit, "{}; timeout requested at [{}], original error {}",
            message, fmt_loc(timeout->loc), std::current_exception());
        throw raft_operation_timeout_error(message.c_str());
    }
}

future<> raft_server_with_timeouts::add_entry(raft::command command, raft::wait_type type,
        seastar::abort_source& as, std::optional<raft_timeout> timeout)
{
    return run_with_timeout([&](abort_source* as) {
            return _group_server.server->add_entry(std::move(command), type, as);
        }, "add_entry", &as, timeout);
}

future<> raft_server_with_timeouts::modify_config(std::vector<raft::config_member> add, std::vector<raft::server_id> del,
        seastar::abort_source* as, std::optional<raft_timeout> timeout)
{
    return run_with_timeout([&](abort_source* as) {
            return _group_server.server->modify_config(std::move(add), std::move(del), as);
        }, "modify_config", as, timeout);
}

future<bool> raft_server_with_timeouts::trigger_snapshot(seastar::abort_source* as, std::optional<raft_timeout> timeout)
{
    return run_with_timeout([&](abort_source* as) {
        return _group_server.server->trigger_snapshot(as);
    }, "trigger_snapshot", as, timeout);
}

future<> raft_server_with_timeouts::read_barrier(seastar::abort_source* as, std::optional<raft_timeout> timeout)
{
    return run_with_timeout([&](abort_source* as) -> future<> {
        co_await utils::get_local_injector().inject("sleep_in_read_barrier", std::chrono::seconds(1));
        co_return co_await _group_server.server->read_barrier(as);
    }, "read_barrier", as, timeout);
}

future<bool> direct_fd_pinger::ping(direct_failure_detector::pinger::endpoint_id id, direct_failure_detector::clock::timepoint_t timeout, abort_source& as, direct_failure_detector::clock& c) {
    auto dst_id = raft::server_id{id};

    try {
        std::chrono::milliseconds timeout_ms = c.to_milliseconds(timeout);
        netw::messaging_service::clock_type::time_point deadline = netw::messaging_service::clock_type::now() + timeout_ms;
        auto reply = co_await ser::raft_rpc_verbs::send_direct_fd_ping(&_ms, locator::host_id{id}, deadline, as, dst_id);
        if (auto* wrong_dst = std::get_if<wrong_destination>(&reply.result)) {
            // FIXME: after moving to host_id based verbs we will not get `wrong_destination`
            //        any more since the connection will fail
            // This may happen e.g. when node B is replacing node A with the same IP.
            // When we ping node A, the pings will reach node B instead.
            // B will detect they were destined for node A and return wrong_destination.
            rslog.trace("ping(id = {}): wrong destination (reached {})",
                        dst_id, wrong_dst->reached_id);
            co_return false;
        } else if (auto* info = std::get_if<group_liveness_info>(&reply.result)) {
            co_return info->group0_alive;
        }
    } catch (seastar::rpc::closed_error&) {
        co_return false;
    }
    co_return true;
}

direct_failure_detector::clock::timepoint_t direct_fd_clock::now() noexcept {
    return base::now().time_since_epoch().count();
}

future<> direct_fd_clock::sleep_until(direct_failure_detector::clock::timepoint_t tp, abort_source& as) {
    auto t = base::time_point{base::duration{tp}};
    auto n = base::now();
    if (t <= n) {
        return make_ready_future<>();
    }

    return sleep_abortable(t - n, as);
}

std::chrono::milliseconds direct_fd_clock::to_milliseconds(direct_failure_detector::clock::timepoint_t tp) const {
    auto t = base::time_point{base::duration{tp}};
    auto n = base::now();
    return std::chrono::duration_cast<std::chrono::milliseconds>(t - n);
}


} // end of namespace service
