git-maintenance detach timing, was Re: I discovered a minor issue with `git fetch`.
- From
Jeff King <peff@peff.net>
- Date
- May 27, 2026, 10:56 UTC
- Message-ID
- <20260527105639.GK981444@coredump.intra.peff.net>
- In-Reply-To
- <CAD6AYr9YmcnkdW=Nx=HUKcuaNbv1ukrAbXRnKyGibCQDy8N3hQ@mail.gmail.com>
On Fri, May 22, 2026 at 03:45:25PM +0800, SURA wrote:
Show 12 quoted lines
> A zombie process has appeared. It appears to originate from a `fetch` > subprocess that terminates very quickly; despite several attempts, I > have been unable to successfully capture it. > > This issue was discovered within a legacy service. A few days after > upgrading to Git 2.53.0, the system's PID resources were exhausted by > zombie processes. This is likely the result of recent changes, as this > problem did not exist in earlier versions (2.4x). > > To be honest, this is not an urgent matter; I have already deployed > `tini` as the init process (PID 1) to prevent the service from > becoming unavailable.
Yeah, I agree that you need some kind of zombie-reaping init process. But I did wonder if we might have started generating more zombies here.
I did a little poking around with "strace -p 1". I didn't see extra fetch processes, but I did see a lot of zombie git-maintenance processes getting reaped. Which makes sense; by default we run background maintenance with --detach. We can't ever reap that ourselves, since the whole point is that it might outlive the parent fetch.
Once upon a time, we used to run "git gc --auto", and it would check whether gc was needed (using a simple count of objects and packs) before detaching. So in most cases it would realize there was nothing to be done and exit immediately without daemonizing, and would get reaped by git-fetch.
We switched to running "git maintenance" in v2.29. But it didn't yet have a detached mode; it just run "git gc --detach" under the hood, so the behavior was roughly the same (gc was reaped by maintenance which was reaped by fetch).
Later, git-maintenance learned its own --detach flag, as of v2.47. But unlike gc, it detaches immediately, and then each sub-task decides if it needs to be run or not. So every "git fetch" will generate a detached maintenance process that then gets reaped by init.
And if you moved from a pre-v2.47 version, then you'd see an increase in such processes.
I think this is probably OK in practice. It is an extra fork that git-gc never incurred, but as long as you have a functioning init process, they won't accumulate.
I do wonder if git-maintenance could be more like git-gc here. Its notion of tasks is more abstract, but it could in theory ask each task "do you need to run?" and if they all say "no", then it can quit without detaching. That would save an extra fork() for every noop auto-maintenance call. I don't know how measurable that is in practice. Or even how easy it is for each task to do such a check. Something like "prefetch" is kind of all-or-nothing; you find out whether it needs doing by doing it.
-Peff