Skip to content

Commit af315f4

Browse files
designcodeclaude
andauthored
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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
"@tigrisdata/cli": patch
3+
---
4+
5+
Fix a deadlock in `tigris buckets migrate` that stalled large migrations, and
6+
overhaul how migrations are paced and displayed.
7+
8+
- **Deadlock fix:** the drain step only polled the oldest in-flight objects, so
9+
a slow object at the head hid the completed objects behind it — their bytes
10+
were never freed, the in-flight budget stayed pinned at its cap, and the
11+
migration wedged (progress frozen with in-flight stuck at ~10 GB). It now
12+
polls a rotating window across the whole in-flight set, so completions are
13+
observed regardless of position.
14+
- **Smallest-first:** objects migrate smallest-first, so progress climbs quickly
15+
and large files finish at the end instead of stalling mid-run.
16+
- **In-flight caps:** in-flight work is bounded by both object count and total
17+
bytes, and the byte budget is enforced across the pending schedule batch, so a
18+
large file can't be scheduled alongside a full batch and blow the budget.
19+
- **Poll backoff:** the `isMigrated` poll backs off (5s up to 30s) after sweeps
20+
where nothing completed, and resets on the next completion, so an idle
21+
migration stops hammering the gateway with status checks.
22+
- **Multi-line, live progress:** progress renders as a sticky multi-line block —
23+
bucket and elapsed clock, file and byte percentages, and the file currently
24+
being pulled (name, size, and how long it has been going) plus the in-flight
25+
count. It redraws in place instead of duplicating lines on window resize, and
26+
truncates each line to the terminal width so nothing wraps. There is no
27+
throughput figure: confirmations are lumpy binary flips (the gateway does the
28+
transfer), not a byte stream, so an "obj/s · MB/s" rate would misrepresent
29+
progress.
30+
- **Responsive cancel:** Ctrl-C stops scheduling and polling and prints a
31+
summary of what was confirmed; objects already scheduled remain queued for
32+
migration server-side, so re-running resumes from there. It is felt
33+
immediately (the poll wait is abortable rather than blocking until it
34+
elapses), and a second Ctrl-C forces an immediate exit.

0 commit comments

Comments
 (0)