S3 driver PurgeUploads causes extreme allocation activity in directoryDiff method
Description
Description
We are running Distribution Registry v3.1.1 with the S3 storage driver (s3-aws) as a Windows service written in Go. The registry uses an S3-compatible endpoint backed by a NAS environment using QuObjects.
The Windows service has experienced repeated crashes and automatic restarts. Go heap profiling shows that the background PurgeUploads operation can generate extremely high allocation activity while walking the S3 storage.
Environment:
- Distribution Registry: v3.1.1
- Storage driver: S3 (s3-aws)
- Platform: Windows
- Go: 1.26.7
- Object storage: QuObjects S3-compatible endpoint backed by NAS
- Approximate number of objects in bucket: 2683 (~2 GB)
Observed behavior
During a problematic run, a heap profile showed approximately 28 GB of cumulative allocation, with the S3 directoryDiff function responsible for approximately 15.41 GiB, or 99.68% of the 15.46 GiB
Relevant profile:
Powershell statistics while process is running:
| Time | PID | Working Set (MB) | Private (MB) | Virtual (GB) | Threads | System Commit (GB) | Commit Limit (GB) |
|---|---|---|---|---|---|---|---|
| 02:37:08 | 3572 | 122.5 | 120.7 | 5.57 | 21 | 7.44 | 32.78 |
| 02:37:22 | 3572 | 502.6 | 501.4 | 5.85 | 21 | 8.23 | 32.78 |
| 02:37:58 | 3572 | 11,689.7 | 11,710.3 | 16.81 | 21 | 18.81 | 32.78 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 02:38:20 | 3572 | 11,670.0 | 24,022.2 | 29.19 | 21 | 30.76 | 34.80 |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 02:39:41 | 3572 | 13,319.1 | 35,455.8 | 39.99 | 21 | 41.97 | 43.06 |
| 02:40:35 | 3572 | 12,243.9 | 34,586.3 | 40.03 | 21 | 41.18 | 44.07 |
| 02:40:36 | 3572 | 12,730.6 | 35,454.4 | 40.04 | 21 | 42.06 | 45.40 |
GO pprof tool shows:
> go tool pprof -list=directoryDiff heap.high
Total: 15.46GB
ROUTINE ======================== github.com/distribution/distribution/v3/registry/storage/driver/s3-aws.directoryDiff in /root/go/pkg/mod/github.com/distribution/distribution/[email protected]/registry/storage/driver/s3-aws/s3.go
15.41GB 15.41GB (flat, cum) 99.68% of Total
. . 1243:func directoryDiff(prev, current string) []string {
. . 1244: var paths []string
. . 1245:
. . 1246: if prev == "" || current == "" {
. . 1247: return paths
. . 1248: }
. . 1249:
. . 1250: parent := current
. . 1251: for {
. . 1252: parent = filepath.Dir(parent)
. . 1253: if parent == "/" || parent == prev || strings.HasPrefix(prev+"/", parent+"/") {
. . 1254: break
. . 1255: }
15.41GB 15.41GB 1256: paths = append(paths, parent)
. . 1257: }
. . 1258: slices.Reverse(paths)
. . 1259: return paths
. . 1260:}
. . 1261:The allocation tree shows the complete call chain:
startUploadPurger
-> PurgeUploads
-> getOutstandingUploads
-> Base.Walk
-> s3-aws.(*driver).Walk
-> s3-aws.(*driver).doWalk
-> s3.ListObjectsV2PagesWithContext
-> s3-aws.(*driver).doWalk.func1
-> directoryDiff15.41 GiB (100%) github.com/distribution/distribution/v3/registry/handlers.startUploadPurger.func1
app.go:1092
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage.PurgeUploads
purgeuploads.go:34
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage.getOutstandingUploads
purgeuploads.go:70
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage/driver/base.(*Base).Walk
base.go:331
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage/driver/s3-aws.(*driver).Walk
s3.go:1105
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage/driver/s3-aws.(*driver).doWalk
s3.go:1151
-> 15.41 GiB (100%) github.com/aws/aws-sdk-go/service/s3.(*S3).ListObjectsV2PagesWithContext
api.go:7654
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage/driver/s3-aws.(*driver).doWalk.func1
s3.go:1158
-> 15.41 GiB (100%) github.com/distribution/distribution/v3/registry/storage/driver/s3-aws.directoryDiff
s3.go:1256 paths = append(paths, parent)After the Windows service was restarted, a profile from a healthy process showed only approximately 15.95 MiB of live heap and no significant directoryDiff allocation. A separate healthy-process alloc_space capture showed approximately 241 MB of cumulative allocation. This suggests that the excessive state is process-local and associated with the S3 walk.
Workaround
We disabled the registry purging option. With purging disabled, the observed allocation spike does not occur.
Reproduce
N/A
Expected behavior
No response
registry version
go.mod
github.com/distribution/distribution/v3 v3.1.1
Additional Info
No response
Source: distribution/distribution