Skip to content

feat: add two-phase progress bar for large model operations#474

Open
aftersnow wants to merge 6 commits intomainfrom
feature/progress-bar-phases
Open

feat: add two-phase progress bar for large model operations#474
aftersnow wants to merge 6 commits intomainfrom
feature/progress-bar-phases

Conversation

@aftersnow
Copy link
Copy Markdown
Contributor

Summary

  • Fix progress bar showing 0kb/s during SHA256 hashing (build) and blob existence checks (push) for large model files
  • Build now shows: "Hashing layer" (SHA256 phase) → "Building layer" (storage write phase)
  • Push now shows: "Checking blob" (HEAD request) → "Pushing blob" (upload phase)
  • Fix data race on bar.msg between Complete() and mpb render goroutine (stringatomic.Value)
  • Fix TOCTOU race in Add() lock pattern (RLockRUnlockLock → single Lock)

Changes

File Change
internal/pb/pb.go Fix data race + lock pattern, add Reset method
internal/pb/pb_test.go 13 new tests (functional, concurrency, error/idempotency)
pkg/backend/build/hooks/hooks.go Add OnHash callback for digest computation
pkg/backend/build/builder.go Wire onHash into computeDigestAndSize with TeeReader
pkg/backend/build/builder_test.go 3 new tests for computeDigestAndSize
pkg/backend/processor/base.go Wire OnHash + Reset in processor hooks
pkg/backend/push.go Two-phase Checking/Pushing display

Test plan

  • go test ./internal/pb/... -race -v — 13 tests pass, race detector clean
  • go test ./pkg/backend/build/... -v — all builder tests pass
  • go test ./pkg/backend/processor/... -v — all processor tests pass
  • go test ./pkg/... -count=1 — full package suite pass
  • go build ./... — compiles successfully
  • Manual E2E: build + push >8GB model on remote test machine

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces an --include flag for the modelfile generate command, allowing users to explicitly include files or directories (such as hidden files) that are normally skipped. The path filtering logic has been migrated to use the doublestar library, enabling support for recursive glob patterns. Additionally, the PR enhances progress bar tracking by adding 'Hashing' and 'Checking' phases and improves thread safety for progress messages using atomic values. Feedback suggests simplifying the hashing implementation in builder.go by using io.Copy directly with the hash object instead of a TeeReader and io.Discard.

Comment thread pkg/backend/build/builder.go Outdated
Comment on lines +269 to +270
tee := io.TeeReader(wrappedReader, hash)
size, err := io.Copy(io.Discard, tee)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of io.TeeReader with io.Copy and io.Discard to compute the hash and get the size is functionally correct, but unnecessarily complex. Since hash.Hash implements io.Writer, you can simplify this by copying directly from the wrappedReader to the hash instance. This is more idiomatic and easier to read.

Suggested change
tee := io.TeeReader(wrappedReader, hash)
size, err := io.Copy(io.Discard, tee)
size, err := io.Copy(hash, wrappedReader)

Comment thread cmd/modelfile/generate.go
flags.StringVarP(&generateConfig.Provider, "provider", "p", "", "explicitly specify the provider for short-form URLs (huggingface, modelscope)")
flags.StringVar(&generateConfig.DownloadDir, "download-dir", "", "custom directory for downloading models (default: system temp directory)")
flags.StringArrayVar(&generateConfig.ExcludePatterns, "exclude", []string{}, "specify glob patterns to exclude files/directories (e.g. *.log, checkpoints/*)")
flags.StringArrayVar(&generateConfig.IncludePatterns, "include", []string{},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems not regarding this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — rebased the branch to drop those unrelated commits. This file is no longer modified in the PR.

…ions

- Change progressBar.msg from string to atomic.Value to fix data race
  between Complete() and mpb render goroutine
- Change Add() from RLock-check-RUnlock-Abort-Lock-write to full write
  lock to eliminate TOCTOU race on same-name bars
- Add Reset() method as semantic alias for Add() to support phase
  transitions (Hashing -> Building, Checking -> Pushing)
- Add 13 unit tests: 6 functional, 3 concurrency (-race), 4 error/idempotency

Signed-off-by: Zhao Chen <zhaochen.zju@gmail.com>
Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
Add OnHashFunc type and OnHash field to Hooks struct. Default OnHash
passes reader through unchanged. Add WithOnHash option function.

Signed-off-by: Zhao Chen <zhaochen.zju@gmail.com>
Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
…ss tracking

- computeDigestAndSize now accepts onHash callback to wrap reader with
  progress tracking before SHA256 hashing via TeeReader
- BuildLayer computes relPath once and passes it to both onHash (via
  closure) and OutputLayer, ensuring bar name consistency across phases
- Add 3 tests: onHash called with correct size, wrapped reader preserves
  digest correctness, reader failure propagation

Signed-off-by: Zhao Chen <zhaochen.zju@gmail.com>
Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
- Add WithOnHash hook calling tracker.Add("Hashing layer") for digest
  computation phase
- Change WithOnStart from tracker.Add to tracker.Reset("Building layer")
  for storage write phase transition

Signed-off-by: Zhao Chen <zhaochen.zju@gmail.com>
Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
- Remove prompt parameter from pushIfNotExist, phases now hardcoded
- Phase 1: show "Checking blob" with nil reader during dst.Exists()
- Phase 2: reset to "Pushing blob" with content reader for upload
- Add pb.Abort on Exists() and PullBlob() error paths
- Manifest/config pushes unchanged (small payloads, no phases needed)

Signed-off-by: Zhao Chen <zhaochen.zju@gmail.com>
Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
Simplify computeDigestAndSize by copying directly to the sha256
writer instead of using io.TeeReader + io.Discard. hash.Hash
already implements io.Writer, making the tee unnecessary.

Addresses review feedback on PR #474.

Signed-off-by: Zhao Chen <winters.zc@antgroup.com>
@aftersnow aftersnow force-pushed the feature/progress-bar-phases branch from 1f183ab to 70d8013 Compare April 23, 2026 03:47
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.

2 participants