Files
MaxScale/server/modules/routing/schemarouter/shard_map.hh
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

180 lines
4.8 KiB
C++

/*
* Copyright (c) 2018 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.
*/
#pragma once
#include <maxscale/ccdefs.hh>
#include <list>
#include <mutex>
#include <string>
#include <unordered_map>
#include <maxscale/service.hh>
using namespace maxscale;
/** This contains the database to server mapping */
typedef std::unordered_map<std::string, SERVER*> ServerMap;
typedef std::unordered_map<uint64_t, SERVER*> BinaryPSMap;
typedef std::unordered_map<uint32_t, uint32_t> PSHandleMap;
class Shard
{
public:
Shard();
~Shard();
/**
* @brief Add a database location
*
* @param db Database to add
* @param target Target where database is located
*
* @return True if location was added
*/
bool add_location(std::string db, SERVER* target);
/**
* @brief Retrieve the location of a database
*
* @param db Database to locate
*
* @return The database or NULL if no server contains the database
*/
SERVER* get_location(std::string db);
void add_statement(std::string stmt, SERVER* target);
void add_statement(uint32_t id, SERVER* target);
void add_ps_handle(uint32_t id, uint32_t handle);
uint32_t get_ps_handle(uint32_t id);
bool remove_ps_handle(uint32_t id);
SERVER* get_statement(std::string stmt);
SERVER* get_statement(uint32_t id);
bool remove_statement(std::string stmt);
bool remove_statement(uint32_t id);
/**
* @brief Change the location of a database
*
* @param db Database to relocate
* @param target Target where database is relocated to
*/
void replace_location(std::string db, SERVER* target);
/**
* @brief Check if shard contains stale information
*
* @param max_interval The maximum lifetime of the shard
*
* @return True if the shard is stale
*/
bool stale(double max_interval) const;
/**
* @brief Check if shard is empty
*
* @return True if shard contains no locations
*/
bool empty() const;
/**
* @brief Retrieve all database to server mappings
*
* @param keys A map where the database to server mappings are added
*/
void get_content(ServerMap& dest);
/**
* @brief Check if this shard is newer than the other shard
*
* @param shard The other shard to check
*
* @return True if this shard is newer
*/
bool newer_than(const Shard& shard) const;
private:
ServerMap m_map;
ServerMap stmt_map;
BinaryPSMap m_binary_map;
PSHandleMap m_ps_handles;
time_t m_last_updated;
};
typedef std::unordered_map<std::string, Shard> ShardMap;
typedef std::unordered_map<std::string, int64_t> MapLimits;
class ShardManager
{
public:
ShardManager();
~ShardManager();
/**
* @brief Retrieve or create a shard
*
* @param user User whose shard to retrieve
* @param max_lifetime The maximum lifetime of a shard
*
* @return The latest version of the shard or a newly created shard if no
* old version is available
*/
Shard get_shard(std::string user, double max_lifetime);
/**
* @brief Update the shard information
*
* The shard information is updated if the new shard contains more up to date
* information than the one stored in the shard manager.
*
* @param shard New version of the shard
* @param user The user whose shard this is
*/
void update_shard(Shard& shard, std::string user);
/**
* Set how many concurrent shard updates are allowed per user
*
* By default only one update per user is allowed.
*
* @param limit Number of concurrent users to allow
*/
void set_update_limit(int64_t limit);
/**
* Start a shard update
*
* The update is considered finished when either update_shard() or cancel_update() is called. One of these
* two must be called by the session once start_update() has returned true.
*
* @param user The user whose shard is about to be updated
*
* @return True if an update can be done. False if there are too many concurrent
* updates being done by this user.
*/
bool start_update(const std::string& user);
/**
* Cancels a started shard update
*
* @param user The user whose shard was being updated
*/
void cancel_update(const std::string& user);
private:
mutable std::mutex m_lock;
ShardMap m_maps;
MapLimits m_limits;
int64_t m_update_limit {1};
};