Hi Lukas,

I started working on a patch for haproxy-systemd-wrapper
(https://gist.github.com/lloesche/f4f70a9dd585ba3ffa21) which blocks
the parent until the pid gets changed (presumably by the newly forked
haproxy process). It works, however if reloads come in extremely fast
there's still a race I believe in main() between it's entry point and
the point where the signal handler is established. As far as I can
tell this race triggers because a reload via SIGUSR2 first execv's
haproxy-systemd-wrapper itself, then forks and starts a new haproxy if
env REEXEC_FLAG is set. My guess is that the rational behind reloading
haproxy-systemd-wrapper is because otherwise the old haproxy process
would end the wrapper due to the way the event loop currently works.
I.e. the wait(&status) would kill the parent when the old child ends.
One way to solve this would be to establish a waitpid only for the
latest haproxy child whose pid we get in spawn_haproxy(). And then
never end haproxy-systemd-wrapper itself during a reload.
The race was hard to debug because it's timing sensitive and attaching
a debugger or running the wrapper via strace made the problem
disappear. The only thing I could see is that sometimes the wrapper
would get killed by a USR2 which should only happen if the signal
handler is not yet established.

Best,
-- Lukas

On Wed, Nov 11, 2015 at 5:10 PM, Lukas Tribus <[email protected]> wrote:
> Hi Lukas,
>
>
>
>> When reloading haproxy too fast on EL7 (RedHat, CentOS) the system is
>> being filled with orphaned processes.
>>
>> I encountered this problem on CentOS 7 with
>> haproxy-1.5.4-4.el7_1.x86_64 but expect it to exist on all systems
>> using haproxy-systemd-wrapper not just those based on Fedora.
>>
>> Steps to reproduce:
>>
>> 1) haproxy is running normal.
>>
>> [root@localhost ~]# ps ax | grep haproxy
>> 3140 ? Ss 0:00 /usr/sbin/haproxy-systemd-wrapper -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
>> 3141 ? S 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
>> 3142 ? Ss 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
>>
>> 2) Several reloads are executed in quick succession. Problem worsens
>> when processes happen to execute a reload in parallel.
>>
>> [root@localhost ~]# while :; do systemctl reload haproxy; done
>> ^C
>>
>> 3) There's multiple haproxy processes running that will never end. As
>> you can see there's duplicate pids for the -sf arg. Maybe caused by a
>> race between haproxy-systemd-wrapper reading and the new haproxy
>> process writing it's pid.
>>
>> [root@localhost ~]# ps ax | grep haproxy
>> 423 ? S 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 419
>> 429 ? S 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 425
>> 430 ? Ss 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 419
>> 431 ? Ss 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 425
>> 31833 ? Ss 0:01 /usr/sbin/haproxy-systemd-wrapper -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
>> 36593 ? S 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 36587
>> 36600 ? Ss 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 36587
>> 38316 ? S 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 38311
>> 38324 ? Ss 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 38311
>> 38344 ? S 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 38325
>> 38350 ? Ss 0:00 /usr/sbin/haproxy -f
>> /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds -sf 38325
>> ...
>> ...
>>
>>
>> I believe the problem is that there's a race in
>> haproxy-systemd-wrapper.c line 98 where it's missing a
>> } else if (nb_pid> 0) { ... block until nb_pid is no longer found in
>> pidfile. Or something similarly blocking.
>>
>> Otherwise the parent will accept new SIGUSR2/SIGHUP reloads before the
>> new haproxy process that was spawned in line 96 has written it's pid
>> file.
>>
>> Also note the following from the systemd.service manpage:
>> "It is strongly recommended to set ExecReload= to a command that not
>> only triggers a configuration reload of the daemon, but also
>> synchronously waits for it to complete."
>> That's currently not the case.
>
> Thanks for the analysis, make sense to me. Also, since locking
> in the parent scripts [1] fixes the issue if I understand correctly,
> it further confirms your suspicion.
>
> CC'ing systemd contributors for comments.
>
>
>
> Regards,
>
> Lukas
>
>
> [1] 
> https://github.com/mesosphere/marathon-lb/commit/83260fdf687c774064b54d3bb009f5b3a1d75c97
>
>

Reply via email to