Replace serial getManifestSize() calls in ListSnapshots with bounded concurrent downloads using errgroup. For each remote snapshot not in the local DB, manifest downloads now run in parallel (up to 10 concurrent goroutines) instead of one at a time.
Changes
Use errgroup with SetLimit(10) for bounded concurrency
Collect remote-only snapshot IDs first, pre-add entries with zero size
Download manifests concurrently, patch sizes from results
Remove now-unused getManifestSize helper (logic inlined into goroutines)
Promote golang.org/x/sync from indirect to direct dependency
## Summary
Replace serial `getManifestSize()` calls in `ListSnapshots` with bounded concurrent downloads using `errgroup`. For each remote snapshot not in the local DB, manifest downloads now run in parallel (up to 10 concurrent goroutines) instead of one at a time.
## Changes
- Use `errgroup` with `SetLimit(10)` for bounded concurrency
- Collect remote-only snapshot IDs first, pre-add entries with zero size
- Download manifests concurrently, patch sizes from results
- Remove now-unused `getManifestSize` helper (logic inlined into goroutines)
- Promote `golang.org/x/sync` from indirect to direct dependency
## Testing
- `make check` passes (fmt-check, lint, tests)
- `docker build .` passes
closes https://git.eeqj.de/sneak/vaultik/issues/8
Replace serial getManifestSize() calls with bounded concurrent downloads
using errgroup. For each remote snapshot not in the local DB, manifest
downloads now run in parallel (up to 10 concurrent) instead of one at a
time.
Changes:
- Use errgroup with SetLimit(10) for bounded concurrency
- Collect remote-only snapshot IDs first, pre-add entries with zero size
- Download manifests concurrently, patch sizes from results
- Remove now-unused getManifestSize helper (logic inlined into goroutines)
- Promote golang.org/x/sync from indirect to direct dependency
closes#8
✅Synchronization: sync.Mutex protects shared results slice; snapshots slice is only mutated single-threaded (before/after errgroup)
✅getManifestSize removed: Fully deleted. Logic inlined into goroutines using gctx (errgroup context) instead of v.ctx for proper cancellation — correct design choice over reusing downloadManifest which uses v.ctx
✅No magic numbers: const maxConcurrentManifestDownloads = 10 with descriptive comment
✅golang.org/x/sync: Promoted from indirect → direct in go.mod; hash-pinned in go.sum
✅Scope: Only go.mod and internal/vaultik/snapshot.go changed — no linter config, test, or Makefile modifications
✅Go 1.26.1: Range variable capture is per-iteration — no closure bug
✅Error propagation: Single goroutine failure cancels context and returns error, matching original serial behavior
✅docker build .: All stages pass (lint, fmt-check, tests, build)
Clean implementation. No issues found.
## Review: PASS ✅
Reviewed [PR #50](https://git.eeqj.de/sneak/vaultik/pulls/50) — concurrent manifest downloads for [issue #8](https://git.eeqj.de/sneak/vaultik/issues/8).
### Checklist
- ✅ **errgroup pattern**: `errgroup.WithContext(v.ctx)` + `g.SetLimit(maxConcurrentManifestDownloads)` — correct bounded concurrency
- ✅ **Synchronization**: `sync.Mutex` protects shared `results` slice; `snapshots` slice is only mutated single-threaded (before/after errgroup)
- ✅ **`getManifestSize` removed**: Fully deleted. Logic inlined into goroutines using `gctx` (errgroup context) instead of `v.ctx` for proper cancellation — correct design choice over reusing `downloadManifest` which uses `v.ctx`
- ✅ **No magic numbers**: `const maxConcurrentManifestDownloads = 10` with descriptive comment
- ✅ **golang.org/x/sync**: Promoted from indirect → direct in `go.mod`; hash-pinned in `go.sum`
- ✅ **Scope**: Only `go.mod` and `internal/vaultik/snapshot.go` changed — no linter config, test, or Makefile modifications
- ✅ **Go 1.26.1**: Range variable capture is per-iteration — no closure bug
- ✅ **Error propagation**: Single goroutine failure cancels context and returns error, matching original serial behavior
- ✅ **`docker build .`**: All stages pass (lint, fmt-check, tests, build)
Clean implementation. No issues found.
Rebased fix/concurrent-manifest-downloads onto current main (after #39, #41, #49, #55 merges).
Conflict was in internal/vaultik/snapshot.go — main had refactored ListSnapshots into extracted helper methods (buildSnapshotInfoList). Resolved by applying the concurrent errgroup pattern within the new function structure. Also fixed a return-value arity mismatch introduced by the rebase (return err → return nil, err).
Rebased `fix/concurrent-manifest-downloads` onto current `main` (after [#39](https://git.eeqj.de/sneak/vaultik/pulls/39), [#41](https://git.eeqj.de/sneak/vaultik/pulls/41), [#49](https://git.eeqj.de/sneak/vaultik/pulls/49), [#55](https://git.eeqj.de/sneak/vaultik/pulls/55) merges).
Conflict was in `internal/vaultik/snapshot.go` — main had refactored `ListSnapshots` into extracted helper methods (`buildSnapshotInfoList`). Resolved by applying the concurrent errgroup pattern within the new function structure. Also fixed a return-value arity mismatch introduced by the rebase (`return err` → `return nil, err`).
`docker build .` passes: fmt-check ✅, lint (0 issues) ✅, all tests ✅, build ✅.
Reviewed PR #50 — concurrent manifest downloads for issue #8, rebased onto current main.
Checklist
✅Rebase conflict resolution: Concurrent errgroup pattern correctly placed within buildSnapshotInfoList (the extracted helper from the refactored ListSnapshots). Return-value arity (return nil, err) matches the ([]SnapshotInfo, error) signature — no mismatches.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Replace serial
getManifestSize()calls inListSnapshotswith bounded concurrent downloads usingerrgroup. For each remote snapshot not in the local DB, manifest downloads now run in parallel (up to 10 concurrent goroutines) instead of one at a time.Changes
errgroupwithSetLimit(10)for bounded concurrencygetManifestSizehelper (logic inlined into goroutines)golang.org/x/syncfrom indirect to direct dependencyTesting
make checkpasses (fmt-check, lint, tests)docker build .passescloses #8
Review: PASS ✅
Reviewed PR #50 — concurrent manifest downloads for issue #8.
Checklist
errgroup.WithContext(v.ctx)+g.SetLimit(maxConcurrentManifestDownloads)— correct bounded concurrencysync.Mutexprotects sharedresultsslice;snapshotsslice is only mutated single-threaded (before/after errgroup)getManifestSizeremoved: Fully deleted. Logic inlined into goroutines usinggctx(errgroup context) instead ofv.ctxfor proper cancellation — correct design choice over reusingdownloadManifestwhich usesv.ctxconst maxConcurrentManifestDownloads = 10with descriptive commentgo.mod; hash-pinned ingo.sumgo.modandinternal/vaultik/snapshot.gochanged — no linter config, test, or Makefile modificationsdocker build .: All stages pass (lint, fmt-check, tests, build)Clean implementation. No issues found.
588e84da9ctod39d939c5bRebased
fix/concurrent-manifest-downloadsonto currentmain(after #39, #41, #49, #55 merges).Conflict was in
internal/vaultik/snapshot.go— main had refactoredListSnapshotsinto extracted helper methods (buildSnapshotInfoList). Resolved by applying the concurrent errgroup pattern within the new function structure. Also fixed a return-value arity mismatch introduced by the rebase (return err→return nil, err).docker build .passes: fmt-check ✅, lint (0 issues) ✅, all tests ✅, build ✅.Review: PASS ✅ (post-rebase)
Reviewed PR #50 — concurrent manifest downloads for issue #8, rebased onto current
main.Checklist
buildSnapshotInfoList(the extracted helper from the refactoredListSnapshots). Return-value arity (return nil, err) matches the([]SnapshotInfo, error)signature — no mismatches.errgroup.WithContext(v.ctx)+g.SetLimit(maxConcurrentManifestDownloads)— bounded concurrency with proper parent context derivationsync.Mutexprotects sharedresultsslice;snapshotsslice only mutated single-threaded (before/after errgroup)gctx(errgroup-derived context) forv.Storage.Get(), notv.ctx— cancellation propagates correctly on first errorgetManifestSizeremoved: Fully deleted, zero references remain. Logic inlined into goroutines.const maxConcurrentManifestDownloads = 10with descriptive commentgo.modgo.modandinternal/vaultik/snapshot.gochanged — no linter config, test, or Makefile modificationssidcaptured per-iteration — no closure bugg.Wait()returns first error, cancelling remaining goroutines viagctxdocker build .: All stages pass (fmt-check ✅, lint ✅, tests ✅, build ✅)Clean rebase. No issues found.