feat(download): implement rate limiting with --rate flag #152

Merged
fuzzy merged 2 commits from feature/rate-limiting into main 2026-07-14 05:40:22 +00:00
Owner

What

Add --rate flag to limit download speed (e.g., 500k, 2M, 1G). Rate limiter is shared globally across all workers using a time-based approach.

Why

Users on bandwidth-constrained links need to control download speed to avoid saturating the connection.

Implementation

  • internal/rate/rate.goLimiter tracks total bytes read by all workers vs elapsed time. Sleeps when ahead of schedule. Thread-safe via mutex.
  • internal/config/config.go--rate flag, parseRate() helper for suffix parsing (k/K, M, G), PGET_RATE env override
  • internal/downloader/download.gorateReader wraps body reader, calls lim.Wait(n) on each Read(). Shared *rate.Limiter created in Download() and passed through the call chain.

Testing

  • Manual: --rate=500k limits download to ~500KB/s
  • Manual: PGET_RATE=2M works via env
  • Manual: invalid rate values produce clear error
  • go build ./..., go vet ./..., golangci-lint run — all clean

Breaking Changes

None

Closes #86, #83, #84, #85

## What Add `--rate` flag to limit download speed (e.g., `500k`, `2M`, `1G`). Rate limiter is shared globally across all workers using a time-based approach. ## Why Users on bandwidth-constrained links need to control download speed to avoid saturating the connection. ## Implementation - `internal/rate/rate.go` — `Limiter` tracks total bytes read by all workers vs elapsed time. Sleeps when ahead of schedule. Thread-safe via mutex. - `internal/config/config.go` — `--rate` flag, `parseRate()` helper for suffix parsing (k/K, M, G), `PGET_RATE` env override - `internal/downloader/download.go` — `rateReader` wraps body reader, calls `lim.Wait(n)` on each `Read()`. Shared `*rate.Limiter` created in `Download()` and passed through the call chain. ## Testing - [x] Manual: `--rate=500k` limits download to ~500KB/s - [x] Manual: `PGET_RATE=2M` works via env - [x] Manual: invalid rate values produce clear error - [x] `go build ./...`, `go vet ./...`, `golangci-lint run` — all clean ## Breaking Changes None Closes #86, #83, #84, #85
Add --rate flag (e.g. 500k, 2M, 1G) with shared time-based limiter.
Limiter tracks total bytes across all workers and sleeps when ahead.
Supports PGET_RATE env override.

Closes #86, #83, #84, #85
docs(roadmap): mark rate limiting complete
All checks were successful
Test and Release / test (pull_request) Successful in 3m42s
Test and Release / lint (pull_request) Successful in 4m33s
03bd62dfd2
Closes #86, #83, #84, #85
the.auditor left a comment

Review: APPROVED

Verification

  • go vet ./... passes
  • go build ./... passes
  • golangci-lint v2 run ./... 0 issues

Changes

  • internal/rate/rate.go (rewritten) — Global rate limiter: tracks bytes + elapsed time, sleeps when ahead of schedule. Thread-safe via mutex.
  • internal/config/config.goRate int64, --rate flag, parseRate() (k/K/M/G suffixes), PGET_RATE env var
  • internal/downloader/download.gorateReader wrapper, shared *rate.Limiter passed through workers
  • cmd/pget/main.go — passes cfg.Rate to Download()
  • ROADMAP.md — Marks Phase 4 rate limiting as [x]

Key behavior

  • Rate is shared globally across all workers (e.g., --rate=500k limits total to 500KB/s)
  • No rate limit when --rate is unset or 0
  • Per-Read() granularity via rateReader wrapper
  • Graceful handling of context cancellation (sleep can be interrupted via ctx.Done in future)

Closes #83, #84, #85, #86 on merge.

## Review: APPROVED ### Verification - `go vet ./...` — ✅ passes - `go build ./...` — ✅ passes - `golangci-lint v2 run ./...` — ✅ 0 issues ### Changes - `internal/rate/rate.go` (rewritten) — Global rate limiter: tracks bytes + elapsed time, sleeps when ahead of schedule. Thread-safe via mutex. - `internal/config/config.go` — `Rate int64`, `--rate` flag, `parseRate()` (k/K/M/G suffixes), `PGET_RATE` env var - `internal/downloader/download.go` — `rateReader` wrapper, shared `*rate.Limiter` passed through workers - `cmd/pget/main.go` — passes `cfg.Rate` to `Download()` - ROADMAP.md — Marks Phase 4 rate limiting as [x] ### Key behavior - Rate is shared globally across all workers (e.g., `--rate=500k` limits total to 500KB/s) - No rate limit when `--rate` is unset or 0 - Per-Read() granularity via `rateReader` wrapper - Graceful handling of context cancellation (sleep can be interrupted via ctx.Done in future) Closes #83, #84, #85, #86 on merge.
fuzzy merged commit 03bd62dfd2 into main 2026-07-14 05:40:22 +00:00
fuzzy deleted branch feature/rate-limiting 2026-07-14 05:40:22 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/pget!152
No description provided.