mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-03-14 03:22:06 +01:00
MINOR: stats: protect proxy iteration via watcher
Define a new <px_watch> watcher member in stats applet context. It is used to register the applet on a proxy when iterating over the proxies list. <obj1> is automatically updated via the watcher interaction. Watcher is first initialized prior to stats_dump_proxies() invocation. This guarantees that stats dump is safe even if applet yields and a backend is removed in parallel.
This commit is contained in:
parent
4bcfc09acf
commit
20376c54e2
@ -578,6 +578,7 @@ struct show_stat_ctx {
|
||||
int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */
|
||||
int st_code; /* the status code returned by an action */
|
||||
struct buffer chunk; /* temporary buffer which holds a single-line output */
|
||||
struct watcher px_watch; /* watcher to automatically update obj1 on backend deletion */
|
||||
struct watcher srv_watch; /* watcher to automatically update obj2 on server deletion */
|
||||
enum stat_state state; /* phase of output production */
|
||||
};
|
||||
|
||||
@ -4089,6 +4089,8 @@ static int http_handle_stats(struct stream *s, struct channel *req, struct proxy
|
||||
ctx->flags |= STAT_F_FMT_HTML; /* assume HTML mode by default */
|
||||
if ((msg->flags & HTTP_MSGF_VER_11) && (txn->meth != HTTP_METH_HEAD))
|
||||
ctx->flags |= STAT_F_CHUNKED;
|
||||
|
||||
watcher_init(&ctx->px_watch, &ctx->obj1, offsetof(struct proxy, watcher_list));
|
||||
watcher_init(&ctx->srv_watch, &ctx->obj2, offsetof(struct server, watcher_list));
|
||||
|
||||
htx = htxbuf(&req->buf);
|
||||
|
||||
@ -2116,6 +2116,8 @@ static size_t http_stats_fastfwd(struct appctx *appctx, struct buffer *buf,
|
||||
static void http_stats_release(struct appctx *appctx)
|
||||
{
|
||||
struct show_stat_ctx *ctx = appctx->svcctx;
|
||||
if (ctx->domain == STATS_DOMAIN_PROXY && ctx->obj1)
|
||||
watcher_detach(&ctx->px_watch);
|
||||
if (ctx->px_st == STAT_PX_ST_SV && ctx->obj2)
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
}
|
||||
|
||||
@ -1616,11 +1616,13 @@ int stats_dump_proxies(struct stconn *sc, struct buffer *buf, struct htx *htx)
|
||||
struct proxy *px;
|
||||
|
||||
/* dump proxies */
|
||||
while (ctx->obj1) {
|
||||
/* obj1 is updated and returned through watcher_next() */
|
||||
for (px = ctx->obj1; px;
|
||||
px = watcher_next(&ctx->px_watch, px->next)) {
|
||||
|
||||
if (stats_is_full(appctx, buf, htx))
|
||||
goto full;
|
||||
|
||||
px = ctx->obj1;
|
||||
/* Skip the global frontend proxies and non-networked ones.
|
||||
* Also skip proxies that were disabled in the configuration
|
||||
* This change allows retrieving stats from "old" proxies after a reload.
|
||||
@ -1631,7 +1633,6 @@ int stats_dump_proxies(struct stconn *sc, struct buffer *buf, struct htx *htx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->obj1 = px->next;
|
||||
ctx->px_st = STAT_PX_ST_INIT;
|
||||
ctx->field = 0;
|
||||
}
|
||||
|
||||
25
src/stats.c
25
src/stats.c
@ -590,12 +590,13 @@ int stats_dump_stat_to_buffer(struct stconn *sc, struct buffer *buf, struct htx
|
||||
goto full;
|
||||
}
|
||||
|
||||
if (domain == STATS_DOMAIN_PROXY)
|
||||
ctx->obj1 = proxies_list;
|
||||
|
||||
ctx->px_st = STAT_PX_ST_INIT;
|
||||
ctx->field = 0;
|
||||
ctx->state = STAT_STATE_LIST;
|
||||
/* Update ctx->obj1 via watcher to point on the first proxy. */
|
||||
if (domain == STATS_DOMAIN_PROXY)
|
||||
watcher_attach(&ctx->px_watch, proxies_list);
|
||||
|
||||
__fallthrough;
|
||||
|
||||
case STAT_STATE_LIST:
|
||||
@ -944,6 +945,8 @@ static int cli_parse_show_stat(char **args, char *payload, struct appctx *appctx
|
||||
ctx->scope_len = 0;
|
||||
ctx->http_px = NULL; // not under http context
|
||||
ctx->flags = STAT_F_SHNODE | STAT_F_SHDESC;
|
||||
|
||||
watcher_init(&ctx->px_watch, &ctx->obj1, offsetof(struct proxy, watcher_list));
|
||||
watcher_init(&ctx->srv_watch, &ctx->obj2, offsetof(struct server, watcher_list));
|
||||
|
||||
if ((strm_li(appctx_strm(appctx))->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER)
|
||||
@ -1028,8 +1031,12 @@ static int cli_io_handler_dump_stat(struct appctx *appctx)
|
||||
static void cli_io_handler_release_stat(struct appctx *appctx)
|
||||
{
|
||||
struct show_stat_ctx *ctx = appctx->svcctx;
|
||||
if (ctx->px_st == STAT_PX_ST_SV && ctx->obj2)
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
|
||||
if (ctx->state == STAT_STATE_LIST && ctx->domain == STATS_DOMAIN_PROXY) {
|
||||
watcher_detach(&ctx->px_watch);
|
||||
if (ctx->px_st == STAT_PX_ST_SV)
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
}
|
||||
}
|
||||
|
||||
static int cli_io_handler_dump_json_schema(struct appctx *appctx)
|
||||
@ -1089,8 +1096,12 @@ static int cli_io_handler_dump_stat_file(struct appctx *appctx)
|
||||
static void cli_io_handler_release_dump_stat_file(struct appctx *appctx)
|
||||
{
|
||||
struct show_stat_ctx *ctx = appctx->svcctx;
|
||||
if (ctx->px_st == STAT_PX_ST_SV && ctx->obj2)
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
|
||||
if (ctx->state == STAT_STATE_LIST && ctx->domain == STATS_DOMAIN_PROXY) {
|
||||
watcher_detach(&ctx->px_watch);
|
||||
if (ctx->px_st == STAT_PX_ST_SV)
|
||||
watcher_detach(&ctx->srv_watch);
|
||||
}
|
||||
}
|
||||
|
||||
int stats_allocate_proxy_counters_internal(struct extra_counters **counters,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user