From a950715176afb2fd29d3ef65a6868974c2204ff7 Mon Sep 17 00:00:00 2001 From: Niclas Antti Date: Tue, 18 Jun 2019 17:02:23 +0300 Subject: [PATCH] MXS-2555 Implement cache eviction (or query re-measurment) strategy --- .../routing/smartrouter/performance.cc | 47 -------------- .../routing/smartrouter/performance.hh | 60 ++++++++++-------- .../routing/smartrouter/smartrouter.cc | 62 +++++++++++++++---- .../routing/smartrouter/smartrouter.hh | 13 ++-- 4 files changed, 92 insertions(+), 90 deletions(-) diff --git a/server/modules/routing/smartrouter/performance.cc b/server/modules/routing/smartrouter/performance.cc index 35134e554..99ec0b0bd 100644 --- a/server/modules/routing/smartrouter/performance.cc +++ b/server/modules/routing/smartrouter/performance.cc @@ -13,44 +13,6 @@ #include "performance.hh" -#include -#include -#include - -const std::string file_version = "Alpha"; // if a file has a different version string, discard it. - -CanonicalPerformance::CanonicalPerformance() - : m_nChanges(0) -{ -} - -bool CanonicalPerformance::insert(const std::string& canonical, const PerformanceInfo& perf) -{ - bool saved = m_perfs.insert({canonical, perf}).second; - m_nChanges += saved; - - return saved; -} - -bool CanonicalPerformance::remove(const std::string& canonical) -{ - auto erased = m_perfs.erase(canonical); - m_nChanges += erased; - return erased; -} - -PerformanceInfo CanonicalPerformance::find(const std::string& canonical) -{ - auto it = m_perfs.find(canonical); - return it == m_perfs.end() ? PerformanceInfo() : it->second; -} - -void CanonicalPerformance::clear() -{ - m_perfs.clear(); - m_nChanges = 0; -} - std::string show_some(const std::string& str, int nchars) { int sz = str.length(); @@ -64,7 +26,6 @@ std::string show_some(const std::string& str, int nchars) } } - // These are TODOs for the GA version. The Beta version will not have persistence. // 1. Read the file once at startup. There might also be a need to do cleanup // of the file if the configuration has changed. @@ -78,11 +39,3 @@ std::string show_some(const std::string& str, int nchars) // probably be dropped immediately. // 6. Save all data at shutdown. // Start using xxhash -// -// Expiration rules. At least these rules should be implemented: -// 1. Since the various engines have different setup times during the first few queries, -// this should be taken into account (not implemented). -// 2. Expire entries after X minutes. -// 3. If the measured time is very different from the stored one (+/20%), -// expire the entry (not implemented). -// More rules can be found out by testing. diff --git a/server/modules/routing/smartrouter/performance.hh b/server/modules/routing/smartrouter/performance.hh index 02eace7c4..49a83dc23 100644 --- a/server/modules/routing/smartrouter/performance.hh +++ b/server/modules/routing/smartrouter/performance.hh @@ -19,8 +19,8 @@ #include -/** Class PerformanceInfo is a basic structure for storing a Host and Duration pair, along with - * the time it was created. +/** PerformanceInfo is a class that on the one hand provides routeQuery() with performance/routing + * information and on the other has data for class SmartRouter to manage the life-time of a measurment. */ class PerformanceInfo { @@ -40,38 +40,24 @@ public: /** Duration since this PerformanceInfo was created */ maxbase::Duration age() const; + + /** Managed and used only by class SmartRouter. */ + void set_eviction_schedule(size_t es); + size_t eviction_schedule() const; + + /** Managed and used only by class SmartRouter. */ + void set_updating(bool val); + bool is_updating() const; private: maxbase::Host m_host; maxbase::Duration m_duration; + int m_eviction_schedule = 0; + bool m_updating = false; + maxbase::TimePoint m_creation_time = maxbase::Clock::now(); }; -/** class CanonicalPerformance holds the performance - * info gathered since the start of Maxscale. - * The Beta release will not perist to file. - */ -class CanonicalPerformance -{ -public: - explicit CanonicalPerformance(); - - /** Insert if not already inserted and return true, else false. */ - bool insert(const std::string& canonical, const PerformanceInfo& perf); - - /** Remove if entry exists and return true, else false. */ - bool remove(const std::string& canonical); - - /** If entry does not exists, returned PerformanceInfo::is_valid()==false */ - PerformanceInfo find(const std::string& canonical); - - void clear(); -private: - std::unordered_map m_perfs; - - mutable int m_nChanges; -}; - // For logging. Shortens str to nchars and adds "..." TODO move somewhere more appropriate std::string show_some(const std::string& str, int nchars = 70); @@ -106,3 +92,23 @@ inline maxbase::Duration PerformanceInfo::age() const { return maxbase::Clock::now() - m_creation_time; } + +inline void PerformanceInfo::set_eviction_schedule(size_t es) +{ + m_eviction_schedule = es; +} + +inline size_t PerformanceInfo::eviction_schedule() const +{ + return m_eviction_schedule; +} + +inline void PerformanceInfo::set_updating(bool val) +{ + m_updating = val; +} + +inline bool PerformanceInfo::is_updating() const +{ + return m_updating; +} diff --git a/server/modules/routing/smartrouter/smartrouter.cc b/server/modules/routing/smartrouter/smartrouter.cc index 990225ac2..488866cb7 100644 --- a/server/modules/routing/smartrouter/smartrouter.cc +++ b/server/modules/routing/smartrouter/smartrouter.cc @@ -197,29 +197,69 @@ uint64_t SmartRouter::getCapabilities() return RCAP_TYPE_TRANSACTION_TRACKING | RCAP_TYPE_CONTIGUOUS_INPUT | RCAP_TYPE_CONTIGUOUS_OUTPUT; } +// Eviction schedule +// Two reasons to evict, and re-measure canonicals. +// 1. When connections are initially created there is more overhead in maxscale and at the server, +// which can (and does) lead to the wrong performance conclusions. +// 2. Depending on the contents and number of rows in tables, different database engines +// have different performance advantages (InnoDb is always very fast for small tables). +// +// TODO make configurable, maybe. +static std::array eviction_schedules = +{ + std::chrono::minutes(2), + std::chrono::minutes(5), + std::chrono::minutes(10), + std::chrono::minutes(20) +}; + +// TODO need to add the default db to the key (or hash) + PerformanceInfo SmartRouter::perf_find(const std::string& canonical) { std::unique_lock guard(m_perf_mutex); - auto perf = m_performance.find(canonical); - if (perf.is_valid() && perf.age() > std::chrono::minutes(1)) // TODO to config, but not yet + auto perf_it = m_perfs.find(canonical); + if (perf_it != end(m_perfs) && !perf_it->second.is_updating()) { - m_performance.remove(canonical); - return PerformanceInfo(); + if (perf_it->second.age() > eviction_schedules[perf_it->second.eviction_schedule()]) + { + MXS_SINFO("Trigger re-measure, schedule " + << eviction_schedules[perf_it->second.eviction_schedule()] + << ", perf: " << perf_it->second.host() + << ", " << perf_it->second.duration() << ", " + << show_some(canonical)); + + // Not actually evicting, but trigger a re-measure only for this caller (this worker). + perf_it->second.set_updating(true); + perf_it = end(m_perfs); + } } - return perf; + return perf_it != end(m_perfs) ? perf_it->second : PerformanceInfo(); } -bool SmartRouter::perf_update(const std::string& canonical, const PerformanceInfo& perf) +void SmartRouter::perf_update(const std::string& canonical, const PerformanceInfo& perf) { std::unique_lock guard(m_perf_mutex); - auto ret = m_performance.insert(canonical, perf); - if (ret) + auto perf_it = m_perfs.find(canonical); + if (perf_it != end(m_perfs)) { - MXS_SDEBUG("Stored perf " << perf.duration() << ' ' << perf.host() << ' ' << show_some(canonical)); - } + MXS_SINFO("Update perf: from " + << perf_it->second.host() << ", " << perf_it->second.duration() + << " to " << perf.host() << ", " << perf.duration() + << ", " << show_some(canonical)); - return ret; + size_t schedule = perf_it->second.eviction_schedule(); + perf_it->second = perf; + perf_it->second.set_eviction_schedule(std::min(++schedule, eviction_schedules.size() - 1)); + perf_it->second.set_updating(false); + } + else + { + m_perfs.insert({canonical, perf}); + MXS_SDEBUG("Stored new perf: " << perf.host() << ", " << perf.duration() + << ", " << show_some(canonical)); + } } diff --git a/server/modules/routing/smartrouter/smartrouter.hh b/server/modules/routing/smartrouter/smartrouter.hh index 9bbdacbd7..e6453ff55 100644 --- a/server/modules/routing/smartrouter/smartrouter.hh +++ b/server/modules/routing/smartrouter/smartrouter.hh @@ -26,7 +26,7 @@ class SmartRouterSession; -/** class Smartrouter. Only defines the mxs::Router<> functions needed for all routers. +/** class Smartrouter. Contains and manages the performance info. */ class SmartRouter : public mxs::Router { @@ -83,12 +83,15 @@ public: /** Thread safe update/insert a PerformanceInfo. Some entry expiration handling is done here. */ - bool perf_update(const std::string& canonical, const PerformanceInfo& perf); + void perf_update(const std::string& canonical, const PerformanceInfo& perf); private: SmartRouter(SERVICE* service); - Config m_config; - CanonicalPerformance m_performance; - std::mutex m_perf_mutex; + Config m_config; + + using Perfs = std::unordered_map; + + std::mutex m_perf_mutex; + Perfs m_perfs; };