blob: bea1389885ab736b00e1540f4dcadde78d512c8b [file] [log] [blame]
/** @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 "PreWarmAlgorithm.h"
#include "I_EventSystem.h"
#include "I_NetVConnection.h"
#include "I_HostDB.h"
#include "YamlSNIConfig.h"
#include "NetTimeout.h"
#include "Milestones.h"
#include "records/I_RecHttp.h"
#include "records/DynamicStats.h"
#include <map>
#include <unordered_map>
#include <memory>
#include <queue>
#include <string_view>
class PreWarmSM;
class PreWarmManager;
struct SNIConfigParams;
extern ClassAllocator<PreWarmSM> preWarmSMAllocator;
extern PreWarmManager prewarmManager;
namespace PreWarm
{
////
// Dst
//
struct Dst {
Dst(const std::string &h, in_port_t p, SNIRoutingType t, int a) : host(h), port(p), type(t), alpn_index(a) {}
std::string host;
in_port_t port = 0;
SNIRoutingType type = SNIRoutingType::NONE;
int alpn_index = SessionProtocolNameRegistry::INVALID;
};
using SPtrConstDst = std::shared_ptr<const Dst>;
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;
}
};
////
// Conf
//
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>;
////
// Stats
//
enum class Stat {
INIT_LIST_SIZE = 0,
OPEN_LIST_SIZE,
HIT,
MISS,
HANDSHAKE_TIME,
HANDSHAKE_COUNT,
RETRY,
LAST_ENTRY,
};
using StatsIds = std::array<int, static_cast<size_t>(PreWarm::Stat::LAST_ENTRY)>;
using SPtrConstStatsIds = std::shared_ptr<const StatsIds>;
using StatsIdMap = std::unordered_map<SPtrConstDst, SPtrConstStatsIds, DstHash, DstKeyEqual>;
} // namespace PreWarm
/**
@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;
};
/**
@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();
// 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:
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;
////
// Variables
//
DynamicStats stats;
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;
};