# How SDS Implements the 0-100 Downgrade Ratio for Canary Releases

> Discover how SDS implements the 0-100 downgrade ratio for zero-downtime canary releases. Learn to precisely control traffic redirection to fallback implementations.

- Repository: [DiDi/sds](https://github.com/didi/sds)
- Tags: deep-dive
- Published: 2026-02-28

---

**The SDS (Service Downgrade System) uses a probabilistic downgrade ratio between 0 and 100 to determine the exact percentage of traffic that gets redirected to fallback implementations, enabling zero-downtime canary releases without additional orchestration infrastructure.**

The `didi/sds` repository implements traffic splitting through a configurable **downgrade ratio** stored in strategy configurations. This integer value—ranging from 0 (no downgrade) to 100 (always downgrade)—allows operators to perform gradual rollouts by controlling the probability that any single request triggers the fallback path.

## Understanding the O-IOO Pattern

SDS documentation refers to the downgrade mechanism as the **O-IOO** pattern, representing the 0-100 percentage scale. When a protected point triggers a downgrade rule (due to latency spikes, error rates, or manual configuration), the system consults this ratio to decide whether to execute the fallback implementation.

The configuration persists in the `point_strategy` table (`downgrade_rate` column) and propagates to clients through heartbeat updates. In [`sds-client/src/main/java/com/didiglobal/sds/client/bean/SdsStrategy.java`](https://github.com/didi/sds/blob/main/sds-client/src/main/java/com/didiglobal/sds/client/bean/SdsStrategy.java), the field definition includes:

```java
/**
 * 降级比率, 取值为[0-100]
 * 例如：值为15表示每100笔请求将有15笔被拒绝掉
 * 默认值：100
 */
private Integer downgradeRate = 100;

public Integer getDowngradeRate() {
    return downgradeRate;
}
public void setDowngradeRate(Integer downgradeRate) {
    this.downgradeRate = downgradeRate;
}

```

## Configuring the Downgrade Ratio

Operators configure the ratio through the SDS admin UI, which persists the value to the database and distributes it to client applications.

### Frontend Configuration

In [`sds-front/src/views/DemotePoint.vue`](https://github.com/didi/sds/blob/main/sds-front/src/views/DemotePoint.vue) (around line 728), the interface exposes the **downgrade ratio** (labeled “降级比例”) as an editable field:

```vue
<el-form-item label="降级比例">
  <el-input v-model="downgradeRatio"></el-input>
</el-form-item>

```

When saved, the admin API persists this value via `PointStrategyDao` to the `downgrade_rate` column.

### Client Synchronization

Every client heartbeat (`CommonSdsClient.heartbeat(...)`) fetches the latest `SdsStrategy` objects, ensuring runtime adjustments to the ratio take effect without application restarts.

## Runtime Probabilistic Logic

The core implementation resides in [`sds-client/src/main/java/com/didiglobal/sds/client/CommonSdsClient.java`](https://github.com/didi/sds/blob/main/sds-client/src/main/java/com/didiglobal/sds/client/CommonSdsClient.java) within the `judge(...)` method (lines 189-252). After verifying that downgrade conditions are met, the client applies the O-IOO logic:

```java
if (strategy.getDowngradeRate() >= 100) {
    needDowngrade = true;                     // 100% → always downgrade
} else {
    needDowngrade = ThreadLocalRandom.current()
                     .nextInt(100) < strategy.getDowngradeRate(); // O-IOO
}

```

**How the probability works:**
- If `downgradeRate` is **≥ 100**, every request enters the fallback path
- If `downgradeRate` is **0**, no requests are downgraded
- For values **1-99**, `ThreadLocalRandom.current().nextInt(100)` generates a uniform integer in `[0, 99]`. The request downgrades only when this random value is strictly less than the configured rate, achieving exact percentage-based traffic splitting.

## Canary Release Application

The probabilistic nature of the **SDS downgrade ratio** transforms the downgrade mechanism into a built-in canary release tool. By configuring a small ratio (e.g., 10) for a new version’s point, only 10% of qualifying traffic actually executes the fallback—which can contain the new implementation—while 90% continues using the stable path.

This approach provides several advantages for gray releases:
- **No sticky session requirements**: Each request evaluates independently using `ThreadLocalRandom`, distributing traffic evenly across instances
- **Instant rollback**: Changing the ratio to 0 immediately stops all canary traffic
- **Gradual scaling**: Operators can increment the ratio (10 → 25 → 50 → 100) to monitor system behavior at each stage

Typical usage patterns invoke `shouldDowngrade(point)` before routing:

```java
if (sdsClient.shouldDowngrade(point)) {
    // Canary path: invoke new implementation or return fallback value
    return newVersionResult;
}
// Standard path: continue with original implementation

```

## Summary

- The **SDS downgrade ratio** is an integer field (`downgradeRate`) in `SdsStrategy` defaulting to 100, representing the percentage of traffic to redirect
- `CommonSdsClient.judge(...)` implements the O-IOO logic using `ThreadLocalRandom.current().nextInt(100)` for probabilistic selection
- Configuration flows from the Vue frontend ([`DemotePoint.vue`](https://github.com/didi/sds/blob/main/DemotePoint.vue)) through `PointStrategyDao` to client heartbeats
- Ratios between 1-99 enable canary releases by probabilistically splitting traffic between original and fallback implementations at the request level
- Setting the ratio to 0 disables downgrade entirely, while 100 forces all qualifying requests to the fallback path

## Frequently Asked Questions

### What does the O-IOO pattern mean in SDS context?

O-IOO is a stylized representation of the **0-100** percentage range used to describe the downgrade ratio configuration. It indicates that the system accepts any integer value between 0 (no traffic downgraded) and 100 (all traffic downgraded), allowing precise control over fallback traffic volume.

### How does the client receive updated downgrade ratio values?

The `CommonSdsClient` establishes a heartbeat connection with the SDS admin server, periodically fetching updated `SdsStrategy` objects. When an operator modifies the **downgrade ratio** through the UI, the new value persists to the database and propagates to all connected clients during the next heartbeat cycle, typically taking effect within seconds.

### Can I use the downgrade ratio for A/B testing different implementations?

Yes. Because the ratio uses `ThreadLocalRandom` for per-request decisions, you can implement A/B testing by configuring fallback paths that route to variant implementations. Setting a ratio of 50 provides roughly even traffic split, though truly sticky user sessions would require additional hashing logic not provided by the base SDS implementation.

### What happens if the downgrade ratio is set to 0?

When `downgradeRate` is 0, the condition `ThreadLocalRandom.current().nextInt(100) < 0` always evaluates to false. Consequently, `needDowngrade` remains false even when other strategy rules trigger, effectively disabling the downgrade mechanism for that point while preserving the configuration for future adjustments.