| /** @file |
| |
| Pre-Warming NetVConnection |
| |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include "proxy/http/PreWarmAlgorithm.h" |
| |
| #include "iocore/eventsystem/EventSystem.h" |
| #include "iocore/net/NetVConnection.h" |
| |
| // inknet |
| #include "iocore/net/PreWarm.h" |
| #include "iocore/net/SSLSNIConfig.h" |
| #include "iocore/net/YamlSNIConfig.h" |
| |
| #include "iocore/hostdb/HostDB.h" |
| #include "iocore/net/NetTimeout.h" |
| #include "proxy/Milestones.h" |
| |
| #include "tsutil/Metrics.h" |
| |
| using ts::Metrics; |
| |
| // tscore |
| #include "tscore/CryptoHash.h" |
| #include "tscore/ink_hrtime.h" |
| |
| #include <array> |
| #include <cstddef> |
| #include <cstdint> |
| #include <map> |
| #include <memory> |
| #include <queue> |
| #include <string> |
| #include <unordered_map> |
| |
| // PreWarm::Dst and PreWarm::SPtrConstDst are defined in iocore. |
| namespace PreWarm |
| { |
| struct DstHash { |
| size_t |
| operator()(const PreWarm::SPtrConstDst &dst) const |
| { |
| CryptoHash hash; |
| CryptoContext context{}; |
| |
| context.update(dst->host.data(), dst->host.size()); |
| context.update(&dst->port, sizeof(in_port_t)); |
| context.update(&dst->type, sizeof(SNIRoutingType)); |
| context.update(&dst->alpn_index, sizeof(int)); |
| |
| context.finalize(hash); |
| |
| return static_cast<size_t>(hash.fold()); |
| } |
| }; |
| |
| struct DstKeyEqual { |
| bool |
| operator()(const PreWarm::SPtrConstDst &x, const PreWarm::SPtrConstDst &y) const |
| { |
| return x->host == y->host && x->port == y->port && x->type == y->type && x->alpn_index == y->alpn_index; |
| } |
| }; |
| |
| struct Conf { |
| Conf(uint32_t min, int32_t max, double rate, ink_hrtime connect_timeout, ink_hrtime inactive_timeout, bool srv_enabled, |
| YamlSNIConfig::Policy verify_server_policy, YamlSNIConfig::Property verify_server_properties, const std::string &sni) |
| : min(min), |
| max(max), |
| rate(rate), |
| connect_timeout(connect_timeout), |
| inactive_timeout(inactive_timeout), |
| srv_enabled(srv_enabled), |
| verify_server_policy(verify_server_policy), |
| verify_server_properties(verify_server_properties), |
| sni(sni) |
| { |
| } |
| |
| uint32_t min = 0; |
| int32_t max = 0; |
| double rate = 1.0; |
| ink_hrtime connect_timeout = 0; |
| ink_hrtime inactive_timeout = 0; |
| bool srv_enabled = false; |
| YamlSNIConfig::Policy verify_server_policy = YamlSNIConfig::Policy::UNSET; |
| YamlSNIConfig::Property verify_server_properties = YamlSNIConfig::Property::UNSET; |
| std::string sni; |
| }; |
| |
| using SPtrConstConf = std::shared_ptr<const Conf>; |
| using ParsedSNIConf = std::unordered_map<SPtrConstDst, SPtrConstConf, DstHash, DstKeyEqual>; |
| |
| enum class CounterStat { |
| HIT = 0, |
| MISS, |
| HANDSHAKE_TIME, |
| HANDSHAKE_COUNT, |
| RETRY, |
| LAST_ENTRY, |
| }; |
| |
| enum class GaugeStat { |
| INIT_LIST_SIZE = 0, |
| OPEN_LIST_SIZE, |
| LAST_ENTRY, |
| }; |
| |
| using CounterIds = std::array<Metrics::Counter::AtomicType *, static_cast<size_t>(PreWarm::CounterStat::LAST_ENTRY)>; |
| using GaugeIds = std::array<Metrics::Gauge::AtomicType *, static_cast<size_t>(PreWarm::GaugeStat::LAST_ENTRY)>; |
| using StatsIds = std::tuple<CounterIds, GaugeIds>; |
| using SPtrConstStatsIds = std::shared_ptr<const StatsIds>; |
| using StatsIdMap = std::unordered_map<SPtrConstDst, SPtrConstStatsIds, DstHash, DstKeyEqual>; |
| } // namespace PreWarm |
| |
| class PreWarmSM; |
| class PreWarmManager; |
| class SNIConfigParams; |
| |
| extern PreWarmManager prewarmManager; |
| |
| /** |
| @class PreWarmSM |
| @brief A state machine to pre-warm connection |
| |
| @startuml |
| hide empty description |
| [*] --> state_init : new |
| state_init --> state_dns_lookup : start() |
| state_init --> state_closed : stop() |
| state_dns_lookup --> state_net_open : HostDB lookup is done |
| state_dns_lookup --> state_init : retry() |
| state_dns_lookup --> state_closed : stop() |
| state_net_open --> state_open : TCP/TLS Handshake is done |
| state_net_open --> state_init : retry() |
| state_net_open --> state_closed : stop() |
| state_open --> state_closed : move_netvc()\nstop() |
| state_closed --> [*] : delete |
| @enduml |
| */ |
| class PreWarmSM : public Continuation |
| { |
| public: |
| PreWarmSM(){}; |
| PreWarmSM(const PreWarm::SPtrConstDst &dst, const PreWarm::SPtrConstConf &conf, const PreWarm::SPtrConstStatsIds &stats_ids); |
| ~PreWarmSM() override; |
| |
| // States |
| int state_init(int event, void *data); |
| int state_dns_lookup(int event, void *data); |
| int state_net_open(int event, void *data); |
| int state_open(int event, void *data); |
| int state_closed(int event, void *data); |
| |
| // Controllers |
| void start(); |
| void retry(); |
| void stop(); |
| void destroy(); |
| |
| // Modifiers |
| NetVConnection *move_netvc(); |
| IOBufferReader *server_buf_reader(); |
| |
| // References |
| bool has_data_from_origin_server() const; |
| |
| // NetTimeout |
| // TODO: constify |
| bool is_active_timeout_expired(ink_hrtime now); |
| bool is_inactive_timeout_expired(ink_hrtime now); |
| |
| // HostDB inline completion functions |
| void process_hostdb_info(HostDBRecord *r); |
| void process_srv_info(HostDBRecord *r); |
| |
| private: |
| enum class Milestone { |
| INIT = 0, |
| DNS_LOOKUP_DONE, |
| ESTABLISHED, |
| CLOSED, |
| LAST_ENTRY, |
| }; |
| |
| Action *_connect(const IpEndpoint &addr); |
| void _reset(); |
| void _record_handshake_time(); |
| |
| //// |
| // Variables |
| // |
| NetTimeout _timeout{}; |
| Milestones<Milestone, static_cast<size_t>(Milestone::LAST_ENTRY)> _milestones; |
| |
| uint32_t _retry_counter = 0; |
| |
| PreWarm::SPtrConstDst _dst; |
| PreWarm::SPtrConstConf _conf; |
| PreWarm::SPtrConstStatsIds _stats_ids; |
| |
| NetVConnection *_netvc = nullptr; |
| Action *_pending_action = nullptr; |
| MIOBuffer *_read_buf = nullptr; |
| IOBufferReader *_read_buf_reader = nullptr; |
| MIOBuffer *_write_buf = nullptr; |
| IOBufferReader *_write_buf_reader = nullptr; |
| Event *_retry_event = nullptr; |
| }; |
| |
| extern ClassAllocator<PreWarmSM, false> preWarmSMAllocator; |
| |
| /** |
| @class PreWarmQueue |
| @detail |
| - Each ET_NET thread has this queue |
| - Responsible for the life cycle of PreWarmSM until giving it to HttpSM |
| |
| @startuml |
| hide empty description |
| [*] --> state_init : new |
| state_init --> state_running : start pre-warming |
| @enduml |
| */ |
| class PreWarmQueue : public Continuation |
| { |
| public: |
| PreWarmQueue(); |
| ~PreWarmQueue() override; |
| |
| // States |
| int state_init(int event, void *data); |
| int state_running(int event, void *data); |
| |
| // Modifiers for queue |
| void push(const PreWarm::SPtrConstDst &dst, PreWarmSM *sm); |
| PreWarmSM *dequeue(const PreWarm::SPtrConstDst &dst); |
| |
| private: |
| using Queue = std::deque<PreWarmSM *>; |
| |
| struct Stat { |
| uint32_t miss = 0; |
| uint32_t hit = 0; |
| }; |
| |
| struct Info { |
| Queue *init_list; |
| Queue *open_list; |
| PreWarm::SPtrConstConf conf; |
| PreWarm::SPtrConstStatsIds stats_ids; |
| Stat stat; |
| }; |
| |
| using Map = std::unordered_map<PreWarm::SPtrConstDst, Info, PreWarm::DstHash, PreWarm::DstKeyEqual>; |
| |
| // construct/destruct PreWarmSM |
| void _new_prewarm_sm(const PreWarm::SPtrConstDst &dst, const PreWarm::SPtrConstConf &conf, |
| const PreWarm::SPtrConstStatsIds &stats_ids); |
| void _delete_prewarm_sm(PreWarmSM *sm); |
| |
| void _reconfigure(); |
| void _make_queue_empty(Queue *q); |
| void _delete_closed_sm(Queue *q); |
| |
| // hooks for pre-warming pool size algorithm |
| void _prewarm_on_event_interval(const PreWarm::SPtrConstDst &dst, const Info &info); |
| void _prewarm_on_dequeue(const PreWarm::SPtrConstDst &dst, const Info &info); |
| |
| //// |
| // Variables |
| // |
| PreWarm::Algorithm _algorithm = PreWarm::Algorithm::V1; |
| |
| Event *_tick_event = nullptr; |
| ink_hrtime _event_period = HRTIME_SECONDS(1); |
| |
| // Force PreWarmSM to open new netvc to keep the connection warm periodically |
| ActivityCop<PreWarmSM> _cop; |
| DLL<PreWarmSM> _cop_list; |
| |
| Map _map; |
| }; |
| |
| /** |
| @class PreWarmManager |
| @details |
| - Global singleton object |
| - Responsible for stats & configs management |
| */ |
| class PreWarmManager |
| { |
| public: |
| PreWarmManager() |
| { |
| // We use the callback because it would introduce a circular dependency |
| // for SNIConfig to explicitly call prewarmManager.reconfigure(). |
| // Because prewarmManager is global there's not a good place to set |
| // this, but as long as there's only one instance of this class |
| // there should not be an issue. |
| SNIConfig::set_on_reconfigure_callback([this]() { this->reconfigure(); }); |
| } |
| |
| static void reconfigure_prewarming_on_threads(); |
| |
| // Controllers |
| void start(); |
| void reconfigure(); |
| void stop(); |
| |
| // References |
| const PreWarm::ParsedSNIConf &get_parsed_conf() const; |
| const PreWarm::StatsIdMap &get_stats_id_map() const; |
| |
| private: |
| void _parse_sni_conf(PreWarm::ParsedSNIConf &parsed_conf, const SNIConfigParams *sni_conf) const; |
| void _register_stats(const PreWarm::ParsedSNIConf &parsed_conf); |
| |
| //// |
| // Variables |
| // |
| // For the race of Main Thread (start up) vs Task Thread (config reload) |
| Ptr<ProxyMutex> _mutex; |
| |
| PreWarm::ParsedSNIConf _parsed_conf; |
| PreWarm::StatsIdMap _stats_id_map; |
| }; |