/* * Copyright (C) 2015 ScyllaDB */ /* * This file is part of Scylla. * * Scylla is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Scylla is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Scylla. If not, see . */ #pragma once #include "core/reactor.hh" #include "service/endpoint_lifecycle_subscriber.hh" #include "service/migration_listener.hh" #include "service/storage_proxy.hh" #include "cql3/query_processor.hh" #include "cql3/values.hh" #include "auth/authenticator.hh" #include "core/distributed.hh" #include #include #include #include #include namespace scollectd { class registrations; } class database; namespace transport { enum class cql_compression { none, lz4, snappy, }; enum cql_frame_flags { compression = 0x01, tracing = 0x02, }; struct [[gnu::packed]] cql_binary_frame_v1 { uint8_t version; uint8_t flags; uint8_t stream; uint8_t opcode; net::packed length; template void adjust_endianness(Adjuster a) { return a(length); } }; struct [[gnu::packed]] cql_binary_frame_v3 { uint8_t version; uint8_t flags; net::packed stream; uint8_t opcode; net::packed length; template void adjust_endianness(Adjuster a) { return a(stream, length); } }; enum class cql_load_balance { none, round_robin, }; cql_load_balance parse_load_balance(sstring value); struct cql_query_state { service::query_state query_state; std::unique_ptr options; cql_query_state(service::client_state& client_state) : query_state(client_state) { } }; class cql_server { private: class event_notifier; static constexpr cql_protocol_version_type current_version = cql_serialization_format::latest_version; std::vector _listeners; distributed& _proxy; distributed& _query_processor; size_t _max_request_size; semaphore _memory_available; seastar::metrics::metric_groups _metrics; std::unique_ptr _notifier; private: uint64_t _connects = 0; uint64_t _connections = 0; uint64_t _requests_served = 0; uint64_t _requests_serving = 0; cql_load_balance _lb; public: cql_server(distributed& proxy, distributed& qp, cql_load_balance lb); future<> listen(ipv4_addr addr, std::shared_ptr = {}, bool keepalive = false); future<> do_accepts(int which, bool keepalive); future<> stop(); public: class response; using response_type = std::pair, service::client_state>; private: class fmt_visitor; friend class process_request_executor; class connection : public boost::intrusive::list_base_hook<> { cql_server& _server; connected_socket _fd; input_stream _read_buf; output_stream _write_buf; seastar::gate _pending_requests_gate; future<> _ready_to_respond = make_ready_future<>(); cql_protocol_version_type _version = 0; cql_compression _compression = cql_compression::none; cql_serialization_format _cql_serialization_format = cql_serialization_format::latest(); service::client_state _client_state; std::unordered_map _query_states; unsigned _request_cpu = 0; enum class state : uint8_t { UNINITIALIZED, AUTHENTICATION, READY }; enum class tracing_request_type : uint8_t { not_requested, no_write_on_close, write_on_close }; state _state = state::UNINITIALIZED; ::shared_ptr _sasl_challenge; public: connection(cql_server& server, connected_socket&& fd, socket_address addr); ~connection(); future<> process(); future<> process_request(); future<> shutdown(); private: friend class process_request_executor; future process_request_one(bytes_view buf, uint8_t op, uint16_t stream, service::client_state client_state, tracing_request_type tracing_request); unsigned frame_size() const; unsigned pick_request_cpu(); cql_binary_frame_v3 parse_frame(temporary_buffer buf); future> read_and_decompress_frame(size_t length, uint8_t flags); future> read_frame(); future process_startup(uint16_t stream, bytes_view buf, service::client_state client_state); future process_auth_response(uint16_t stream, bytes_view buf, service::client_state client_state); future process_options(uint16_t stream, bytes_view buf, service::client_state client_state); future process_query(uint16_t stream, bytes_view buf, service::client_state client_state); future process_prepare(uint16_t stream, bytes_view buf, service::client_state client_state); future process_execute(uint16_t stream, bytes_view buf, service::client_state client_state); future process_batch(uint16_t stream, bytes_view buf, service::client_state client_state); future process_register(uint16_t stream, bytes_view buf, service::client_state client_state); shared_ptr make_unavailable_error(int16_t stream, exceptions::exception_code err, sstring msg, db::consistency_level cl, int32_t required, int32_t alive); shared_ptr make_read_timeout_error(int16_t stream, exceptions::exception_code err, sstring msg, db::consistency_level cl, int32_t received, int32_t blockfor, bool data_present); shared_ptr make_mutation_write_timeout_error(int16_t stream, exceptions::exception_code err, sstring msg, db::consistency_level cl, int32_t received, int32_t blockfor, db::write_type type); shared_ptr make_already_exists_error(int16_t stream, exceptions::exception_code err, sstring msg, sstring ks_name, sstring cf_name); shared_ptr make_unprepared_error(int16_t stream, exceptions::exception_code err, sstring msg, bytes id); shared_ptr make_error(int16_t stream, exceptions::exception_code err, sstring msg); shared_ptr make_ready(int16_t stream); shared_ptr make_supported(int16_t stream); shared_ptr make_result(int16_t stream, shared_ptr msg); shared_ptr make_topology_change_event(const transport::event::topology_change& event); shared_ptr make_status_change_event(const transport::event::status_change& event); shared_ptr make_schema_change_event(const transport::event::schema_change& event); shared_ptr make_autheticate(int16_t, const sstring&); shared_ptr make_auth_success(int16_t, bytes); shared_ptr make_auth_challenge(int16_t, bytes); future<> write_response(foreign_ptr>&& response, cql_compression compression = cql_compression::none); void check_room(bytes_view& buf, size_t n); void validate_utf8(sstring_view s); int8_t read_byte(bytes_view& buf); int32_t read_int(bytes_view& buf); int64_t read_long(bytes_view& buf); uint16_t read_short(bytes_view& buf); sstring read_string(bytes_view& buf); sstring_view read_string_view(bytes_view& buf); sstring_view read_long_string_view(bytes_view& buf); bytes_opt read_bytes(bytes_view& buf); bytes read_short_bytes(bytes_view& buf); cql3::raw_value read_value(bytes_view& buf); cql3::raw_value_view read_value_view(bytes_view& buf); void read_name_and_value_list(bytes_view& buf, std::vector& names, std::vector& values); void read_string_list(bytes_view& buf, std::vector& strings); void read_value_view_list(bytes_view& buf, std::vector& values); db::consistency_level read_consistency(bytes_view& buf); std::unordered_map read_string_map(bytes_view& buf); std::unique_ptr read_options(bytes_view& buf); std::unique_ptr read_options(bytes_view& buf, uint8_t); void init_cql_serialization_format(); friend event_notifier; }; friend class type_codec; private: bool _stopping = false; promise<> _all_connections_stopped; future<> _stopped = _all_connections_stopped.get_future(); boost::intrusive::list _connections_list; uint64_t _total_connections = 0; uint64_t _current_connections = 0; uint64_t _connections_being_accepted = 0; private: void maybe_idle() { if (_stopping && !_connections_being_accepted && !_current_connections) { _all_connections_stopped.set_value(); } } }; class cql_server::event_notifier : public service::migration_listener, public service::endpoint_lifecycle_subscriber { uint16_t _port; std::set _topology_change_listeners; std::set _status_change_listeners; std::set _schema_change_listeners; std::unordered_map _last_status_change; public: event_notifier(uint16_t port); void register_event(transport::event::event_type et, cql_server::connection* conn); void unregister_connection(cql_server::connection* conn); virtual void on_create_keyspace(const sstring& ks_name) override; virtual void on_create_column_family(const sstring& ks_name, const sstring& cf_name) override; virtual void on_create_user_type(const sstring& ks_name, const sstring& type_name) override; virtual void on_create_view(const sstring& ks_name, const sstring& view_name) override; virtual void on_create_function(const sstring& ks_name, const sstring& function_name) override; virtual void on_create_aggregate(const sstring& ks_name, const sstring& aggregate_name) override; virtual void on_update_keyspace(const sstring& ks_name) override; virtual void on_update_column_family(const sstring& ks_name, const sstring& cf_name, bool columns_changed) override; virtual void on_update_user_type(const sstring& ks_name, const sstring& type_name) override; virtual void on_update_view(const sstring& ks_name, const sstring& view_name, bool columns_changed) override; virtual void on_update_function(const sstring& ks_name, const sstring& function_name) override; virtual void on_update_aggregate(const sstring& ks_name, const sstring& aggregate_name) override; virtual void on_drop_keyspace(const sstring& ks_name) override; virtual void on_drop_column_family(const sstring& ks_name, const sstring& cf_name) override; virtual void on_drop_user_type(const sstring& ks_name, const sstring& type_name) override; virtual void on_drop_view(const sstring& ks_name, const sstring& view_name) override; virtual void on_drop_function(const sstring& ks_name, const sstring& function_name) override; virtual void on_drop_aggregate(const sstring& ks_name, const sstring& aggregate_name) override; virtual void on_join_cluster(const gms::inet_address& endpoint) override; virtual void on_leave_cluster(const gms::inet_address& endpoint) override; virtual void on_up(const gms::inet_address& endpoint) override; virtual void on_down(const gms::inet_address& endpoint) override; virtual void on_move(const gms::inet_address& endpoint) override; }; using response_type = cql_server::response_type; }