package store import ( "context" "strings" "testing" "time" "github.com/jackc/pgx/v5/pgconn" ) // A full store (SQLSTATE 53300, "too many clients already") is not asked again every 250 ms: Ready backs off, // doubling its wait up to a bound, and says once that the store is full (issues 1945 and 1277, 2026-10-25 16:08: the // controller's retries kept novox's store full). func TestReadyBacksOffOnAFullStore(t *testing.T) { wasPing, wasWait, wasSay := pingStore, readyWait, readySay t.Cleanup(func() { pingStore, readyWait, readySay = wasPing, wasWait, wasSay }) asked := 0 pingStore = func(context.Context, *Store) error { asked++ if asked <= 8 { return &pgconn.PgError{Code: "53300", Message: "sorry, too many clients already"} } return nil } var waits []time.Duration readyWait = func(_ context.Context, d time.Duration) error { waits = append(waits, d); return nil } var said []string readySay = func(line string) { said = append(said, line) } if err := (&Store{context: "example"}).Ready(context.Background(), time.Hour); err != nil { t.Fatalf("Ready: %v", err) } if len(waits) != 8 { t.Fatalf("waited %d times, want 8: %v", len(waits), waits) } for i := 1; i < len(waits); i++ { if waits[i] < waits[i-1] { t.Fatalf("wait %d (%s) is shorter than the one before (%s): %v", i, waits[i], waits[i-1], waits) } } if waits[1] <= waits[0] { t.Fatalf("Ready does not back off on a full store: %v", waits) } if most := waits[len(waits)-1]; most > fullStoreMostWait { t.Fatalf("the wait grew past its bound %s: %v", fullStoreMostWait, waits) } full := 0 for _, l := range said { if strings.Contains(l, "full") { full++ } } if full != 1 { t.Fatalf("said that the store is full %d times, want once: %q", full, said) } } // A store that does not answer for another reason (not up yet) is still asked again every 250 ms. func TestReadyWaitsShortlyOnAStoreNotUp(t *testing.T) { wasPing, wasWait, wasSay := pingStore, readyWait, readySay t.Cleanup(func() { pingStore, readyWait, readySay = wasPing, wasWait, wasSay }) asked := 0 pingStore = func(context.Context, *Store) error { asked++ if asked <= 3 { return context.DeadlineExceeded } return nil } var waits []time.Duration readyWait = func(_ context.Context, d time.Duration) error { waits = append(waits, d); return nil } readySay = func(string) {} if err := (&Store{context: "example"}).Ready(context.Background(), time.Hour); err != nil { t.Fatalf("Ready: %v", err) } for _, w := range waits { if w != 250*time.Millisecond { t.Fatalf("waits %v, want 250ms each", waits) } } }