MXS-1929: Make services destroyable

Services can now be destroyed if they have no active listeners and they
are not linked to servers. When these conditions are met, the service will
be destroyed when the last session for the service is closed.

The closing of a service will close all listeners that were once assigned
to the service. This allows closing of the ports at runtime which
previously was done only on shutdown.

Exposed the command through the REST API but not through MaxAdmin as it is
deprecated.
This commit is contained in:
Markus Mäkelä 2018-07-18 10:03:54 +03:00
parent 18c1ec2678
commit 5a40064826
No known key found for this signature in database
GPG Key ID: 72D48FCE664F7B19
7 changed files with 163 additions and 4 deletions

View File

@ -157,6 +157,7 @@ typedef struct service
uint64_t capabilities; /**< The capabilities of the service, @see enum routing_capability */
int max_retry_interval; /**< Maximum retry interval */
bool session_track_trx_state; /**< Get transaction state via session track mechanism */
bool active; /**< Whether the service is still active */
} SERVICE;
typedef enum count_spec_t

View File

@ -979,8 +979,8 @@ bool runtime_destroy_listener(SERVICE *service, const char *name)
{
rval = true;
MXS_NOTICE("Destroyed listener '%s' for service '%s'. The listener "
"will be removed after the next restart of MaxScale.",
name, service->name);
"will be removed after the next restart of MaxScale or "
"when the associated service is destroyed.", name, service->name);
}
return rval;
@ -1078,6 +1078,26 @@ bool runtime_create_filter(const char *name, const char *module, MXS_CONFIG_PARA
return rval;
}
bool runtime_destroy_service(SERVICE* service)
{
bool rval = false;
mxs::SpinLockGuard guard(crt_lock);
ss_dassert(service && service->active);
if (service_can_be_destroyed(service))
{
service_destroy(service);
rval = true;
}
else
{
runtime_error("Service '%s' cannot be destroyed: Remove all servers and "
"destroy all listeners first", service->name);
}
return rval;
}
bool runtime_destroy_monitor(MXS_MONITOR *monitor)
{
bool rval = false;

View File

@ -216,6 +216,17 @@ bool runtime_create_filter(const char *name, const char *module, MXS_CONFIG_PARA
*/
bool runtime_destroy_monitor(MXS_MONITOR *monitor);
/**
* Destroy a service
*
* The service can only be destroyed if it uses no servers and has no active listeners.
*
* @param service Service to destroy
*
* @return True if service was destroyed
*/
bool runtime_destroy_service(SERVICE* service);
/**
* @brief Create a new server from JSON
*

View File

@ -40,10 +40,31 @@ SERVICE* service_alloc(const char *name, const char *router, MXS_CONFIG_PARAMETE
/**
* Free a service
*
* @note Must not be called if the service has any active client connections or
* active listeners
*
* @param service Service to free
*/
void service_free(SERVICE* service);
/**
* Mark a service for destruction
*
* Once the service reference count drops down to zero, the service is destroyed.
*
* @param service Service to destroy
*/
void service_destroy(SERVICE *service);
/**
* Check whether a service can be destroyed
*
* @param service Service to check
*
* @return True if service can be destroyed
*/
bool service_can_be_destroyed(SERVICE *service);
/**
* @brief Shut all services down
*

View File

@ -459,6 +459,19 @@ HttpResponse cb_delete_listener(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT);
}
HttpResponse cb_delete_service(const HttpRequest& request)
{
SERVICE* service = service_find(request.uri_part(1).c_str());
ss_dassert(service);
if (runtime_destroy_service(service))
{
return HttpResponse(MHD_HTTP_NO_CONTENT);
}
return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
}
HttpResponse cb_all_servers(const HttpRequest& request)
{
return HttpResponse(MHD_HTTP_OK, server_list_to_json(request.host()));
@ -930,6 +943,8 @@ public:
m_delete.push_back(SResource(new Resource(cb_delete_server, 2, "servers", ":server")));
m_delete.push_back(SResource(new Resource(cb_delete_monitor, 2, "monitors", ":monitor")));
m_delete.push_back(SResource(new Resource(cb_delete_service, 2, "services", ":service")));
m_delete.push_back(SResource(new Resource(cb_delete_user, 3, "users", "inet", ":inetuser")));
m_delete.push_back(SResource(new Resource(cb_delete_user, 3, "users", "unix", ":unixuser")));

View File

@ -58,6 +58,7 @@
#include "internal/filter.h"
#include "internal/modules.h"
#include "internal/service.h"
#include "internal/routingworker.hh"
/** This define is needed in CentOS 6 systems */
#if !defined(UINT64_MAX)
@ -114,6 +115,7 @@ SERVICE* service_alloc(const char *name, const char *router, MXS_CONFIG_PARAMETE
service->stats.started = time(0);
service->stats.n_failed_starts = 0;
service->state = SERVICE_STATE_ALLOC;
service->active = true;
spinlock_init(&service->spin);
service->max_retry_interval = config_get_integer(params, CN_MAX_RETRY_INTERVAL);
@ -183,6 +185,7 @@ SERVICE* service_alloc(const char *name, const char *router, MXS_CONFIG_PARAMETE
void service_free(SERVICE* service)
{
ss_dassert(atomic_load_int(&service->client_count) == 0);
ss_dassert(!service->active);
spinlock_acquire(&service_spin);
@ -208,6 +211,7 @@ void service_free(SERVICE* service)
{
auto tmp = service->ports;
service->ports = service->ports->next;
ss_dassert(!tmp->active);
listener_free(tmp);
}
@ -219,6 +223,7 @@ void service_free(SERVICE* service)
while (service->dbref)
{
SERVER_REF* tmp = service->dbref;
ss_dassert(!tmp->active);
service->dbref = service->dbref->next;
MXS_FREE(tmp);
}
@ -231,6 +236,34 @@ void service_free(SERVICE* service)
MXS_FREE(service);
}
void service_destroy(SERVICE* service)
{
#ifdef SS_DEBUG
auto current = mxs::RoutingWorker::get_current();
auto main = mxs::RoutingWorker::get(mxs::RoutingWorker::MAIN);
ss_info_dassert(current == main, "Destruction of service must be done on the main worker");
#endif
ss_dassert(service->active);
service->active = false;
char filename[PATH_MAX + 1];
snprintf(filename, sizeof(filename), "%s/%s.cnf", get_config_persistdir(),
service->name);
if (unlink(filename) == -1 && errno != ENOENT)
{
MXS_ERROR("Failed to remove persisted service configuration at '%s': %d, %s",
filename, errno, mxs_strerror(errno));
}
if (atomic_load_int(&service->client_count) == 0)
{
// The service has no active sessions, it can be closed immediately
service_free(service);
}
}
/**
* Check to see if a service pointer is valid
*
@ -841,6 +874,36 @@ bool service_has_named_listener(SERVICE *service, const char *name)
return false;
}
bool service_can_be_destroyed(SERVICE *service)
{
bool rval = true;
LISTENER_ITERATOR iter;
for (SERV_LISTENER *listener = listener_iterator_init(service, &iter);
listener; listener = listener_iterator_next(&iter))
{
if (listener_is_active(listener))
{
rval = false;
break;
}
}
if (rval)
{
for (auto s = service->dbref; s; s = s->next)
{
if (s->active)
{
rval = false;
break;
}
}
}
return rval;
}
/**
* Allocate a new server reference
*
@ -1238,8 +1301,12 @@ service_find(const char *servname)
spinlock_acquire(&service_spin);
service = allServices;
while (service && strcmp(service->name, servname) != 0)
while (service)
{
if (strcmp(service->name, servname) == 0 && service->active)
{
break;
}
service = service->next;
}
spinlock_release(&service_spin);

View File

@ -44,6 +44,7 @@
#include "internal/filter.h"
#include "internal/routingworker.hh"
#include "internal/session.h"
#include "internal/service.h"
using std::string;
using std::stringstream;
@ -341,6 +342,23 @@ void session_close(MXS_SESSION *session)
}
}
class ServiceDestroyTask: public mxs::WorkerDisposableTask
{
public:
ServiceDestroyTask(SERVICE* service):
m_service(service)
{
}
void execute(Worker& worker) override
{
service_free(m_service);
}
private:
SERVICE* m_service;
};
/**
* Deallocate the specified session
*
@ -395,7 +413,13 @@ static void session_free(MXS_SESSION *session)
session->state = SESSION_STATE_FREE;
session_final_free(session);
atomic_add(&service->client_count, -1);
if (atomic_add(&service->client_count, -1) == 1 && !service->active)
{
// Destroy the service in the main routing worker thread
mxs::RoutingWorker* main_worker = mxs::RoutingWorker::get(mxs::RoutingWorker::MAIN);
main_worker->post(std::auto_ptr<ServiceDestroyTask>(new ServiceDestroyTask(service)));
}
}
static void