Hi, On Thu, Jul 2, 2026 at 4:18 PM Masahiko Sawada <[email protected]> wrote: > > > Thank you Sawada-san. I've been thinking more about it and I agree we > > need to address this. While I still think the scenario is unlikely in > > practice (SIGTERM would have to take a long time, the slot's xmin > > would have to be very old while the walsender is still running, etc.), > > I think it's worth handling. > > > > I can think of a couple of approaches: > > > > 1. Use ConditionVariableTimedSleep instead of ConditionVariableSleep > > when called from an autovacuum worker. Workers don't block forever, > > but they still wait for the timeout duration, still send redundant > > SIGTERMs, and a correct timeout value needs to be chosen. When it > > expires, the worker either retries (still stuck) or gives up (same as > > approach 2). > > > > 2. Make the vacuum path non-blocking when another process is already > > invalidating the same slot. The first process to attempt invalidation > > proceeds normally: it sends SIGTERM and waits on > > ConditionVariableSleep for the process holding the slot to exit. But > > if a subsequent autovacuum worker finds that another process has > > already initiated invalidation of this slot, it skips the slot and > > proceeds with vacuum instead of waiting on the same > > ConditionVariableSleep. > > > > I think approach 2 is simple. If another process is already > > invalidating the slot, there's no reason for the autovacuum worker to > > also block. The tradeoff is that this vacuum cycle's OldestXmin won't > > move forward and it will need another cycle for this relation. But > > that's fine given that the scenario as explained above is unlikely to > > happen in practice. > > > > Please let me know if my thinking sounds reasonable. I'm open to other > > ideas too. > > The third idea I came up with is that (auto)vacuum behaves differently > in terms of XID-aged slot invalidation depending on the slot being > used or not; (auto)vacuum invalidate the XID-aged slot if no one is > holding the slot, and it just wakes up the checkpointer to invalidate > the slot if a process is still holding the slot. If the XID-aged slot > is not held by any process, (auto)vacuum simply invalidates the slot. > I believe that while the former case happens in most cases in > practice, delegating the checkpointer to invalidate XID-aged slots > might help avoid vacuum from being blocked. > > What do you think about the above idea?
I see two cases here. 1/ No one is holding the slot. The connection is gone for whatever reason. Here vacuum just acquires and invalidates the slot right away, no signal, no waiting, and this already works. 2/ The slot is held but has aged out, due to a slow connection, replica lag, or slow decoding/apply. This is the case that involves terminating the holder and waiting, which is where blocking can happen. I partially agree with your suggestion for case 2. My preference is to not add any blockers for vacuum. It does opportunistic XID-age invalidation, invalidates the slots it can take without waiting, and leaves the held ones to the checkpointer, which is the guaranteed path. It is also easier to reason about and to explain to users. The tradeoff is that vacuum won't invalidate a held slot in the same pass, so the relation being vacuumed right then doesn't get the advanced horizon. Once the checkpointer has invalidated the slot, later relations pick it up, which may well be in the same vacuum cycle or next. I am fine with that. Given this, I do not think 0003 is needed anymore. Its whole purpose was to stop multiple vacuum processes piling onto the same slot's condition variable. If the vacuum never waits there, that contention cannot happen. There is only one checkpointer. The unheld case is already safe. The check runs under the slot's spinlock, so the first process marks the slot invalidated and any others see it is done and move on. No signal, no waiting. I will drop 0003. I would skip having the vacuum explicitly wake the checkpointer when it sees a held slot. Imagine one walsender holding the slot's xmin and 8 autovacuum workers running. Each worker hits that same held slot and would signal the checkpointer, so we would get a signal per worker for a single slot, repeated every cycle until the checkpointer acts. If the checkpointer is already running, another request gets queued, and this can happen repeatedly, creating a flood of checkpoint requests. Since 0001 already invalidates aged slots during every checkpoint, the slot gets cleaned up on the checkpointer's regular pass anyway. I would rather rely on that than send extra signals to perform checkpoints. Thoughts? -- Bharath Rupireddy Amazon Web Services: https://aws.amazon.com
