Skip to content

trustPackedRefsStat=after_open does not work reliably #271

Description

@klingarkar

Version

6.10

Operating System

Linux/Unix

Bug description

On an NFS-backed repository shared by multiple hosts, a reading host can fail to observe updates to packed-refs made by a writing host, even when core.trustPackedRefsStat=after_open is configured.

RefDirectory's AFTER_OPEN refresh opens the packed-refs file itself to force the NFS client to refresh cached attributes before FileSnapshot.isModified() decides whether to re-read it. This is insufficient in the following scenario:

On NFS, updating packed-refs replaces the file by atomically swapping in a new inode under the same name. If the previous packed-refs is still held open on a host when the swap occurs, NFS preserves the old inode as a hidden .nfsXXXX entry instead of deleting it (silly-rename). In this state, a reading host can keep resolving/observing the stale inode, and opening+closing the packed-refs file does not reliably trigger an attribute refresh. As a result, isModified() compares against stale attributes, concludes nothing changed, and the reader skips re-reading, so refs written on the writing host go unnoticed.

Actual behavior

With AFTER_OPEN configured, after a writing host updates packed-refs, a reading host may not detect that packed-refs was updated and read stale refs.

Expected behavior

After a writing host updates packed-refs, a reading host with AFTER_OPEN configured should always detect that packed-refs has changed and re-read it.

Relevant log output

Other information

This can possibly be reproduced by:

Reader

  1. Open a repo (with trustPackedRefsStat=after_open configured) via JGit RepositoryCache
  2. Start a reader thread which calls getRefDatabase().getRefs() in a loop
  3. Print any added/changed refs whenever a change in refs is detected

Writer

  1. Open the same repo's packed-refs file once in a thread and keep it open for the whole test run
  2. Repeat below for a few iterations:
  • Open the repo via RepositoryCache
  • Create a new commit
  • Generate couple of randomly named refs
  • Write both refs atomically using PackedBatchRefUpdate
  • Sleep 1-2s before the next iteration
  1. After all iterations, stop the packed-refs holder thread

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions