Skip to content

hook: Fix voting on pushes which delete packed references

When a client pushes a deletion into a transactions-enabled storage, then Gitaly would from time to time fail to accept this push. This failure was consistent for multiple hours until housekeeping kicked in, and ultimately was caused by different nodes having different state when it comes to packed references. So if a client tried to delete a reference which was packed on one node, but wasn't on another, the vote would've failed to reach quorum and thus the transaction was aborted.

The root cause is that when git deletes a reference which is in the packed-refs file, it needs to create two reference transactions: one to delete the packed-refs ref, and one to delete the loose ref. Given that this only happens if the reference is packed, our transactional voting logic started to become dependent on how well the refs are packed.

This commit implements a workaround to fix this issue: if we observe a reference transaction which consists only of force-deletions, we assume git is cleaning up the packed-refs file. Even if this is messing with git internals, it should work reasonably well: packed-refs are never updated by any "normal" git command, but it can only get deletions queued to fix the unshadowing problem when a loose ref gets deleted. As a result, it would fix our problem while hopefully being quite limited when it comes to false positives: most git commands we use specify the currently expected OID of the ref and thus wouldn't count as force-deletion.

Merge request reports