operation of "unpublish backend" command. This command is restricted and can
only be issued on sockets configured for levels "operator" or "admin".
+purge server <backend>/<server>
+ This is an alternative command to "del server". Its main difference is that
+ if the server cannot be immediately removed due to remaining active or idle
+ connections, the server is flagged for background deletion instead of
+ rejecting the command. A scheduled deletion is cancelled if the server state
+ is explicitly reset, even if still in maintenance mode.
+
quit
Close the connection when in interactive mode.
#define SRV_F_UDP_GSO_NOTSUPP 0x40000 /* UDP GSO is disabled due to a previous error encountered */
#define SRV_F_NAME_REFD 0x80000 /* this server's name is statically referenced (use-server, track, sample arg) */
#define SRV_F_UMODIFIED 0x100000 /* at least one setting has been explicitely set via a server keyword */
+#define SRV_F_TO_PURGE 0x200000 /* server will be deleted when unused */
/* configured server options for send-proxy (server->pp_opts) */
#define SRV_PP_V1 0x0001 /* proxy protocol version 1 */
struct list el_px; /* attach point in parent proxy */
struct list el_alt; /* extra attach point for several usages
* - for <servers_to_init> global list during parsing
+ * - for parent proxy purge list at runtime
*/
int cklen; /* the len of the cookie, to speed up checks */
int rdr_len; /* the length of the redirection prefix */
#include <haproxy/applet-t.h>
#include <haproxy/arg-t.h>
#include <haproxy/counters.h>
+#include <haproxy/errors.h>
#include <haproxy/freq_ctr.h>
#include <haproxy/proxy-t.h>
#include <haproxy/resolvers-t.h>
return !s->uweight || (s->cur_admin & SRV_ADMF_DRAIN);
}
+/* Cancel the deletion of <s> server. */
+static inline void srv_cancel_purge(struct server *s)
+{
+ /* Only server flag is reset, Cannot remove it from proxy purge list
+ * without lock. This is performed instead during purge task execution.
+ */
+ if (s->flags & SRV_F_TO_PURGE) {
+ ha_notice("Server '%s/%s' purge cancelled.\n", s->proxy->id, s->id);
+ s->flags &= ~SRV_F_TO_PURGE;
+ }
+}
+
/* Puts server <s> into maintenance mode, and propagate that status down to all
* tracking servers.
*/
{
srv_set_admin_flag(s, SRV_ADMF_FMAINT, SRV_ADM_STCHGC_NONE);
srv_clr_admin_flag(s, SRV_ADMF_FDRAIN);
+ srv_cancel_purge(s);
}
/* Puts server <s> into drain mode, and propagate that status down to all
{
srv_set_admin_flag(s, SRV_ADMF_FDRAIN, SRV_ADM_STCHGC_NONE);
srv_clr_admin_flag(s, SRV_ADMF_FMAINT);
+ srv_cancel_purge(s);
}
/* Puts server <s> into ready mode, and propagate that status down to all
{
srv_clr_admin_flag(s, SRV_ADMF_FDRAIN);
srv_clr_admin_flag(s, SRV_ADMF_FMAINT);
+ srv_cancel_purge(s);
}
static inline void srv_set_init_state(struct server *srv)
struct proxy *px = srv->proxy;
LIST_DEL_INIT(&srv->el_px);
-
- if (global.mode & MODE_STARTING) {
- /* Necessary if startup is interrupted due to an early error before check_config_validity(). */
- LIST_DEL_INIT(&srv->el_alt);
- }
+ LIST_DEL_INIT(&srv->el_alt);
/* Reset the proxy's ready_srv if it was this one. */
HA_ATOMIC_CAS(&px->ready_srv, &srv, NULL);
ha_free(&p);
}
+/* Try to delete servers flagged for purge in <px> backend. */
+static int proxy_purge_servers(struct proxy *px)
+{
+ struct server *srv;
+ int ret;
+
+ while (!LIST_ISEMPTY(&px->servers_purge)) {
+ srv = LIST_NEXT(&px->servers_purge, struct server *, el_alt);
+ if (!(srv->flags & SRV_F_TO_PURGE)) {
+ LIST_DEL_INIT(&srv->el_alt);
+ continue;
+ }
+
+ /* TODO unnecessary server lookup */
+ ret = srv_check_for_deletion(px->id, srv->id, PR_DEL_MODE_SYNC, NULL, NULL, NULL);
+ if (ret < 0) {
+ srv_cancel_purge(srv);
+ LIST_DEL_INIT(&srv->el_alt);
+ }
+ else if (ret > 0) {
+ ha_notice("Server '%s/%s' purged.\n", px->id, srv->id);
+ srv_unregister(srv);
+ srv_drop(srv);
+ /* TODO yield after nth deletion performed in a row */
+ }
+ else {
+ /* Interrupt work on first server not yet removable. */
+ break;
+ }
+ }
+
+ return 0;
+}
+
/* Proxy purge task handler, only used by user configured backend instances.
* Its objective is to remove any servers flagged with purge or the backend
* instance itself.
{
struct proxy *px = context;
+ /* Must not dereference <px> before entering thread isolation and
+ * checking task state, as proxy_drop() performed outside isolation
+ * during "del backend".
+ */
+
thread_isolate_full();
if (t->state & TASK_KILLED) {
goto requeue;
}
+ if (!LIST_ISEMPTY(&px->servers_purge))
+ proxy_purge_servers(px);
+
+ /* Set next task execution or cancel it if no more work to do. */
if (!LIST_ISEMPTY(&px->servers_purge))
t->expire = tick_add(now_ms, MS_TO_TICKS(1000));
else
srv->obj_type = OBJ_TYPE_SERVER;
srv->proxy = proxy;
LIST_APPEND(&proxy->servers, &srv->el_px);
+ LIST_INIT(&srv->el_alt);
LIST_INIT(&srv->srv_rec_item);
LIST_INIT(&srv->ip_rec_item);
LIST_INIT(&srv->pp_tlvs);
return 1;
}
+/* Parser for "purge server" CLI command. Always return 1. */
+static int cli_parse_purge_server(char **args, char *payload, struct appctx *appctx, void *private)
+{
+ struct proxy *be;
+ struct server *srv;
+ const char *be_name, *sv_name, *msg;
+ int ret;
+
+ if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
+ return 1;
+
+ ++args;
+
+ thread_isolate_full();
+
+ if (parse_be_srv(args[1], &be_name, &sv_name, &msg)) {
+ cli_err(appctx, msg);
+ goto out;
+ }
+
+ ret = srv_check_for_deletion(be_name, sv_name, &be, &srv, &msg);
+ if (ret < 0) {
+ /* Unremovable server, cancel purge. */
+ cli_err(appctx, msg);
+ goto out;
+ }
+
+ if (!ret) {
+ /* Cannot remove server yet, schedule purging. */
+ if (!LIST_INLIST(&srv->el_alt))
+ LIST_APPEND(&be->servers_purge, &srv->el_alt);
+ srv->flags |= SRV_F_TO_PURGE;
+
+ if (!tick_isset(be->purge_task->expire))
+ be->purge_task->expire = tick_add(now_ms, 1000);
+ task_queue(be->purge_task);
+ ha_notice("Server '%s/%s' flagged for deletion.\n", be->id, srv->id);
+ cli_msg(appctx, LOG_INFO, "Server scheduled for purge.\n");
+ }
+ else {
+ srv_unregister(srv);
+ ha_notice("Server %s/%s deleted.\n", be->id, srv->id);
+ cli_msg(appctx, LOG_INFO, "Server deleted.\n");
+ srv_drop(srv);
+ }
+
+ out:
+ thread_release();
+ return 1;
+}
+
/* Reset the statistics counters of a single server, invoked from the
* "clear counters server <backend>/<server> [force]" CLI command (dispatched
* by cli_parse_clear_counters() in stats.c, since "clear counters" is a
{ { "set", "weight", NULL }, "set weight <bk>/<srv> (DEPRECATED) : change a server's weight (use 'set server' instead)", cli_parse_set_weight },
{ { "add", "server", NULL }, "add server <bk>/<srv> : create a new server", cli_parse_add_server, cli_io_handler_add_server },
{ { "del", "server", NULL }, "del server <bk>/<srv> : remove a server", cli_parse_delete_server, NULL },
+ { { "purge", "server", NULL }, "purge server <bk>/<srv> : remove on background a server", cli_parse_purge_server, NULL },
{{},}
}};