Today, the "system.clients" virtual table lists active connections (and
their various properties, like client address, logged in username and
client version) only for CQL requests. In this patch we make Alternator
active clients also be listed on this virtual table.
Unlike CQL where logged in username applies to a complete connection,
in the Alternator API, different requests, theoretically signed by
different users, can arrive over the same HTTP connection. So instead of
listing the currently open *connections*, we list the currently active
*requests*.
This means that when scanning system.clients, you will only see requests
which are being handled right now - and not inactive HTTP connections.
I think this good enough (besides being the correct thing to do) - one
of the goals of this system.clients is to be able to see what kind of
drivers are being used by the user (the "driver_name" field in the
system.clients) - on a busy server there will always be some (even many)
requests being handled, so we'll always have plenty of requests to see
in system.clients.
By the way, note that for Alternator requests, what we use for the
"driver_name" is the request's User-Agent header. AWS SDKs typically
write the driver's name, its version, and often a lot of other
information in that header. For example, Boto3 sends a User-Agent
looking like:
Boto3/1.38.46 md/Botocore#1.38.46 md/awscrt#0.24.2
ua/2.1 os/linux#6.15.4-100.fc41.x86_64 md/arch#x86_64
lang/python#3.13.5 md/pyimpl#CPython m/N,P,b,D,Z
cfg/retry-mode#legacy Botocore/1.38.46 Resource
A functional test for the new feature - adding Alternator requests to
the system.clients table - will be in the next patch.
Fixes #24993
Signed-off-by: Nadav Har'El <nyh@scylladb.com>
113 lines
4.4 KiB
C++
113 lines
4.4 KiB
C++
/*
|
|
* Copyright 2019-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "alternator/executor.hh"
|
|
#include "utils/scoped_item_list.hh"
|
|
#include <seastar/core/future.hh>
|
|
#include <seastar/core/condition-variable.hh>
|
|
#include <seastar/http/httpd.hh>
|
|
#include <seastar/net/tls.hh>
|
|
#include <optional>
|
|
#include "alternator/auth.hh"
|
|
#include "service/qos/service_level_controller.hh"
|
|
#include "utils/small_vector.hh"
|
|
#include "utils/updateable_value.hh"
|
|
#include <seastar/core/units.hh>
|
|
|
|
struct client_data;
|
|
|
|
namespace alternator {
|
|
|
|
using chunked_content = rjson::chunked_content;
|
|
|
|
class server : public peering_sharded_service<server> {
|
|
static constexpr size_t content_length_limit = 16*MB;
|
|
using alternator_callback = std::function<future<executor::request_return_type>(executor&, executor::client_state&,
|
|
tracing::trace_state_ptr, service_permit, rjson::value, std::unique_ptr<http::request>)>;
|
|
using alternator_callbacks_map = std::unordered_map<std::string_view, alternator_callback>;
|
|
|
|
httpd::http_server _http_server;
|
|
httpd::http_server _https_server;
|
|
executor& _executor;
|
|
service::storage_proxy& _proxy;
|
|
gms::gossiper& _gossiper;
|
|
auth::service& _auth_service;
|
|
qos::service_level_controller& _sl_controller;
|
|
|
|
key_cache _key_cache;
|
|
utils::updateable_value<bool> _enforce_authorization;
|
|
utils::small_vector<std::reference_wrapper<seastar::httpd::http_server>, 2> _enabled_servers;
|
|
named_gate _pending_requests;
|
|
// In some places we will need a CQL updateable_timeout_config object even
|
|
// though it isn't really relevant for Alternator which defines its own
|
|
// timeouts separately. We can create this object only once.
|
|
updateable_timeout_config _timeout_config;
|
|
|
|
alternator_callbacks_map _callbacks;
|
|
|
|
semaphore* _memory_limiter;
|
|
utils::updateable_value<uint32_t> _max_concurrent_requests;
|
|
|
|
::shared_ptr<seastar::tls::server_credentials> _credentials;
|
|
|
|
class json_parser {
|
|
static constexpr size_t yieldable_parsing_threshold = 16*KB;
|
|
chunked_content _raw_document;
|
|
rjson::value _parsed_document;
|
|
std::exception_ptr _current_exception;
|
|
semaphore _parsing_sem{1};
|
|
condition_variable _document_waiting;
|
|
condition_variable _document_parsed;
|
|
abort_source _as;
|
|
future<> _run_parse_json_thread;
|
|
public:
|
|
json_parser();
|
|
// Moving a chunked_content into parse() allows parse() to free each
|
|
// chunk as soon as it is parsed, so when chunks are relatively small,
|
|
// we don't need to store the sum of unparsed and parsed sizes.
|
|
future<rjson::value> parse(chunked_content&& content);
|
|
future<> stop();
|
|
};
|
|
json_parser _json_parser;
|
|
|
|
// The server maintains a list of ongoing requests, that are being handled
|
|
// by handle_api_request(). It uses this list in get_client_data(), which
|
|
// is called when reading the "system.clients" virtual table.
|
|
struct ongoing_request {
|
|
socket_address _client_address;
|
|
sstring _user_agent;
|
|
sstring _username;
|
|
scheduling_group _scheduling_group;
|
|
bool _is_https;
|
|
client_data make_client_data() const;
|
|
};
|
|
utils::scoped_item_list<ongoing_request> _ongoing_requests;
|
|
|
|
public:
|
|
server(executor& executor, service::storage_proxy& proxy, gms::gossiper& gossiper, auth::service& service, qos::service_level_controller& sl_controller);
|
|
|
|
future<> init(net::inet_address addr, std::optional<uint16_t> port, std::optional<uint16_t> https_port, std::optional<tls::credentials_builder> creds,
|
|
utils::updateable_value<bool> enforce_authorization, semaphore* memory_limiter, utils::updateable_value<uint32_t> max_concurrent_requests);
|
|
future<> stop();
|
|
// get_client_data() is called (on each shard separately) when the virtual
|
|
// table "system.clients" is read. It is expected to generate a list of
|
|
// clients connected to this server (on this shard). This function is
|
|
// called by alternator::controller::get_client_data().
|
|
future<utils::chunked_vector<client_data>> get_client_data();
|
|
private:
|
|
void set_routes(seastar::httpd::routes& r);
|
|
// If verification succeeds, returns the authenticated user's username
|
|
future<std::string> verify_signature(const seastar::http::request&, const chunked_content&);
|
|
future<executor::request_return_type> handle_api_request(std::unique_ptr<http::request> req);
|
|
};
|
|
|
|
}
|
|
|