Skip to content

Invalidate every ref's lock snapshot when a relayed lock changes - #60

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/lock-change-invalidates-every-ref
Sep 28, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/lock-change-invalidates-every-ref

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #46

What was wrong

A lock listing is cached under the ?refspec= it was fetched with. A create (POST /locks) or unlock (POST /locks/:id/unlock) carries its ref in the JSON body instead. After a create or unlock, LockRouteHandler.InvalidateIfChanged built the key to clear from the query string, so it cleared the null-ref entry. The listing cached under the refspec stayed stale for up to ListTtl: a new lock was missing from it, and a released lock still showed as held.

Change

I took the simpler of the two fixes suggested in the issue, as the triage comment recommends:

  • ILockSnapshotStore.Invalidate(string upstream, string repositoryPath) drops every snapshot for that repository, whatever ref it was cached under.
  • The relayed create/unlock path in LockRouteHandler calls it.
  • The batch fan-out in LockFanOut calls it as well. That covers a client that lists under one ref and locks under another.
  • The only cost is a refetch of the listing.

Test

LockListCachingTests.ChangingALock_InvalidatesASnapshotListedUnderARefspec covers a create case and an unlock case. Each lists with ?refspec=refs/heads/main, changes a lock with that ref in the body, then lists again and checks that the change shows up.

  • Without the fix, both cases fail on the second listing. I checked this by stashing the GitLfsCache/ changes and running the test.
  • With the fix, the full suite passes: 328/328.

🤖 Generated with Claude Code

https://claude.ai/code/session_016wsoxnwaqMuAzvkm2xnzYh


Generated by Claude Code

A lock listing is cached under the ?refspec= it was fetched with, but a
create or unlock carries its ref in the JSON body, so LockRouteHandler
invalidated the null-ref key and left the refspec-scoped listing stale for
up to ListTtl: a new lock missing, a released lock still shown.

ILockSnapshotStore.Invalidate now takes the upstream and repository and
drops every snapshot for that repository whatever its ref. Both the relayed
create/unlock and the batch fan-out use it, which also covers a client that
lists under one ref and locks under another. The only cost is a refetch.

Fixes #46

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wsoxnwaqMuAzvkm2xnzYh
Comment thread GitLfsCache/Locks/LockSnapshotStore.cs Fixed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wsoxnwaqMuAzvkm2xnzYh
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Creating or unlocking a lock does not invalidate the cached lock list when the list was fetched with ?refspec=

2 participants