mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
5.6 KiB
168 lines
5.6 KiB
#ifndef __CLASS_DNS_CACHE_H
|
|
#define __CLASS_DNS_CACHE_H
|
|
|
|
// Shared DNS cache infrastructure used by both MySQL_Monitor and PgSQL_Monitor.
|
|
// The cache itself is just a hostname -> [IP] map with TTL; the resolver thread
|
|
// does getaddrinfo and feeds the cache. Each monitor instantiates its own
|
|
// DNS_Cache and supplies its own counters, so the two protocols share code but
|
|
// have independent cache state.
|
|
|
|
#include <pthread.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include <algorithm>
|
|
#include <atomic>
|
|
#include <cassert>
|
|
#include <future>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
#include "thread.h"
|
|
#include "wqueue.h"
|
|
|
|
struct DNS_Cache_Record {
|
|
DNS_Cache_Record() = default;
|
|
DNS_Cache_Record(DNS_Cache_Record&&) = default;
|
|
DNS_Cache_Record(const DNS_Cache_Record&) = default;
|
|
DNS_Cache_Record& operator=(DNS_Cache_Record&&) = default;
|
|
DNS_Cache_Record& operator=(const DNS_Cache_Record&) = default;
|
|
DNS_Cache_Record(const std::string& hostname, const std::vector<std::string>& ips, unsigned long long ttl = 0)
|
|
: hostname_(hostname), ttl_(ttl) {
|
|
std::copy(ips.begin(), ips.end(), std::inserter(ips_, ips_.end()));
|
|
}
|
|
DNS_Cache_Record(const std::string& hostname, std::set<std::string>&& ips, unsigned long long ttl = 0)
|
|
: hostname_(hostname), ips_(std::move(ips)), ttl_(ttl) { }
|
|
|
|
~DNS_Cache_Record() = default;
|
|
|
|
std::string hostname_;
|
|
std::set<std::string> ips_;
|
|
unsigned long long ttl_ = 0;
|
|
};
|
|
|
|
class DNS_Cache {
|
|
|
|
public:
|
|
DNS_Cache() : enabled(true) {
|
|
int rc = pthread_rwlock_init(&rwlock_, nullptr);
|
|
assert(rc == 0);
|
|
}
|
|
|
|
~DNS_Cache() {
|
|
pthread_rwlock_destroy(&rwlock_);
|
|
}
|
|
|
|
inline
|
|
void set_enabled_flag(bool value) {
|
|
enabled = value;
|
|
}
|
|
|
|
// Wire the cache up to per-instance counters maintained by the owning
|
|
// monitor. Pass nullptr to leave any counter unincremented; this is what
|
|
// keeps MySQL_Monitor and PgSQL_Monitor cache state independent. The
|
|
// counters are atomic because resolver workers increment them while
|
|
// p_update_metrics() reads them from another thread.
|
|
inline
|
|
void set_counters(std::atomic<unsigned long long>* queried,
|
|
std::atomic<unsigned long long>* lookup_success,
|
|
std::atomic<unsigned long long>* record_updated) {
|
|
counter_queried_ = queried;
|
|
counter_lookup_success_ = lookup_success;
|
|
counter_record_updated_ = record_updated;
|
|
}
|
|
|
|
bool add(const std::string& hostname, std::vector<std::string>&& ips);
|
|
bool add_if_not_exist(const std::string& hostname, std::vector<std::string>&& ips);
|
|
void remove(const std::string& hostname);
|
|
void clear();
|
|
bool empty() const;
|
|
std::string lookup(const std::string& hostname, size_t* ip_count) const;
|
|
|
|
private:
|
|
struct IP_ADDR {
|
|
std::vector<std::string> ips;
|
|
// 'counter' is bumped by get_next_ip() (a const method) for
|
|
// round-robin selection; the logical state of the cache record is
|
|
// unchanged, so mutable is the right tool here and lets us drop a
|
|
// const_cast at the call site.
|
|
mutable unsigned long counter = 0;
|
|
};
|
|
|
|
std::string get_next_ip(const IP_ADDR& ip_addr) const;
|
|
std::unordered_map<std::string, IP_ADDR> records;
|
|
std::atomic_bool enabled;
|
|
mutable pthread_rwlock_t rwlock_;
|
|
|
|
std::atomic<unsigned long long>* counter_queried_ { nullptr };
|
|
std::atomic<unsigned long long>* counter_lookup_success_ { nullptr };
|
|
std::atomic<unsigned long long>* counter_record_updated_ { nullptr };
|
|
};
|
|
|
|
struct DNS_Resolve_Data {
|
|
std::promise<std::tuple<bool, DNS_Cache_Record>> result;
|
|
std::shared_ptr<DNS_Cache> dns_cache;
|
|
std::string hostname;
|
|
std::set<std::string> cached_ips;
|
|
unsigned int ttl = 0;
|
|
unsigned int refresh_intv = 0;
|
|
// Address family for getaddrinfo, set by the caller from its protocol's
|
|
// resolution_family setting. AF_UNSPEC keeps the OS default behavior.
|
|
int ai_family = AF_UNSPEC;
|
|
};
|
|
|
|
// Worker function for the DNS resolver thread pool. Performs getaddrinfo
|
|
// on dns_resolve_data->hostname, populates the cache, and fulfils the
|
|
// future on dns_resolve_data->result.
|
|
void* monitor_dns_resolver_thread(const std::vector<DNS_Resolve_Data*>& dns_resolve_data_list);
|
|
|
|
// Minimal worker thread that drains a wqueue<DNS_Resolve_Data*> and calls
|
|
// monitor_dns_resolver_thread() on each item. Independent of any monitor
|
|
// singleton so it can back PgSQL_Monitor's DNS cache loop alongside (or
|
|
// instead of) MySQL_Monitor's existing ConsumerThread-based pool.
|
|
//
|
|
// Each item ownership is transferred to this worker: the worker frees it
|
|
// after running monitor_dns_resolver_thread. A NULL dequeue is the exit
|
|
// signal — the loop terminates cleanly so callers can shut the pool down by
|
|
// pushing one NULL per worker.
|
|
class DNSResolverWorker : public Thread {
|
|
public:
|
|
DNSResolverWorker(wqueue<DNS_Resolve_Data*>& q, const char* name = nullptr)
|
|
: queue_(q) {
|
|
if (name && name[0])
|
|
snprintf(thr_name_, sizeof(thr_name_), "%.15s", name);
|
|
else
|
|
snprintf(thr_name_, sizeof(thr_name_), "DNSResolver");
|
|
}
|
|
|
|
void* run() override;
|
|
|
|
private:
|
|
wqueue<DNS_Resolve_Data*>& queue_;
|
|
char thr_name_[16] {};
|
|
};
|
|
|
|
// Return true if 'ip' is a valid IPv4 or IPv6 address string.
|
|
bool validate_ip(const std::string& ip);
|
|
|
|
// Return the IP address of the peer connected to 'socket_fd', or "" on
|
|
// failure / non-IP families.
|
|
std::string get_connected_peer_ip_from_socket(int socket_fd);
|
|
|
|
// Helper: stringify a list of IPs for debug logging. Defined inline because
|
|
// it's templated over the iterable type used by the various call sites.
|
|
template<class T>
|
|
std::string debug_iplisttostring(const T& ips) {
|
|
std::stringstream sstr;
|
|
for (const std::string& ip : ips)
|
|
sstr << ip << " ";
|
|
return sstr.str();
|
|
}
|
|
|
|
#endif // __CLASS_DNS_CACHE_H
|