MXS-1929: Move user reloading to Service

The Service class now handles the reloading of users. This removes the
need to expose the rate limits.
This commit is contained in:
Markus Mäkelä 2018-08-03 00:17:16 +03:00
parent 373fb89dca
commit 3038eb3326
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
2 changed files with 105 additions and 100 deletions

View File

@ -25,7 +25,7 @@
* @file service.h - MaxScale internal service functions
*/
struct UserLoadLimit
struct LastUserLoad
{
time_t last = 0; // The last time the users were loaded
bool warned = false; // Has a warning been logged
@ -36,6 +36,7 @@ class Service: public SERVICE
{
public:
using FilterList = std::vector<SFilterDef>;
using RateLimits = std::vector<LastUserLoad>;
Service(const std::string& name, const std::string& router, MXS_CONFIG_PARAMETER* params);
@ -91,12 +92,18 @@ public:
return !m_filters.empty();
}
/**
* Reload users for all listeners
*
* @return True if loading of users was successful
*/
bool refresh_users();
// TODO: Make JSON output internal (could iterate over get_filters() but that takes the service lock)
json_t* json_relationships(const char* host) const;
// TODO: Make these private
mutable std::mutex lock;
std::vector<UserLoadLimit> rate_limits; /**< The refresh rate limits for users of each thread */
private:
FilterList m_filters; /**< Ordered list of filters */
@ -106,6 +113,7 @@ private:
std::string m_password; /**< Password */
std::string m_weightby; /**< Weighting parameter name */
std::string m_version_string; /**< Version string sent to clients */
RateLimits m_rate_limits; /**< The refresh rate limits for users of each thread */
};
/**

View File

@ -151,14 +151,13 @@ static std::string get_version_string(MXS_CONFIG_PARAMETER* params)
Service::Service(const std::string& service_name, const std::string& router_name,
MXS_CONFIG_PARAMETER* params):
rate_limits(config_threadcount()),
m_name(service_name),
m_router_name(router_name),
m_user(config_get_string(params, CN_USER)),
m_password(config_get_string(params, CN_PASSWORD)),
m_weightby(config_get_string(params, CN_WEIGHTBY)),
m_version_string(get_version_string(params))
m_version_string(get_version_string(params)),
m_rate_limits(config_threadcount())
{
const MXS_MODULE* module = get_module(router_name.c_str(), MODULE_ROUTER);
ss_dassert(module);
@ -196,6 +195,29 @@ Service::Service(const std::string& service_name, const std::string& router_name
log_auth_warnings = config_get_bool(params, CN_LOG_AUTH_WARNINGS);
strip_db_esc = config_get_bool(params, CN_STRIP_DB_ESC);
session_track_trx_state = config_get_bool(params, CN_SESSION_TRACK_TRX_STATE);
/**
* At service start last update is set to config->users_refresh_time seconds earlier.
* This way MaxScale could try reloading users just after startup. But only if user
* refreshing has not been turned off.
*/
MXS_CONFIG* cnf = config_get_global_options();
// Defaults for disabled reloading of users
bool warned = true;
bool last = time(NULL);
if (cnf->users_refresh_time != INT32_MAX)
{
last -= cnf->users_refresh_time;
warned = false;
}
for (auto& a : m_rate_limits)
{
a.last = last;
a.warned = warned;
}
}
Service::~Service()
@ -404,34 +426,6 @@ serviceStartPort(Service *service, SERV_LISTENER *port)
}
}
MXS_CONFIG* config = config_get_global_options();
time_t last;
bool warned;
/**
* At service start last update is set to config->users_refresh_time seconds earlier.
* This way MaxScale could try reloading users just after startup. But only if user
* refreshing has not been turned off.
*/
if (config->users_refresh_time == INT32_MAX)
{
last = time(NULL);
warned = true; // So that there will not be a refresh rate warning.
}
else
{
last = time(NULL) - config->users_refresh_time;
warned = false;
}
int nthreads = config_threadcount();
for (int i = 0; i < nthreads; ++i)
{
service->rate_limits[i].last = last;
service->rate_limits[i].warned = warned;
}
if (port->listener->func.listen(port->listener, config_bind))
{
port->listener->session = session_alloc(service, port->listener);
@ -1327,6 +1321,75 @@ void dListListeners(DCB *dcb)
}
}
bool Service::refresh_users()
{
bool ret = true;
int self = mxs_rworker_get_current_id();
ss_dassert(self >= 0);
time_t now = time(NULL);
// Use unique_lock instead of lock_guard to make the locking conditional
std::unique_lock<std::mutex> guard(lock, std::defer_lock);
if ((capabilities & ACAP_TYPE_ASYNC) == 0)
{
// Use only one rate limitation for synchronous authenticators to keep
// rate limitations synchronous as well
self = 0;
guard.lock();
}
MXS_CONFIG* config = config_get_global_options();
/* Check if refresh rate limit has been exceeded */
if (now < m_rate_limits[self].last + config->users_refresh_time)
{
if (!m_rate_limits[self].warned)
{
MXS_WARNING("[%s] Refresh rate limit (once every %ld seconds) exceeded for "
"load of users' table.",
m_name.c_str(), config->users_refresh_time);
m_rate_limits[self].warned = true;
}
}
else
{
m_rate_limits[self].last = now;
m_rate_limits[self].warned = false;
LISTENER_ITERATOR iter;
for (SERV_LISTENER *listener = listener_iterator_init(this, &iter);
listener; listener = listener_iterator_next(&iter))
{
/** Load the authentication users before before starting the listener */
if (listener_is_active(listener) && listener->listener &&
listener->listener->authfunc.loadusers)
{
switch (listener->listener->authfunc.loadusers(listener))
{
case MXS_AUTH_LOADUSERS_FATAL:
MXS_ERROR("[%s] Fatal error when loading users for listener '%s',"
" authentication will not work.", m_name.c_str(), listener->name);
ret = false;
break;
case MXS_AUTH_LOADUSERS_ERROR:
MXS_WARNING("[%s] Failed to load users for listener '%s', authentication"
" might not work.", m_name.c_str(), listener->name);
ret = false;
break;
default:
break;
}
}
}
}
return ret;
}
/**
* Refresh the database users for the service
* This function replaces the MySQL users used by the service with the latest
@ -1339,73 +1402,7 @@ int service_refresh_users(SERVICE *svc)
{
Service* service = static_cast<Service*>(svc);
ss_dassert(service);
int ret = 1;
int self = mxs_rworker_get_current_id();
ss_dassert(self >= 0);
time_t now = time(NULL);
// Use unique_lock instead of lock_guard to make the locking conditional
std::unique_lock<std::mutex> guard(service->lock, std::defer_lock);
if ((service->capabilities & ACAP_TYPE_ASYNC) == 0)
{
// Use only one rate limitation for synchronous authenticators to keep
// rate limitations synchronous as well
self = 0;
guard.lock();
}
MXS_CONFIG* config = config_get_global_options();
/* Check if refresh rate limit has been exceeded */
if (now < service->rate_limits[self].last + config->users_refresh_time)
{
if (!service->rate_limits[self].warned)
{
MXS_WARNING("[%s] Refresh rate limit (once every %ld seconds) exceeded for "
"load of users' table.",
service->name, config->users_refresh_time);
service->rate_limits[self].warned = true;
}
}
else
{
service->rate_limits[self].last = now;
service->rate_limits[self].warned = false;
ret = 0;
LISTENER_ITERATOR iter;
for (SERV_LISTENER *listener = listener_iterator_init(service, &iter);
listener; listener = listener_iterator_next(&iter))
{
/** Load the authentication users before before starting the listener */
if (listener_is_active(listener) && listener->listener &&
listener->listener->authfunc.loadusers)
{
switch (listener->listener->authfunc.loadusers(listener))
{
case MXS_AUTH_LOADUSERS_FATAL:
MXS_ERROR("[%s] Fatal error when loading users for listener '%s',"
" authentication will not work.", service->name, listener->name);
ret = 1;
break;
case MXS_AUTH_LOADUSERS_ERROR:
MXS_WARNING("[%s] Failed to load users for listener '%s', authentication"
" might not work.", service->name, listener->name);
ret = 1;
break;
default:
break;
}
}
}
}
return ret;
return service->refresh_users() ? 0 : 1;
}
void service_add_parameters(Service *service, const MXS_CONFIG_PARAMETER *param)