]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: resolvers: Properly stop server resolutions on soft-stop
authorChristopher Faulet <cfaulet@haproxy.com>
Tue, 14 Mar 2023 13:41:55 +0000 (14:41 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Tue, 14 Mar 2023 14:23:55 +0000 (15:23 +0100)
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

index 0524c6329fe94dc362929ac059bec0e75f0453e4..0b78fafb8b5374cd8dd2898ab38fd5de6cb37e07 100644 (file)
@@ -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;