ProductCode & review

Stacked changes

Review small changes that land in order. Nothing waits on a 2,000-line diff.

Split the work into a stack of branches. Each gets its own review, the hub shows where it sits in the stack, and sync lands what merged and restacks the rest.

Sarah Chen
Clamp retry backoff and honour Retry-After0 / 3 viewed0
Review changes
Needs review

Clamp retry backoff and honour Retry-After

#418
OpenPRPriya Raman wants to merge priya/retry-after into main3 files+37−3Updated 6 min ago
Stack2 of 3Copy stack
  1. PR#419Show the retry budget on the checkout dashboardDraft14 min ago
  2. PR#418Clamp retry backoff and honour Retry-AfterNeeds review6 min ago
  3. PR#417Add a retry budget to the payment clientMerged1 day ago
  4. mainTrunk
DescriptionEdit

Caps the exponential backoff at Policy.Max and waits out a 429’s Retry-After instead of retrying into it.

Activity
  • Priya Raman pushed e41b9d2 Honour Retry-After on 4291 hr ago
  • Priya Raman pushed a1f4c2e Cap backoff at Policy.Max6 min ago
WritePreview
Leave a comment
Comment
services/checkout/client.go+2−1goViewed
57 lines@@ func (c *Client) Do(req *http.Request) (*http.Response, error) {
++58 resp, err := c.http.Do(req)58 resp, err := c.http.Do(req)
++59 if err != nil {59 if err != nil {
++60 return nil, err60 return nil, err
++61 }61 }
++62 if resp.StatusCode >= 500 {62 if retryable(resp.StatusCode) {
+63 c.after = retryAfter(resp.Header)
++63 return resp, errRetry64 return resp, errRetry
++64 }65 }
to end of file
services/checkout/retry.go+12−2goViewed
40 lines@@ func (p *Policy) delay(n int) time.Duration {
++41func (p *Policy) delay(n int) time.Duration {41func (p *Policy) delay(n int) time.Duration {
++42 d := p.Base << n42 d := p.Base << n
++43 if d > p.Max {43 if d <= 0 || d > p.Max {
++44 d = p.Max44 d = p.Max
++45 }45 }
++46 return d + jitter(d)46 return d + jitter(d/2)
++47}47}
+48
+49// retryAfter reads a 429's Retry-After.
+50func retryAfter(h http.Header) time.Duration {
+51 v := h.Get("Retry-After")
+52 secs, err := strconv.Atoi(v)
+53 if err != nil || secs < 0 {
+54 return 0
+55 }
+56 return time.Duration(secs) * time.Second
+57}
to end of file
services/checkout/retry_test.go+23−0goViewed
+1package checkout
+2
+3import (
+4 "net/http"
+5 "testing"
+6 "time"
+7)
+8
+9func TestDelayStaysInsideMax(t *testing.T) {
+10 p := Policy{Base: time.Second, Max: 30 * time.Second}
+11 for n := 0; n < 64; n++ {
+12 if d := p.delay(n); d <= 0 || d > p.Max+p.Max/2 {
+13 t.Fatalf("delay(%d) = %v, outside (0, %v]", n, d, p.Max)
+14 }
+15 }
+16}
+17
+18func TestRetryAfterSeconds(t *testing.T) {
+19 h := http.Header{"Retry-After": []string{"7"}}
+20 if got := retryAfter(h); got != 7*time.Second {
+21 t.Fatalf("retryAfter = %v, want 7s", got)
+22 }
+23}

How you use it

Stacked changes, step by step.

  1. 01Stacked changes

    Split as you go

    Start the next branch on top of the current one and keep working. The parent is stored in git itself, under refs/branch-metadata, in the same shape Graphite uses, so a clone carries the stack and no service has to remember it.

  2. 02Stacked changes

    Submit: one change request per branch

    Submit pushes the stack and opens or updates a change request for each branch. The review page reads the stack from git, shows “2 of 2”, links the neighbours, and offers the whole chain as markdown to paste anywhere.

  3. 03Stacked changes

    The agent’s loop: a stack per task, reviews that stay honest

    An agent builds its task as a stack too, and submits it the same way. Approvals pin to the commit they judged, so when the agent pushes again after a review, the approval reads outdated and the reviewer sees exactly what changed since. Threads on a superseded commit collapse, because stacking force-pushes constantly.

  4. 04Stacked changes

    Land in order

    A fix that belongs three branches down goes there with absorb, and everything above is restacked. Sync pulls trunk, deletes what landed and restacks what is left, keeping the reviews that still apply. Three 200-line reviews get read today; one 600-line review waits for Friday.

Coming soon: a merge queue; a stack lands one branch at a time from the review page · squash and rebase merge options

The full reference lives in the docs.

Works with

Boots in seconds. Your team joins with a link.