MXS-1883 Maintenance is now the only user-modifiable bit for a monitored server

The request to turn maintenance off/on is a separate flag, although the actual
status is still contained in the status bitfield.
This commit is contained in:
Esa Korhonen
2018-05-24 13:47:56 +03:00
parent 2f48d079db
commit c039821467
11 changed files with 182 additions and 40 deletions

View File

@ -182,3 +182,19 @@ bool atomic_cas_ptr(void **variable, void** old_value, void *new_value)
return __sync_bool_compare_and_swap(variable, *old_value, new_value);
#endif
}
int atomic_exchange_int(int *variable, int new_value)
{
#ifdef MXS_USE_ATOMIC_BUILTINS
return __atomic_exchange_n(variable, new_value, __ATOMIC_SEQ_CST);
#else
/* The __sync-functions have a questionable exchange-operation (__sync_lock_test_and_set) which
* doesn't always write the requested value. Using compare-and-swap instead. */
int old_val = *variable;
while (!__sync_bool_compare_and_swap(variable, old_val, new_value))
{
old_val = *variable;
}
return old_val;
#endif
}

View File

@ -1737,9 +1737,9 @@ void release_monitor_servers(MXS_MONITOR *monitor)
}
}
/**
* Sets the current status of all servers monitored by this monitor to
* the pending status. This should only be called at the beginning of
* a monitor loop, after the servers are locked.
* Check if admin is requesting setting or clearing maintenance status on the server and act accordingly.
* Should be called at the beginning of a monitor loop.
*
* @param monitor The target monitor
*/
void servers_status_pending_to_current(MXS_MONITOR *monitor)
@ -1747,11 +1747,22 @@ void servers_status_pending_to_current(MXS_MONITOR *monitor)
MXS_MONITORED_SERVER *ptr = monitor->monitored_servers;
while (ptr)
{
ptr->server->status = ptr->server->status_pending;
// The only server status bit the admin may change is the [Maintenance] bit.
int admin_msg = atomic_exchange_int(&ptr->server->maint_request, MAINTENANCE_NO_CHANGE);
if (admin_msg == MAINTENANCE_ON)
{
// TODO: Change to writing MONITORED_SERVER->pending status instead once cleanup done.
server_set_status_nolock(ptr->server, SERVER_MAINT);
}
else if (admin_msg == MAINTENANCE_OFF)
{
server_clear_status_nolock(ptr->server, SERVER_MAINT);
}
ptr = ptr->next;
}
monitor->server_pending_changes = false;
}
/**
* Sets the pending status of all servers monitored by this monitor to
* the current status. This should only be called at the end of
@ -1763,7 +1774,6 @@ void servers_status_current_to_pending(MXS_MONITOR *monitor)
MXS_MONITORED_SERVER *ptr = monitor->monitored_servers;
while (ptr)
{
ptr->server->status_pending = ptr->server->status;
ptr = ptr->next;
}
}
@ -2165,7 +2175,6 @@ static const char* process_server(MXS_MONITOR *monitor, const char *data, const
uint64_t status = maxscale::get_byteN(sptr, MMB_LEN_SERVER_STATUS);
db->mon_prev_status = status;
db->server->status_pending = status;
server_set_status_nolock(db->server, status);
monitor_set_pending_status(db, status);
break;

View File

@ -25,6 +25,7 @@
#include <maxscale/adminusers.h>
#include <maxscale/modulecmd.h>
#include <maxscale/semaphore.hh>
#include <maxscale/server.hh>
#include "internal/httprequest.hh"
#include "internal/httpresponse.hh"
@ -658,8 +659,15 @@ HttpResponse cb_set_server(const HttpRequest& request)
if (opt)
{
server_set_status(server, opt);
return HttpResponse(MHD_HTTP_NO_CONTENT);
string errmsg;
if (mxs::server_set_status(server, opt, &errmsg))
{
return HttpResponse(MHD_HTTP_NO_CONTENT);
}
else
{
return HttpResponse(MHD_HTTP_FORBIDDEN, mxs_json_error(errmsg.c_str()));
}
}
return HttpResponse(MHD_HTTP_FORBIDDEN,
@ -674,8 +682,15 @@ HttpResponse cb_clear_server(const HttpRequest& request)
if (opt)
{
server_clear_status(server, opt);
return HttpResponse(MHD_HTTP_NO_CONTENT);
string errmsg;
if (mxs::server_clear_status(server, opt, &errmsg))
{
return HttpResponse(MHD_HTTP_NO_CONTENT);
}
else
{
return HttpResponse(MHD_HTTP_FORBIDDEN, mxs_json_error(errmsg.c_str()));
}
}
return HttpResponse(MHD_HTTP_FORBIDDEN,

View File

@ -26,7 +26,6 @@
#include <maxscale/config.h>
#include <maxscale/service.h>
#include <maxscale/session.h>
#include <maxscale/server.h>
#include <maxscale/spinlock.h>
#include <maxscale/dcb.h>
#include <maxscale/poll.h>
@ -40,11 +39,13 @@
#include <maxscale/clock.h>
#include <maxscale/http.hh>
#include <maxscale/maxscale.h>
#include <maxscale/server.hh>
#include "internal/monitor.h"
#include "internal/poll.h"
#include "internal/routingworker.hh"
using maxscale::Semaphore;
using maxscale::Worker;
using maxscale::WorkerTask;
@ -62,7 +63,10 @@ const char CN_PROXY_PROTOCOL[] = "proxy_protocol";
static SPINLOCK server_spin = SPINLOCK_INIT;
static SERVER *allServers = NULL;
static const char ERR_CANNOT_MODIFY[] = "The server is monitored, so only the maintenance status can be "
"set/cleared manually. Status was not modified.";
static const char WRN_REQUEST_OVERWRITTEN[] = "Previous maintenance request was not yet read by the monitor "
"and was overwritten.";
static void spin_reporter(void *, char *, int);
static void server_parameter_free(SERVER_PARAM *tofree);
@ -119,7 +123,7 @@ SERVER* server_alloc(const char *name, const char *address, unsigned short port,
server->auth_instance = auth_instance;
server->port = port;
server->status = SERVER_RUNNING;
server->status_pending = SERVER_RUNNING;
server->maint_request = MAINTENANCE_NO_CHANGE;
server->node_id = -1;
server->rlag = MAX_RLAG_UNDEFINED;
server->master_id = -1;
@ -1306,28 +1310,47 @@ SERVER* server_repurpose_destroyed(const char *name, const char *protocol, const
* @param server The server to update
* @param bit The bit to set for the server
*/
void server_set_status(SERVER *server, int bit)
bool mxs::server_set_status(SERVER *server, int bit, string* errmsg_out)
{
bool written = false;
/* First check if the server is monitored. This isn't done under a lock
* but the race condition cannot cause significant harm. Monitors are never
* freed so the pointer stays valid.
*/
* freed so the pointer stays valid. */
MXS_MONITOR *mon = monitor_server_in_use(server);
spinlock_acquire(&server->lock);
if (mon && mon->state == MONITOR_STATE_RUNNING)
{
/* Set a pending status bit. It will be activated on the next monitor
* loop. Also set a flag so the next loop happens sooner.
*/
server->status_pending |= bit;
mon->server_pending_changes = true;
/* This server is monitored, in which case modifying any other status bit than Maintenance is
* disallowed. Maintenance is set/cleared using a special variable which the monitor reads when
* starting the next update cycle. Also set a flag so the next loop happens sooner. */
if (bit & ~SERVER_MAINT)
{
MXS_ERROR(ERR_CANNOT_MODIFY);
if (errmsg_out)
{
*errmsg_out = ERR_CANNOT_MODIFY;
}
}
else if (bit & SERVER_MAINT)
{
// Warn if the previous request hasn't been read.
int previous_request = atomic_exchange_int(&server->maint_request, MAINTENANCE_ON);
written = true;
if (previous_request != MAINTENANCE_NO_CHANGE)
{
MXS_WARNING(WRN_REQUEST_OVERWRITTEN);
}
mon->server_pending_changes = true;
}
}
else
{
/* Set the bit directly */
server_set_status_nolock(server, bit);
written = true;
}
spinlock_release(&server->lock);
return written;
}
/**
* Clear a status bit in the server under a lock. This ensures synchronization
@ -1337,24 +1360,42 @@ void server_set_status(SERVER *server, int bit)
* @param server The server to update
* @param bit The bit to clear for the server
*/
void server_clear_status(SERVER *server, int bit)
bool mxs::server_clear_status(SERVER *server, int bit, string* errmsg_out)
{
bool written = false;
MXS_MONITOR *mon = monitor_server_in_use(server);
spinlock_acquire(&server->lock);
if (mon && mon->state == MONITOR_STATE_RUNNING)
{
/* Clear a pending status bit. It will be activated on the next monitor
* loop. Also set a flag so the next loop happens sooner.
*/
server->status_pending &= ~bit;
mon->server_pending_changes = true;
// See server_set_status().
if (bit & ~SERVER_MAINT)
{
MXS_ERROR(ERR_CANNOT_MODIFY);
if (errmsg_out)
{
*errmsg_out = ERR_CANNOT_MODIFY;
}
}
else if (bit & SERVER_MAINT)
{
// Warn if the previous request hasn't been read.
int previous_request = atomic_exchange_int(&server->maint_request, MAINTENANCE_OFF);
written = true;
if (previous_request != MAINTENANCE_NO_CHANGE)
{
MXS_WARNING(WRN_REQUEST_OVERWRITTEN);
}
mon->server_pending_changes = true;
}
}
else
{
/* Clear bit directly */
server_clear_status_nolock(server, bit);
written = true;
}
spinlock_release(&server->lock);
return written;
}
bool server_is_mxs_service(const SERVER *server)