Hot-Swapping Mechanism for Dynamically Adjusting SDS Downgrade Strategies: A Deep Dive into Didi SDS
SDS (Service Degrade System) enables zero-downtime strategy updates through a hot-swapping mechanism that pulls fresh downgrade configurations from the admin server every 5 seconds and atomically replaces the in-memory strategy map without restarting the JVM or redefining classes.
The Didi SDS (Service Degrade System) is an open-source Java framework designed to protect microservices through runtime downgrade capabilities. Its hot-swapping mechanism for dynamically adjusting SDS downgrade strategies allows operators to modify thresholds, exception rates, and fallback behaviors in real-time while production traffic continues unaffected. This implementation relies on a combination of Java agent instrumentation, background heartbeat pulling, and atomic memory operations to achieve seamless configuration updates.
Core Components of the Hot-Swapping Architecture
The mechanism consists of four cooperating components that work together to enable dynamic strategy adjustment without application restarts.
SdsBootStrap (Java Agent Entry Point)
Located in sds-bootstrap/src/main/java/com/didiglobal/sds/bootstrap/SdsBootStrap.java, this class serves as the entry point for the Java agent. Its premain() method parses agent arguments, instantiates the singleton SdsClient, and registers the SdsClassFileTransformer for the user-specified package scan path. This establishes the foundation for hot-swapping by injecting the downgrade decision points directly into business methods at startup.
SdsClassFileTransformer (Bytecode Instrumentation)
Found in sds-bootstrap/src/main/java/com/didiglobal/sds/bootstrap/transformer/SdsClassFileTransformer.java, this component instruments methods annotated with @SdsDowngradeMethod. It injects bytecode that wraps method execution with calls to SdsClient.shouldDowngrade(..) before entry and SdsClient.downgradeFinally(..) after completion. This design makes the downgrade logic data-driven—the bytecode remains static while the decision strategy referenced by the client can be hot-swapped at any time.
SdsHeartBeatService (Background Strategy Puller)
Defined in sds-client/src/main/java/com/didiglobal/sds/client/service/SdsHeartBeatService.java, this service runs CyclePullPointStrategyTask in a scheduled thread that executes every 5 seconds. It sends heartbeat requests to the SDS admin server endpoint /sds/heartbeat/pullstrategy, transmitting the current client version and active point list. When the server responds with changed=true, the service triggers the hot-swap sequence.
SdsStrategyService (Atomic Configuration Holder)
Located in sds-client/src/main/java/com/didiglobal/sds/client/service/SdsStrategyService.java, this class maintains a ConcurrentHashMap<String, SdsStrategy> that maps point identifiers to their corresponding strategy executors (VisitStrategyExecutor, TokenBucketStrategyExecutor, etc.). The resetAll() method performs an atomic replacement of the entire map, enabling instant updates visible to all subsequent downgrade checks.
How the Hot-Swapping Loop Works
The hot-swapping mechanism operates through a continuous six-step cycle that ensures strategies remain fresh without interrupting service.
1. Agent Startup and Instrumentation
When launching the JVM with the SDS agent, the premain() method initializes the system:
java -javaagent:/opt/sds/sds-bootstrap-1.0.0.jar=demoGroup,orderService,http://sds-admin:8080,com.example.service
The transformer then processes every method bearing @SdsDowngradeMethod, injecting bytecode equivalent to:
SdsClient __sdsClient = SdsBootStrap.getClient();
if (__sdsClient != null && __sdsClient.shouldDowngrade("POINT")) {
throw new SdsException("POINT", ExceptionCode.DOWNGRADE);
}
// … original method body …
SdsClient ____sdsClient = SdsBootStrap.getClient();
if (____sdsClient != null) {
____sdsClient.downgradeFinally("POINT");
}
This instrumentation ensures that every business method references the global SdsClient for its downgrade decisions.
2. Periodic Strategy Fetching
Every 5 seconds, SdsHeartBeatService executes its pull task:
// Inside SdsHeartBeatService.updatePointStrategyFromWebServer()
if (response.isChanged()) {
ConcurrentHashMap<String, SdsStrategy> newStrategies = new ConcurrentHashMap<>();
for (SdsStrategy s : response.getStrategies()) {
newStrategies.put(s.getPoint(), s);
}
// Hot-swap the whole map atomically
SdsStrategyService.getInstance().resetAll(newStrategies);
// Synchronize fallback return values
SdsDowngradeReturnValueService.getInstance().reset(newStrategies);
}
The service parses the server response into a fresh ConcurrentHashMap, preparing it for atomic installation.
3. Atomic Memory Replacement
The resetAll() method in SdsStrategyService writes the new map reference to a volatile field. Because ConcurrentHashMap is thread-safe and the reference replacement is atomic, all subsequent calls to shouldDowngrade immediately observe the new thresholds. Ongoing requests continue using the old strategy map until they complete, while new requests pick up the configuration instantly.
4. Return-Value Synchronization
SdsDowngradeReturnValueService, located in sds-client/src/main/java/com/didiglobal/sds/client/service/SdsDowngradeReturnValueService.java, performs an identical atomic swap for fallback return values. This ensures that both the downgrade decision logic and the fallback behavior remain consistent after a hot-swap operation.
Practical Implementation Examples
Configuring Business Methods for Hot-Swap
Annotate service methods to enable dynamic downgrade adjustment:
public class OrderService {
@SdsDowngradeMethod(point = "CREATE_ORDER")
public Order createOrder(OrderReq req) {
// business logic …
return orderDao.save(req);
}
}
The point identifier serves as the key for strategy lookup in the hot-swapped map.
Inspecting Current Strategies at Runtime
Debug or monitor the active configuration without stopping the service:
SdsStrategy current = SdsStrategyService.getInstance().getStrategy("CREATE_ORDER");
System.out.println("Current downgradeRate = " + current.getDowngradeRate());
Because the underlying map refreshes atomically, this call always reflects the latest configuration pulled from the admin server.
Summary
- Bytecode instrumentation via
SdsClassFileTransformerembeds downgrade checkpoints that reference dynamic strategy lookups, separating decision logic from business code. - Heartbeat pulling through
SdsHeartBeatServicechecks for configuration updates every 5 seconds via the/sds/heartbeat/pullstrategyendpoint. - Atomic map replacement in
SdsStrategyServiceusesConcurrentHashMapand volatile references to swap entire strategy sets instantly without class redefinition. - Zero-downtime updates allow in-flight requests to complete using old strategies while new requests immediately adopt updated thresholds.
- Synchronized fallbacks via
SdsDowngradeReturnValueServiceensure that return-value strategies hot-swap alongside downgrade decision logic.
Frequently Asked Questions
What triggers a strategy update in SDS?
The SdsHeartBeatService polls the SDS admin server every 5 seconds. When the server response indicates changed=true—typically because an operator modified thresholds through the admin console—the client parses the new strategy definitions and triggers the hot-swap. This pull-based model ensures the client always receives the latest configuration without requiring push notifications.
Does hot-swapping require restarting the Java application?
No. The hot-swapping mechanism for dynamically adjusting SDS downgrade strategies operates entirely at runtime. The Java agent performs one-time bytecode instrumentation during premain(), after which strategies update through in-memory map replacement. No JVM restart, class reloading, or process interruption is required to apply new downgrade thresholds.
How does SDS ensure thread safety during configuration updates?
Thread safety is achieved through two mechanisms. First, SdsStrategyService stores strategies in a ConcurrentHashMap, which provides thread-safe read operations. Second, the resetAll() method replaces the entire map reference atomically using a volatile field. This ensures that all shouldDowngrade() calls see a consistent strategy set, either the old or new configuration, never a partially updated state.
What happens to requests currently executing during a hot-swap?
Requests in flight continue using the strategy map reference they obtained at the start of their execution. Because the old ConcurrentHashMap remains in memory until all references are released, ongoing method involutions complete safely. New requests that begin after the atomic swap immediately query the updated strategy thresholds, achieving zero-downtime configuration changes.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →