Repository navigation
git annex test fails on NFS: exact high-resolution mtime comparison in linkAnnex / prepSendAnnex #294
Description
Activity
yarikoptic-gitmate commented
on Sep 28, 2026 CollaboratorAuthorMore actionsIt is the runner's kernel: 6.8 passes, 6.17 fails. Export mode is irrelevant
Run 36491917512 —
nfs-home, 2 runner images × 2 export modes × 2 reps, one build shared by all 8 jobs (10.20260901+git71-g0b0bd01297, the OsPath build):runner export rep 1 rep 2 ubuntu-22.04 rw(sync)✅ pass ✅ pass ubuntu-22.04 rw,async✅ pass ✅ pass ubuntu-24.04 rw(sync)❌ fail ❌ fail ubuntu-24.04 rw,async❌ fail ❌ fail 4/4 on each side.
syncvsasyncon the export changes nothing; the runner image decides it. What differs, as each job now logs:ubuntu-22.04 (passes) ubuntu-24.04 (fails) kernel 6.8.0-1064-azure 6.17.0-1022-azure nfs-utils 1:2.6.1-1ubuntu1.2 1:2.6.4-3ubuntu5.1 export line /tmp/nfsmount_ localhost(rw)identical mount options …,hard,proto=tcp6,……,hard,fatal_neterrors=none,proto=tcp6,…git-annex 10.20260901+git71-g0b0bd01297identical outcome all 26 groups, 7m31s 10 FAILs:import,lock, andstoreKey/present Trueintestremotefatal_neterrorsis a client option only the newer kernel advertises — the mount option lists are otherwise character-identical, so the NFS client is the only thing that moved.The failing arm reproduced the same mechanism, again mtime-only with inode and size unchanged:
unlock foo DIAGNOSTIC: linkAnnex: inode cache of .git/annex/objects/Kj/0x/SHA256E-s20--e394…/SHA256E-s20--e394… changed while copying it; before: 9446136 20 1790635113 563914024; after: 9446136 20 1790635153 0176511 git-annex: unlock failedinode
9446136and size20both unchanged; mtime +39.44 s — another stale cached attribute, against the 60 s defaultacregmax.This also answers "why did it pass before?"
Nothing in git-annex changed.
test-annexmoved to ubuntu-24.04 in4cf75f8d8(2026-01-03) and still passed on 2026-03-13, because the 24.04 image then carried a 6.8-series kernel — the same series ubuntu-22.04 still runs today. GitHub has since advanced that image to 6.17, and the newer NFS client returns a changed high-resolution mtime for an untouched file often enough to break git-annex's exactcompareStrongcomparison. A years-old assumption, a moving client kernel.Options, in order of cost
- Mount
nfs-homewithactimeo=0— proven fix on 24.04 (run 35882579073, 3/3 → 0/3), at roughly 4× the wall clock. Keeps testing a current kernel, which is the point of the flavor. - Pin the
nfs-homecell to ubuntu-22.04 — green and fast, but then the flavor stops exercising a modern NFS client, and this failure would go unnoticed for the users who will hit it. - Leave it red and treat it as the upstream report's evidence.
My preference is 1: it keeps the coverage honest and makes the job green, and the ~4× cost lands on one cell.
Either way the upstream report gets stronger: the trigger is a stock NFS client on a current kernel, not an exotic configuration.
Generated by Claude Code
- Mount
- added a commit that references this issue
on Sep 30, 2026
Tracking issue for the nightly
test-annex (nfs-home, ubuntu-24.04)failure, root-caused with the instrumented harness in #293. Nothing here needs a patch in this repository; it is a git-annex upstream matter plus a workaround for NFS users.Symptom
test-annex (nfs-home)fails on roughly two nightlies out of three, in a different test each night, and is usually the only failing job in the run. The messages alternate between:<file> failed to link to annex—git annex addof an unlocked filegit-annex: unlock failed—git annex unlockException: content changed while it was being sent—testremotetransfersRoot cause
All three are the same assumption. git-annex records a file's
InodeCache—(inode, size, high-resolution mtime)— copies the file, stats it again, and compares the two withcompareStrong, i.e. exact equality including the nanosecond field. If they differ it concludes the file changed underneath, deletes the destination and fails.On NFS a client can return two different mtimes for a file nothing wrote to. Instrumented runs caught it four times, always with inode and size unchanged and only the mtime moving:
import)1790084329.078602968→.090671216add subdirs)1790178434.030902619→.309979362edit)1790084752.023778495→1790084806.769318488conversion annexed to git)1790178735.562779131→1790178776.778504714The sub-second ones are the client's value being replaced by the server's within the same second; the tens-of-seconds ones are a cached attribute going stale (default
acregmaxis 60 s). In the object cases nothing in the test writes to the file at all.Two call sites are affected, so this is not confined to one command:
Annex/Content.hs:646—checksrcunchangedinlinkAnnex→failed to link to annex/unlock failedAnnex/Content.hs:730—sameInodeCacheinprepSendAnnex→content changed while it was being sentcompareInodeCachesonly relaxes tocompareWeak(2-second tolerance) when the inode sentinal says inodes changed, which never happens here, so the comparison stays exact.Confirmed by experiment
Run 35882579073 ran the
nfs-homeflavor three ways, three times each, same build and same runner batch:defaultactimeo0-o actimeo=0noac-o noacactimeo=0disables attribute caching and changes nothing else, so this is the attribute cache, not the write path. Cost is roughly 4×: a passingdefaultjob did the same 26 groups in 8m20s against 32m01s withactimeo=0.Workaround for NFS users: mount with
actimeo=0.noacalso works but additionally forces synchronous writes and is not needed.Is it a regression?
Not in the git-annex code that fails — in the CI environment or in timing.
test-annex (nfs-home)passed on 2026-03-13 (run 23036066273, where onlytest-dataladfailed) and was failing by 2026-08-08 (run 31238024732). Most June–July nightlies failed atbuild-package, so the suite did not run and the transition is masked in the GitHub-side logs; the tinuous archive has those runs if anyone wants to bisect the date exactly.Annex/Content*.hs,Utility/InodeCache.hs,Annex/InodeSentinal.hsorUtility/CopyFile.hs—95520e391"generalize type", which only changesdownloadUrl. No commit has touched thecompareStrongusage since at least 2025-01-01, and both the strict comparison and the high-resolution mtime predate 2022-08 (as far back as this repo'supstream/mastermirror can be deepened).So the code has assumed this for years; what changed is how often the NFS client actually violates it on the runners.
Reproducing
Two scripts, proposed for eval-under in con/eval-under#11:
mtime-stability— write, stat, copy as git-annex copies, stat again, compare; reports a rate, no git-annex involved. This is the minimal reproducer: the failure is visible with four lines of Python.git-annex-linkannex— loopsunlockand unlockedadd, reporting a failure rate in minutes rather than a pass/fail of the whole suite.That PR also adds
eval-under nfs --mount-opts, without whichactimeo=0could not be tested locally at all.Suggested next step
Report upstream at git-annex.branchable.com: exact equality on a high-resolution mtime is not a safe assumption on NFS, at both
checksrcunchangedandsameInodeCache, with the numbers above and theactimeo=0control. A fix confined tolinkAnnexwould leave transfers failing.