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:
parent
18c1ec2678
commit
5a40064826
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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")));
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user