# How the Proxy Backoff Algorithm Handles a Complete Proxy Pool Failure in biliTickerBuy

> Learn how the Proxy Backoff algorithm in biliTickerBuy handles proxy pool failure. It retries with exponential backoff capped at 600 seconds, notifying users until recovery.

- Repository: [Qizhuo Xie/biliTickerBuy](https://github.com/mikumifa/biliTickerBuy)
- Tags: internals
- Published: 2026-06-23

---

**The `ProxyBackoff` class implements an exponential backoff capped at 600 seconds, delivering a single user notification while continuing to retry until the proxy pool recovers.**

The `biliTickerBuy` repository is an open-source ticket purchasing automation tool that relies heavily on proxy pools to distribute requests. When every proxy in the pool becomes unavailable simultaneously—a complete proxy pool failure—the application must avoid crashing or hammering dead services while maintaining purchase attempts.

## Core Implementation in the ProxyBackoff Class

The proxy backoff algorithm is encapsulated in [`util/proxy/ProxyBackoff.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/util/proxy/ProxyBackoff.py). This class is instantiated for each buying session in [`task/buy.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy.py) and manages the timing and user notifications when proxy requests fail consecutively.

### Exponential Delay Growth

The algorithm calculates the next retry delay using exponential growth bounded by a maximum ceiling. The implementation in `next_delay_seconds()` follows this logic:

```python
delay = int(round(self.base_seconds * (self.factor ** self.exhausted_rounds)))
self.exhausted_rounds += 1
return min(delay, self.max_seconds)

```

The parameters are configurable:
- **base_seconds**: Starting delay, defaulting to **30 seconds**
- **factor**: Multiplier applied after each failure, fixed at **2.0**
- **max_seconds**: Upper ceiling for delays, defaulting to **600 seconds** (10 minutes)

This means the first failure waits ~30 seconds, the second waits ~60 seconds, the third ~120 seconds, and so on, until hitting the 600-second cap.

### Reset on Success

When a request finally succeeds after obtaining a fresh proxy, the calling code invokes `proxy_backoff.reset()` (called from [`task/buy_helpers.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy_helpers.py)). This method clears the `exhausted_rounds` counter and the internal notification flag, restoring the backoff to its initial state and allowing immediate rapid retries for subsequent requests.

### Single-Shot Notification

The `should_notify()` method returns `True` only the first time it is called after a failure series begins. This ensures the UI displays a **"all proxies failed"** warning exactly once per outage cycle, preventing log spam or repetitive alerts while the system continues its backoff loop.

## Integration with the Buying Workflow

In the main buying loop (found in [`task/buy_helpers.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy_helpers.py)), the algorithm integrates with request handling through a consistent pattern:

```python
delay_seconds = proxy_backoff.next_delay_seconds()
if proxy_backoff.should_notify():
    # Trigger UI warning: "All proxies are currently unavailable"

time.sleep(delay_seconds)

```

When a complete proxy pool failure occurs, this integration ensures the application:
- Continues retrying after increasingly longer sleeps (capped at `max_seconds`)
- Warns the user exactly once via the UI or logs
- Avoids overwhelming dead proxy services with rapid reconnection attempts

## Configuration and Defaults

The configurable limits are defined in [`interface/config.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/interface/config.py), where `proxy_backoff_max_seconds` defaults to 600 seconds. Users can adjust `base_seconds` and `max_seconds` when instantiating the `ProxyBackoff` class to match their specific proxy pool recovery characteristics or network conditions.

## Practical Usage Example

The following pattern demonstrates how to implement the proxy backoff algorithm in custom polling logic:

```python
from util.proxy.ProxyBackoff import ProxyBackoff
import time

# Initialize with custom limits

backoff = ProxyBackoff(base_seconds=20, factor=2.0, max_seconds=300)

while True:
    try:
        response = send_request_via_proxy()
        backoff.reset()  # Clear counters on success

        break
    except ProxyError:
        wait = backoff.next_delay_seconds()
        if backoff.should_notify():
            print("⚠️ All proxies are currently unavailable.")
        print(f"Retrying in {wait} seconds...")
        time.sleep(wait)

```

This pattern mirrors the production implementation in [`task/buy_helpers.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy_helpers.py), ensuring graceful degradation during total proxy outages.

## Summary

- The `ProxyBackoff` class in [`util/proxy/ProxyBackoff.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/util/proxy/ProxyBackoff.py) provides exponential backoff with configurable base delay (30s default) and maximum cap (600s default).
- Delays double after each consecutive failure (`factor=2.0`) until reaching the maximum threshold.
- The `reset()` method restores initial state upon successful requests, while `should_notify()` ensures users receive only one alert per outage cycle.
- Integration in [`task/buy.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy.py) and [`task/buy_helpers.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy_helpers.py) prevents application crashes during complete proxy pool failures while respecting rate limits.

## Frequently Asked Questions

### What happens when all proxies fail simultaneously in biliTickerBuy?

The application enters a backoff loop where it waits for exponentially increasing intervals (starting at 30 seconds, capped at 600 seconds) and displays a single warning notification. It continues retrying until a proxy becomes available again rather than terminating the session.

### How does the proxy backoff algorithm calculate retry delays?

The algorithm uses the formula `base_seconds * (factor ^ exhausted_rounds)`, rounded to the nearest integer. With default values, this produces delays of 30s, 60s, 120s, 240s, etc., until hitting the `max_seconds` ceiling defined in [`interface/config.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/interface/config.py).

### Can I customize the maximum wait time for proxy retries?

Yes, the `max_seconds` parameter is configurable when creating a `ProxyBackoff` instance. The default value of 600 seconds can be overridden in the constructor or adjusted in the application configuration to match your proxy pool's expected recovery time.

### Where does the proxy backoff reset occur after failures?

The reset happens in [`task/buy_helpers.py`](https://github.com/mikumifa/biliTickerBuy/blob/main/task/buy_helpers.py) immediately after a successful request completes. Calling `proxy_backoff.reset()` clears the `exhausted_rounds` counter and the notification flag, allowing the next failure sequence to start fresh from the base delay.