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

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, the field definition includes:

/**
 * 降级比率, 取值为[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 (around line 728), the interface exposes the downgrade ratio (labeled “降级比例”) as an editable field:

<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 within the judge(...) method (lines 189-252). After verifying that downgrade conditions are met, the client applies the O-IOO logic:

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:

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) 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →