feat(routing): add strategy failover controls

This commit is contained in:
elky
2026-09-09 09:12:09 +08:00
parent e58570d79d
commit f2839ae6a7
31 changed files with 1881 additions and 160 deletions
+40 -1
View File
@@ -7,6 +7,7 @@ use crate::actions::{
RoutingAction, RoutingRulePhase, RoutingSchedulingMode, RoutingSetPriorityMode,
};
use crate::conditions::RoutingCondition;
use crate::RoutingFailoverRules;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RoutingSchedulingPreset {
@@ -33,7 +34,7 @@ pub const DEFAULT_STICKY_KEY_ATTEMPTS: u32 = 2;
/// transport configuration. A resolved policy is snapshotted for the request
/// and can therefore be consumed by execution without rereading mutable
/// system settings.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Default)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
pub struct RoutingExecutionPolicy {
#[serde(default, skip_serializing_if = "is_false")]
pub enable_cf_heartbeat: bool,
@@ -41,6 +42,12 @@ pub struct RoutingExecutionPolicy {
pub cyber_continue_failover: bool,
#[serde(default, skip_serializing_if = "is_false")]
pub cancel_on_client_disconnect: bool,
#[serde(default)]
pub max_transfer_count: u64,
#[serde(default)]
pub max_transfer_timeout_seconds: u64,
#[serde(default)]
pub failover_rules: RoutingFailoverRules,
}
impl<'de> Deserialize<'de> for RoutingExecutionPolicy {
@@ -60,6 +67,12 @@ impl<'de> Deserialize<'de> for RoutingExecutionPolicy {
cyber_continue_failover: bool,
#[serde(default)]
cancel_on_client_disconnect: bool,
#[serde(default)]
max_transfer_count: u64,
#[serde(default)]
max_transfer_timeout_seconds: u64,
#[serde(default)]
failover_rules: RoutingFailoverRules,
}
let value = LegacyCompatibleExecutionPolicy::deserialize(deserializer)?;
@@ -69,6 +82,9 @@ impl<'de> Deserialize<'de> for RoutingExecutionPolicy {
|| value.enable_standard_text_sync_heartbeat,
cyber_continue_failover: value.cyber_continue_failover,
cancel_on_client_disconnect: value.cancel_on_client_disconnect,
max_transfer_count: value.max_transfer_count,
max_transfer_timeout_seconds: value.max_transfer_timeout_seconds,
failover_rules: value.failover_rules,
})
}
}
@@ -116,6 +132,29 @@ fn is_false(value: &bool) -> bool {
mod execution_policy_tests {
use super::*;
#[test]
fn routing_failover_configuration_round_trips_and_validates() {
let config: RoutingGroupConfig = serde_json::from_value(serde_json::json!({
"default_policy": {
"max_transfer_count": 3,
"max_transfer_timeout_seconds": 90,
"failover_rules": {
"success_failover_patterns": [{ "pattern": "(?i)capacity" }],
"error_stop_patterns": [{ "status_codes": [400, 413] }]
}
}
}))
.unwrap();
crate::validate_routing_group_config(&config).unwrap();
assert_eq!(config.default_policy.execution_policy.max_transfer_count, 3);
let value = serde_json::to_value(&config).unwrap();
assert_eq!(value["default_policy"]["max_transfer_timeout_seconds"], 90);
assert_eq!(
serde_json::from_value::<RoutingGroupConfig>(value).unwrap(),
config
);
}
#[test]
fn cancellation_defaults_off_and_round_trips_with_legacy_heartbeat() {
let default: RoutingDefaultPolicy = serde_json::from_str("{}").unwrap();