Commit af315f4
fix(cli): resolve buckets migrate stalls and overhaul progress display (#202)
* fix(cli): sweep whole in-flight set in migrate to prevent deadlock
buckets migrate schedules server-side pulls into an in-flight queue capped
by bytes, then polls isMigrated() to free completed ones. drainCompleted
only checked the oldest CONCURRENCY items, but migrations don't complete in
FIFO order — a slow/large object at the head hid the completed objects
behind it, so their bytes were never freed. inFlightBytes stayed pinned at
the cap, the scheduler's throttle never released, and the migration
deadlocked (progress frozen, in-flight stuck at ~10 GB) until restarted.
Poll a rotating window across the entire in-flight set (cursor advances
each call) so completions anywhere are observed and their bytes freed, and
only back off once a full sweep finds nothing. Adds unit tests covering
out-of-order completion behind a stuck head.
Assisted-by: Opus 4.8 via Claude Code
* feat(cli): improve migrate large-file UX (order, caps, progress)
Follow-on to the drain deadlock fix, for the "feels blocked on a large
file" experience:
- Migrate smallest objects first so progress climbs quickly and large
files finish at the end (orderForMigration).
- Bound in-flight by object count as well as bytes (MAX_IN_FLIGHT_OBJECTS)
so runs with millions of small files don't balloon the poll set; a
single file larger than the whole byte budget is still admitted once the
queue empties (atCapacity).
- Richer progress line: throughput (obj/s, bytes/s), in-flight object
count, and the oldest in-flight object (name/size/age) once it has been
pulling a while — so a slow large file is named instead of looking stuck.
Adds unit tests for orderForMigration, atCapacity, and oldestInFlight.
Assisted-by: Opus 4.8 via Claude Code
* fix(cli): enforce in-flight byte budget across the schedule batch
The throttle checked capacity against state.inFlight, but scheduled objects
sit in an unflushed `batch` (uncounted) until it reaches SCHEDULE_BATCH_SIZE
or the loop ends. A run with only a couple of objects therefore flushed
them all at once, ignoring the byte budget — e.g. an 877 MB file and a
~21.9 GB file were scheduled together (22.8 GB in flight, over the 10 GB
cap), so the large file contended with the small one and both stalled.
Track the batch's bytes and flush it before adding an item that would
exceed the budget (or the object cap), via shouldFlushBatch. The small
file is now scheduled and confirmed on its own first, then the large file
runs alone. Adds unit tests for shouldFlushBatch.
Assisted-by: Opus 4.8 via Claude Code
* feat(cli): multi-line, responsive migrate progress
Render migrate progress as a sticky multi-line block (bucket + elapsed,
file and byte percentages, current activity and in-flight count) that
redraws in place instead of duplicating lines on window resize or Ctrl-C.
Back off the isMigrated poll (5s up to 30s, reset on completion) so an idle
migration stops hammering the gateway, make the poll wait abortable so
Ctrl-C is felt immediately (a second Ctrl-C force-quits), and keep the
spinner and clock live on a render timer.
Assisted-by: Opus 4.8 via Claude Code
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>1 parent e64d887 commit af315f4
3 files changed
Lines changed: 586 additions & 49 deletions
File tree
- .changeset
- packages/cli
- src/lib/buckets
- test/lib/buckets
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
0 commit comments