From 52ec6f14c4cbfe23cbe3bf6ef55af84ba17047de Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 14 Mar 2023 14:41:55 +0100 Subject: [PATCH] BUG/MEDIUM: resolvers: Properly stop server resolutions on soft-stop When HAproxy is stopping, the DNS resolutions must be stopped, except those triggered from a "do-resolve" action. To do so, the resolutions themselves cannot be destroyed, the current design is too complex. However, it is possible to mute the resolvers tasks. The same is already performed with the health-checks. On soft-stop, the tasks are still running periodically but nothing if performed. For the resolvers, when the process is stopping, before running a resolution, we check all the requesters attached to this resolution. If t least a request is a stream or if there is a requester attached to a running proxy, a new resolution is triggered. Otherwise, we ignored the resolution. It will be evaluated again on the next wakeup. This way, "do-resolv" action are still working during soft-stop but other resoluation are stopped. Of course, it may be see as a feature and not a bug because it was never performed. But it is in fact not expected at all to still performing resolutions when HAProxy is stopping. In addution, a proxy option will be added to change this behavior. This patch partially fixes the issue #1874. It could be backported to 2.7 and maybe to 2.6. But no further. --- src/resolvers.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/resolvers.c b/src/resolvers.c index 0524c6329..0b78fafb8 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -2394,6 +2394,48 @@ struct task *process_resolvers(struct task *t, void *context, unsigned int state /* Handle all resolutions in the wait list */ list_for_each_entry_safe(res, resback, &resolvers->resolutions.wait, list) { + + if (unlikely(stopping)) { + /* If haproxy is stopping, check if the resolution to know if it must be run or not. + * If at least a requester is a stream (because of a do-resolv action) or if there + * is a requester attached to a running proxy, the resolution is performed. + * Otherwise, it is skipped for now. + */ + struct resolv_requester *req; + int must_run = 0; + + list_for_each_entry(req, &res->requesters, list) { + struct proxy *px = NULL; + + switch (obj_type(req->owner)) { + case OBJ_TYPE_SERVER: + px = __objt_server(req->owner)->proxy; + break; + case OBJ_TYPE_SRVRQ: + px = __objt_resolv_srvrq(req->owner)->proxy; + break; + case OBJ_TYPE_STREAM: + /* Always perform the resolution */ + must_run = 1; + break; + default: + break; + } + /* Perform the resolution if the proxy is not stopped or disabled */ + if (px && !(px->flags & (PR_FL_DISABLED|PR_FL_STOPPED))) + must_run = 1; + + if (must_run) + break; + } + + if (!must_run) { + /* Skip the reolsution. reset it and wait for the next wakeup */ + resolv_reset_resolution(res); + continue; + } + } + if (LIST_ISEMPTY(&res->requesters)) { abort_resolution(res); continue;