Files
scylladb/service/session.hh
Benny Halevy 0a1e7de6ea service: session: use named gate
Signed-off-by: Benny Halevy <bhalevy@scylladb.com>
2025-04-12 11:28:49 +03:00

129 lines
3.9 KiB
C++

/*
* Copyright (C) 2023-present ScyllaDB
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
*/
#pragma once
#include "utils/UUID.hh"
#include <seastar/core/gate.hh>
#include <seastar/core/shared_future.hh>
#include <seastar/core/semaphore.hh>
#include <boost/intrusive/list.hpp>
#include <unordered_set>
namespace service {
using session_id = utils::tagged_uuid<struct session_id_tag>;
// We want it be different than default-constructed session_id to catch mistakes.
constexpr session_id default_session_id = session_id(
utils::UUID(0x81e7fc5a8d4411ee, 0x8577325096b39f47)); // timeuuid 2023-11-27 16:46:27.182089.0 UTC
/// Session is used to track execution of work related to some greater task, identified by session_id.
/// Work can enter the session using enter(), and is considered to be part of the session
/// as long as the guard returned by enter() is alive.
///
/// Session goes over the following states monotonically:
/// 1) open - accepts work via enter()
/// 2) closing - rejects work via enter()
/// 3) closed - rejects work via enter(), and no guards are alive anymore
///
/// Sessions are removed only after they are closed, it's impossible to have a session::guard of a session
/// which is not in the registry.
class session {
public:
using link_type = bi::list_member_hook<bi::link_mode<bi::auto_unlink>>;
private:
session_id _id;
seastar::named_gate _gate;
std::optional<shared_future<>> _closed;
link_type _link;
public:
using list_type = boost::intrusive::list<session,
boost::intrusive::member_hook<session, session::link_type, &session::_link>,
boost::intrusive::constant_time_size<false>>;
public:
class guard {
session* _session;
seastar::gate::holder _holder;
public:
explicit guard(session& s);
guard(const guard&) noexcept = default;
guard(guard&&) noexcept = default;
guard& operator=(guard&&) noexcept = default;
guard& operator=(const guard&) noexcept = default;
~guard();
void check() const {
if (!valid()) {
throw seastar::abort_requested_exception();
}
}
[[nodiscard]] bool valid() const {
return !_session->_closed;
}
};
explicit session(session_id id) : _id(id), _gate("session") {}
guard enter() {
return guard(*this);
};
/// No new work is admitted to enter the session after this.
/// Can be called many times.
void start_closing() noexcept {
if (!_closed) {
_closed = seastar::shared_future<>(_gate.close());
}
}
/// Returns true iff the session is not open.
/// Calling enter() in this state will fail.
bool is_closing() const {
return bool(_closed);
}
session_id id() const {
return _id;
}
/// Post-condition of successfully resolved future: There are no guards alive for this session, and
/// and it's impossible to create more such guards later.
/// Can be called concurrently.
future<> close() {
start_closing();
return _closed->get_future();
}
};
class session_manager {
std::unordered_map<session_id, std::unique_ptr<session>> _sessions;
session::list_type _closing_sessions;
seastar::semaphore _session_drain_sem{1};
public:
session_manager();
session::guard enter_session(session_id id);
/// Creates a session on this shard if it doesn't exist yet.
/// If the session already exists does nothing.
void create_session(session_id);
/// Calls start_closing() on all sessions except those in keep.
void initiate_close_of_sessions_except(const std::unordered_set<session_id>& keep);
/// Post-condition: All sessions which are in closing state before the call will be in closed state after the call.
/// Can be called concurrently.
future<> drain_closing_sessions();
};
} // namespace service