Files
scylla/service/topology_guard.hh
Tomasz Grabiec 0e42fe4c3c storage_service: Introduce concept of a topology_guard
topology_guard is used to track distributed operations started by the
topology change coordinator, e.g. streaming, to make sure that those
operations have no side effects after topology change coordinator
moved to the next migration stage, of a given tablet or of the whole
ring.

topology_guard can be sent over the wire in the form of
frozen_topology_guard. It can be materialized again on the other
side. While in transit, it doesn't block the coordinator barriers. But
if the coordinator moved on, materialization of the guard will
fail. So tracking safety is preserved.

In this patch, the guard implementation is based on tracking work
under global sessions, but the concept is flexible and other
mechanisms can be used without changing user code.
2023-12-05 14:09:35 +01:00

75 lines
2.8 KiB
C++

/*
* Copyright (C) 2023-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "service/session.hh"
#include "replica/database_fwd.hh"
namespace service {
/// Represents topology_guard in transit.
/// Can be passed across nodes and copied across shards.
/// Guard in transit doesn't block barriers, but can be materialized into topology_guard
/// which does. If the guard was invalidated while in transit, materialization will fail.
/// Has an null state which represents no guard.
/// topology_guard created out of null frozen_topology_guard is valid and never aborted.
using frozen_topology_guard = session_id;
/// Represents a guard which is always valid and never aborted.
constexpr frozen_topology_guard null_topology_guard = default_session_id;
session_manager& get_topology_session_manager();
/// A guard used by operations started by the topology change coordinator whose scope
/// of operation is supposed to be limited to a single transition stage.
/// The coordinator invalidates guards, typically when it advances to the next stage.
/// Invalidated guards are still alive and block barriers, but their check() will throw, which
/// gives the holder a chance to interrupt the operation and release the guard early.
/// The token metadata barrier executed by the coordinator waits for guards to die, even the
/// invalidated ones.
///
/// Guards allow coordinators to make sure there are no side-effects from operations started
/// in earlier stages of topology change operation. If all side-effects are performed under
/// the guard's scope, then executing a token metadata barrier after invalidating the guard
/// guarantees that there can be no side-effects from earlier stages.
/// The token metadata barrier must contact all nodes which may have alive guards.
template <typename T> concept TopologyGuard = requires(T guard, replica::table& t, frozen_topology_guard frozen_guard) {
// Acquiring the guard.
{ T(t, frozen_guard) } -> std::same_as<T>;
// Moving the guard.
{ T(std::move(guard)) } -> std::same_as<T>;
// Checking if the guard was invalidated.
{ guard.check() } -> std::same_as<void>;
};
/// TopologyGuard implementation where the scope of the guard's validity
/// is limited to the scope of a session validity.
/// The topology change coordinator invalidates the guards by closing the session.
class session_topology_guard {
session::guard _guard;
public:
using frozen = session_id;
session_topology_guard(replica::table& t, frozen_topology_guard g)
: _guard(get_topology_session_manager().enter_session(g))
{ }
void check() {
return _guard.check();
}
};
static_assert(TopologyGuard<session_topology_guard>);
using topology_guard = session_topology_guard;
} // namespace service