MXS-2555 Implement cache eviction (or query re-measurment) strategy
This commit is contained in:
@ -13,44 +13,6 @@
|
||||
|
||||
#include "performance.hh"
|
||||
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <mutex>
|
||||
|
||||
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.
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
/** 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<std::string, PerformanceInfo> 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;
|
||||
}
|
||||
|
@ -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<maxbase::Duration, 4> 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<std::mutex> 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<std::mutex> 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));
|
||||
}
|
||||
}
|
||||
|
@ -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<SmartRouter, SmartRouterSession>
|
||||
{
|
||||
@ -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::string, PerformanceInfo>;
|
||||
|
||||
std::mutex m_perf_mutex;
|
||||
Perfs m_perfs;
|
||||
};
|
||||
|
Reference in New Issue
Block a user