Repository navigation
gocached: shard put-queue cleanup intents and temp files by blob - #9
Merged
Merged
Conversation
On ci-gocached-aws-1's S3 Files NFS mount, the kernel serializes creates, unlinks, and renames within a directory, holding the directory's lock for each operation's server round trip of about 5ms. A benchmark measured it directly: with 64 goroutines creating and unlinking files in one directory, 62 were parked on the directory lock and about one was in an NFS RPC, for 96 creates+unlinks a second at any concurrency, while the same load over 256 directories reached 3,300 a second with 47 RPCs in flight. That one-directory ceiling is the put queue's: every copy into the main blob directory created and later unlinked a cleanup intent in the single .cleanup directory, and created its temp file in the main directory's root before renaming it out. The change in #8 stops writing intents for the most common case, blobs already present. For the blobs that are really copied, spread the remaining mutations over the 256 shards the main directory already uses: - Intents go in .cleanup/<xx>/, xx being the first two hex digits of the blob's name, rather than directly in .cleanup. - The temp file is created in the blob's own shard directory instead of the main directory's root, so its rename into place stays within that one directory and locks nothing else. Both directories are created on demand: a create is tried first and the directory made only if it fails with ENOENT, as renameCreatingDir did for the rename, which no longer needs that since the temp file now lives in the target's directory. The sweeper lists .cleanup and then each shard subdirectory in it, sweeping intents in both the new layout and, for upgrades, the old flat one. That's 257 directory listings a minute instead of one, each locking only its own subdirectory, and only briefly. Nothing else lists the main directory's shard directories (usage stats come from SQLite, and GETs and eviction use exact names), so a temp file appearing in one while a copy is in flight changes nothing. Rolling back to an older version after this one has run would leave intents in shard subdirectories unswept, and the old sweeper would log each subdirectory as a malformed intent once a minute. Updates tailscale/corp#47471 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: Icadb72b048c11f6e58c3332903a50438c561a283
bradfitz
force-pushed
the
bradfitz/putq_shard_dirs
branch
from
October 8, 2026 17:41
ca842a8 to
7d5ca60
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
On ci-gocached-aws-1's S3 Files NFS mount, the kernel serializes
creates, unlinks, and renames within a directory, holding the
directory's lock for each operation's server round trip of about 5ms.
A benchmark measured it directly: with 64 goroutines creating and
unlinking files in one directory, 62 were parked on the directory lock
and about one was in an NFS RPC, for 96 creates+unlinks a second at any
concurrency, while the same load over 256 directories reached 3,300 a
second with 47 RPCs in flight. That one-directory ceiling is the put
queue's: every copy into the main blob directory created and later
unlinked a cleanup intent in the single .cleanup directory, and created
its temp file in the main directory's root before renaming it out.
The change in #8 stops writing intents for the most common case, blobs
already present. For the blobs that are really copied, spread the
remaining mutations over the 256 shards the main directory already
uses:
.cleanup/<xx>/,xxbeing the first two hex digits ofthe blob's name, rather than directly in .cleanup.
of the main directory's root, so its rename into place stays within
that one directory and locks nothing else.
Both directories are created on demand: a create is tried first and
the directory made only if it fails with ENOENT, as renameCreatingDir
did for the rename, which no longer needs that since the temp file now
lives in the target's directory.
The sweeper lists .cleanup and then each shard subdirectory in it,
sweeping intents in both the new layout and, for upgrades, the old flat
one. That's 257 directory listings a minute instead of one, each
locking only its own subdirectory, and only briefly.
Nothing else lists the main directory's shard directories (usage stats
come from SQLite, and GETs and eviction use exact names), so a temp
file appearing in one while a copy is in flight changes nothing.
Rolling back to an older version after this one has run would leave
intents in shard subdirectories unswept, and the old sweeper would log
each subdirectory as a malformed intent once a minute.
Updates tailscale/corp#47471
Signed-off-by: Brad Fitzpatrick bradfitz@tailscale.com
Change-Id: Icadb72b048c11f6e58c3332903a50438c561a283