Files
MaxScale/server/modules/routing/schemarouter/shard_map.cc
Markus Mäkelä 41c2a6ee8e MXS-3892: Limit concurrent mapping of databases
As there are no practical benefits to multiple sessions for the same user
mapping the databases at the same time, limiting them to one update per
user is sensible. This is especially true now that we know the
information_schema tables aren't the most efficient things in the world.

The current code implements this rate limiting by closing any extra
sessions that would start a second update. The final implementation should
suspend them for the duration of the update as it is far more
user-friendly.

The limits are currently global as the shard caches are also global. This
is a performance bottleneck and it could be solved by storing the shard
cache inside of a mxs::WorkerGlobal instead of having it as a global
cache.
2021-12-31 15:25:18 +02:00

243 lines
5.3 KiB
C++

/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2025-10-29
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include "shard_map.hh"
#include <algorithm>
#include <maxbase/alloc.h>
Shard::Shard()
: m_last_updated(time(NULL))
{
}
Shard::~Shard()
{
}
bool Shard::add_location(std::string db, SERVER* target)
{
return m_map.insert(std::make_pair(db, target)).second;
}
void Shard::add_statement(std::string stmt, SERVER* target)
{
stmt_map[stmt] = target;
}
void Shard::add_statement(uint32_t id, SERVER* target)
{
MXS_DEBUG("ADDING ID: [%u] server: [%s]", id, target->name());
m_binary_map[id] = target;
}
void Shard::add_ps_handle(uint32_t id, uint32_t handle)
{
MXS_DEBUG("ID: [%u] HANDLE: [%u]", id, handle);
m_ps_handles[id] = handle;
}
bool Shard::remove_ps_handle(uint32_t id)
{
return m_ps_handles.erase(id);
}
uint32_t Shard::get_ps_handle(uint32_t id)
{
PSHandleMap::iterator it = m_ps_handles.find(id);
if (it != m_ps_handles.end())
{
return it->second;
}
return 0;
}
void Shard::replace_location(std::string db, SERVER* target)
{
m_map[db] = target;
}
SERVER* Shard::get_location(std::string table)
{
SERVER* rval = NULL;
if (table.find(".") == std::string::npos)
{
for (ServerMap::iterator it = m_map.begin(); it != m_map.end(); it++)
{
std::transform(table.begin(), table.end(), table.begin(), ::tolower);
std::string db = it->first.substr(0, it->first.find("."));
std::transform(db.begin(), db.end(), db.begin(), ::tolower);
if (db.compare(table) == 0)
{
if ((rval && rval != it->second))
{
MXS_DEBUG("There are 2 databases with same name on a different servers: '%s' and '%s'. "
"Connecting to '%s'",
rval->name(),
it->second->name(),
rval->name());
break;
}
else
{
rval = it->second;
}
}
}
}
else
{
for (ServerMap::iterator it = m_map.begin(); it != m_map.end(); it++)
{
std::transform(table.begin(), table.end(), table.begin(), ::tolower);
std::string db = it->first;
std::transform(db.begin(), db.end(), db.begin(), ::tolower);
if (db.compare(table) == 0)
{
rval = it->second;
break;
}
}
}
return rval;
}
SERVER* Shard::get_statement(std::string stmt)
{
SERVER* rval = NULL;
ServerMap::iterator iter = stmt_map.find(stmt);
if (iter != stmt_map.end())
{
rval = iter->second;
}
return rval;
}
SERVER* Shard::get_statement(uint32_t id)
{
SERVER* rval = NULL;
BinaryPSMap::iterator iter = m_binary_map.find(id);
if (iter != m_binary_map.end())
{
rval = iter->second;
}
return rval;
}
bool Shard::remove_statement(std::string stmt)
{
return stmt_map.erase(stmt);
}
bool Shard::remove_statement(uint32_t id)
{
return m_binary_map.erase(id);
}
bool Shard::stale(double max_interval) const
{
time_t now = time(NULL);
return difftime(now, m_last_updated) > max_interval;
}
bool Shard::empty() const
{
return m_map.size() == 0;
}
void Shard::get_content(ServerMap& dest)
{
for (ServerMap::iterator it = m_map.begin(); it != m_map.end(); it++)
{
dest.insert(*it);
}
}
bool Shard::newer_than(const Shard& shard) const
{
return m_last_updated > shard.m_last_updated;
}
ShardManager::ShardManager()
{
}
ShardManager::~ShardManager()
{
}
Shard ShardManager::get_shard(std::string user, double max_interval)
{
std::lock_guard<std::mutex> guard(m_lock);
ShardMap::iterator iter = m_maps.find(user);
if (iter == m_maps.end() || iter->second.stale(max_interval))
{
// No previous shard or a stale shard, construct a new one
if (iter != m_maps.end())
{
m_maps.erase(iter);
}
return Shard();
}
// Found valid shard
return iter->second;
}
void ShardManager::update_shard(Shard& shard, std::string user)
{
std::lock_guard<std::mutex> guard(m_lock);
ShardMap::iterator iter = m_maps.find(user);
if (iter == m_maps.end() || shard.newer_than(iter->second))
{
m_maps[user] = shard;
}
mxb_assert(m_limits[user] > 0);
--m_limits[user];
}
void ShardManager::set_update_limit(int64_t limit)
{
std::lock_guard<std::mutex> guard(m_lock);
m_update_limit = limit;
}
bool ShardManager::start_update(const std::string& user)
{
bool rval = false;
std::lock_guard<std::mutex> guard(m_lock);
if (m_limits[user] < m_update_limit)
{
++m_limits[user];
rval = true;
}
return rval;
}
void ShardManager::cancel_update(const std::string& user)
{
std::lock_guard<std::mutex> guard(m_lock);
mxb_assert(m_limits[user] > 0);
--m_limits[user];
}