Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions internal/gitclone/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,19 @@ func (r *Repository) TryStartCloning() bool {
return true
}

// WithFetchExclusion runs fn while holding the fetch semaphore, preventing
// concurrent git fetch operations. Use this for operations like tar that
// read the repository directory non-atomically and need a consistent view.
func (r *Repository) WithFetchExclusion(ctx context.Context, fn func() error) error {
select {
case <-r.fetchSem:
defer func() { r.fetchSem <- struct{}{} }()
return fn()
case <-ctx.Done():
return errors.Wrap(ctx.Err(), "context cancelled waiting for fetch exclusion")
}
}

// MarkRestored configures a restored snapshot (e.g. from S3) as a mirror.
// The caller must have already transitioned to StateCloning (via
// TryStartCloning) before extracting the snapshot. On error the state is
Expand Down
9 changes: 7 additions & 2 deletions internal/strategy/git/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,13 @@ func (s *Strategy) generateAndUploadMirrorSnapshot(ctx context.Context, repo *gi
cacheKey := mirrorSnapshotCacheKey(upstream)
excludePatterns := []string{"*.lock"}

if err := repo.WithReadLock(func() error {
return snapshot.Create(ctx, s.cache, cacheKey, repo.Path(), 0, excludePatterns, s.config.ZstdThreads)
// Hold the fetch semaphore while tar-ing the bare mirror directory.
// Without this, a concurrent git fetch can replace packed-refs mid-read,
// causing tar to capture a truncated file.
if err := repo.WithFetchExclusion(ctx, func() error {
return repo.WithReadLock(func() error {
return snapshot.Create(ctx, s.cache, cacheKey, repo.Path(), 0, excludePatterns, s.config.ZstdThreads)
})
}); err != nil {
return errors.Wrap(err, "create mirror snapshot")
}
Expand Down