refactor: extract runtime state backends

This commit is contained in:
fawney19
2026-05-08 00:18:12 +08:00
parent 6f620d92be
commit 6247ac3edc
111 changed files with 4358 additions and 3203 deletions
+1
View File
@@ -13,6 +13,7 @@ aether-contracts.workspace = true
aether-gateway.workspace = true
aether-http.workspace = true
aether-runtime.workspace = true
aether-runtime-state.workspace = true
axum.workspace = true
bytes.workspace = true
http.workspace = true
@@ -6,12 +6,12 @@ use aether_data::driver::postgres::{
DatabaseRecordId, PostgresLeaseClaimOptions, PostgresLeaseClaimSpec, PostgresLeaseRunnerConfig,
PostgresPoolConfig,
};
use aether_data::driver::redis::{
RedisClientConfig, RedisConsumerGroup, RedisConsumerName, RedisLockLease, RedisLockRunner,
RedisLockRunnerConfig, RedisStreamName, RedisStreamReclaimConfig, RedisStreamRunner,
RedisStreamRunnerConfig,
use aether_data::PostgresBackend;
use aether_runtime_state::{
RedisClientConfig, RedisClientFactory, RedisConsumerGroup, RedisConsumerName, RedisKeyspace,
RedisLockLease, RedisLockRunner, RedisLockRunnerConfig, RedisStreamName,
RedisStreamReclaimConfig, RedisStreamRunner, RedisStreamRunnerConfig,
};
use aether_data::{PostgresBackend, RedisBackend};
use aether_testkit::{init_test_runtime_for, ManagedPostgresServer, ManagedRedisServer};
use futures_util::stream::{self, StreamExt};
use serde::Serialize;
@@ -189,10 +189,12 @@ async fn run_suite(
})
.expect("postgres url should resolve");
let redis_backend = RedisBackend::from_config(RedisClientConfig {
let redis_factory = RedisClientFactory::new(RedisClientConfig {
url: redis_url.clone(),
key_prefix: Some(format!("aether-dependency-pressure-{}", std::process::id())),
})?;
let redis_client = redis_factory.connect_lazy()?;
let redis_keyspace = redis_factory.config().keyspace();
let postgres_backend = PostgresBackend::from_config(PostgresPoolConfig {
database_url: postgres_url.clone(),
min_connections: 1,
@@ -206,22 +208,30 @@ async fn run_suite(
bootstrap_postgres_lease_table(postgres_backend.pool_clone(), config).await?;
let lock_runner = redis_backend.lock_runner(RedisLockRunnerConfig {
command_timeout_ms: Some(config.timeout.as_millis() as u64),
default_ttl_ms: 5_000,
})?;
let stream_runner = redis_backend.stream_runner(RedisStreamRunnerConfig {
command_timeout_ms: Some(config.timeout.as_millis() as u64),
read_block_ms: Some(10),
read_count: 64,
})?;
let lock_runner = RedisLockRunner::new(
redis_client.clone(),
redis_keyspace.clone(),
RedisLockRunnerConfig {
command_timeout_ms: Some(config.timeout.as_millis() as u64),
default_ttl_ms: 5_000,
},
)?;
let stream_runner = RedisStreamRunner::new(
redis_client.clone(),
redis_keyspace.clone(),
RedisStreamRunnerConfig {
command_timeout_ms: Some(config.timeout.as_millis() as u64),
read_block_ms: Some(10),
read_count: 64,
},
)?;
let lease_runner = postgres_backend.lease_runner(PostgresLeaseRunnerConfig {
statement_timeout_ms: Some(config.timeout.as_millis() as u64),
lock_timeout_ms: Some(1_000),
})?;
let redis_lock = benchmark_redis_lock(&redis_backend, &lock_runner, config).await?;
let redis_stream = benchmark_redis_stream(&redis_backend, &stream_runner, config).await?;
let redis_lock = benchmark_redis_lock(&redis_keyspace, &lock_runner, config).await?;
let redis_stream = benchmark_redis_stream(&redis_keyspace, &stream_runner, config).await?;
let postgres_lease = benchmark_postgres_lease(&lease_runner, config).await?;
Ok(DependencyPressureBaselineReport {
@@ -263,7 +273,7 @@ async fn bootstrap_postgres_lease_table(
}
async fn benchmark_redis_lock(
backend: &RedisBackend,
keyspace: &RedisKeyspace,
runner: &RedisLockRunner,
config: &DependencyPressureBaselineConfig,
) -> Result<RedisLockPressureReport, Box<dyn std::error::Error>> {
@@ -271,7 +281,6 @@ async fn benchmark_redis_lock(
let renew = Arc::new(SummaryCollector::default());
let release = Arc::new(SummaryCollector::default());
let next = Arc::new(std::sync::atomic::AtomicUsize::new(0));
let keyspace = backend.keyspace();
stream::iter(0..config.redis_lock_concurrency)
.for_each_concurrent(config.redis_lock_concurrency, |_| {
@@ -338,11 +347,11 @@ async fn record_redis_lock_follow_up(
}
async fn benchmark_redis_stream(
backend: &RedisBackend,
keyspace: &RedisKeyspace,
runner: &RedisStreamRunner,
config: &DependencyPressureBaselineConfig,
) -> Result<RedisStreamPressureReport, Box<dyn std::error::Error>> {
let stream = backend.keyspace().stream_name("dependency-pressure");
let stream = keyspace.stream_name("dependency-pressure");
let group = RedisConsumerGroup("dependency-group".to_string());
let consumer_a = RedisConsumerName("consumer-a".to_string());
let consumer_b = RedisConsumerName("consumer-b".to_string());
@@ -7,8 +7,10 @@ use aether_data::driver::postgres::{
PostgresLeaseClaimOptions, PostgresLeaseClaimSpec, PostgresLeaseRunnerConfig,
PostgresPoolConfig, PostgresTransactionOptions,
};
use aether_data::driver::redis::{RedisClientConfig, RedisLockRunnerConfig};
use aether_data::{DataLayerError, PostgresBackend, RedisBackend};
use aether_data::{DataLayerError, PostgresBackend};
use aether_runtime_state::{
RedisClientConfig, RedisClientFactory, RedisLockRunner, RedisLockRunnerConfig,
};
use aether_testkit::{
init_test_runtime_for, reserve_local_port, ManagedPostgresServer, ManagedRedisServer,
TunnelHarness, TunnelHarnessConfig,
@@ -235,15 +237,20 @@ async fn benchmark_redis_restart_recovery(
config: &FailureRecoveryBaselineConfig,
) -> Result<RecoverySummary, Box<dyn std::error::Error>> {
let redis_url = redis_server.lock().await.redis_url().to_string();
let backend = RedisBackend::from_config(RedisClientConfig {
let factory = RedisClientFactory::new(RedisClientConfig {
url: redis_url,
key_prefix: Some(format!("aether-failure-recovery-{}", std::process::id())),
})?;
let runner = backend.lock_runner(RedisLockRunnerConfig {
command_timeout_ms: Some(250),
default_ttl_ms: 1_000,
})?;
let keyspace = backend.keyspace();
let client = factory.connect_lazy()?;
let keyspace = factory.config().keyspace();
let runner = RedisLockRunner::new(
client,
keyspace.clone(),
RedisLockRunnerConfig {
command_timeout_ms: Some(250),
default_ttl_ms: 1_000,
},
)?;
let collector = Arc::new(RecoveryCollector::default());
let next_attempt = Arc::new(AtomicUsize::new(0));
let phase = Arc::new(AtomicUsize::new(0));
@@ -4,7 +4,9 @@ use std::sync::Arc;
use std::time::{Duration, Instant};
use aether_contracts::{ExecutionPlan, ExecutionTimeouts, RequestBody};
use aether_runtime::{DistributedConcurrencyGate, RedisDistributedConcurrencyConfig};
use aether_runtime_state::{
RedisClientConfig, RuntimeSemaphore, RuntimeSemaphoreConfig, RuntimeState,
};
use aether_testkit::{
init_test_runtime_for, run_multi_url_http_load_probe, ExecutionRuntimeHarness,
ExecutionRuntimeHarnessConfig, GatewayHarness, GatewayHarnessConfig, HttpLoadProbeConfig,
@@ -165,13 +167,15 @@ async fn start_gateway_pair(
config.request_limit,
redis_url,
"gateway-a",
)?;
)
.await?;
let gate_b = distributed_request_gate(
"gateway_requests_distributed",
config.request_limit,
redis_url,
"gateway-a",
)?;
"gateway-b",
)
.await?;
let gateway_a = GatewayHarness::start(GatewayHarnessConfig {
upstream_base_url: upstream_base_url.to_string(),
data_config: None,
@@ -209,13 +213,15 @@ async fn start_execution_runtime_pair(
config.request_limit,
redis_url,
"execution-runtime-a",
)?;
)
.await?;
let gate_b = distributed_request_gate(
"execution_runtime_requests_distributed",
config.request_limit,
redis_url,
"execution-runtime-a",
)?;
"execution-runtime-b",
)
.await?;
let runtime_a = ExecutionRuntimeHarness::start(ExecutionRuntimeHarnessConfig {
max_in_flight_requests: None,
distributed_request_gate: Some(gate_a),
@@ -245,13 +251,15 @@ async fn start_tunnel_pair(
config.tunnel_request_limit,
redis_url,
"tunnel-a",
)?;
)
.await?;
let gate_b = distributed_request_gate(
"tunnel_requests_distributed",
config.tunnel_request_limit,
redis_url,
"tunnel-a",
)?;
"tunnel-b",
)
.await?;
let tunnel_a = TunnelHarness::start(TunnelHarnessConfig {
distributed_request_gate: Some(gate_a),
..TunnelHarnessConfig::default()
@@ -279,21 +287,27 @@ async fn start_tunnel_pair(
))
}
fn distributed_request_gate(
async fn distributed_request_gate(
name: &'static str,
limit: usize,
redis_url: &str,
key_scope: &str,
) -> Result<DistributedConcurrencyGate, Box<dyn std::error::Error>> {
Ok(DistributedConcurrencyGate::new_redis(
name,
limit,
RedisDistributedConcurrencyConfig {
) -> Result<RuntimeSemaphore, Box<dyn std::error::Error>> {
let runtime = RuntimeState::redis(
RedisClientConfig {
url: redis_url.to_string(),
key_prefix: Some(format!(
"aether-baseline-{}-{name}-{key_scope}",
std::process::id()
)),
},
Some(1_000),
)
.await?;
Ok(runtime.semaphore(
name,
limit,
RuntimeSemaphoreConfig {
lease_ttl_ms: 30_000,
renew_interval_ms: 10_000,
command_timeout_ms: Some(1_000),
@@ -1,11 +1,10 @@
use std::path::PathBuf;
use std::time::{Duration, Instant};
use aether_data::driver::redis::{
RedisClientConfig, RedisConsumerGroup, RedisConsumerName, RedisStreamReclaimConfig,
RedisStreamRunnerConfig,
use aether_runtime_state::{
RedisClientConfig, RedisClientFactory, RedisConsumerGroup, RedisConsumerName, RedisStreamName,
RedisStreamReclaimConfig, RedisStreamRunner, RedisStreamRunnerConfig,
};
use aether_data::RedisBackend;
use aether_testkit::{init_test_runtime_for, ManagedRedisServer};
use serde::Serialize;
@@ -87,19 +86,25 @@ async fn run_suite(
})
.expect("redis url should be resolved");
let backend = RedisBackend::from_config(RedisClientConfig {
let factory = RedisClientFactory::new(RedisClientConfig {
url: redis_url.clone(),
key_prefix: Some(format!("aether-baseline-{}", std::process::id())),
})?;
let stream = backend.keyspace().stream_name("worker-baseline");
let client = factory.connect_lazy()?;
let keyspace = factory.config().keyspace();
let stream = keyspace.stream_name("worker-baseline");
let group = RedisConsumerGroup("worker-group".to_string());
let consumer_a = RedisConsumerName("consumer-a".to_string());
let consumer_b = RedisConsumerName("consumer-b".to_string());
let runner = backend.stream_runner(RedisStreamRunnerConfig {
command_timeout_ms: Some(2_000),
read_block_ms: Some(10),
read_count: 64,
})?;
let runner = RedisStreamRunner::new(
client,
keyspace,
RedisStreamRunnerConfig {
command_timeout_ms: Some(2_000),
read_block_ms: Some(10),
read_count: 64,
},
)?;
runner
.ensure_consumer_group(&stream, &group, "0-0")
.await
@@ -124,8 +129,8 @@ async fn run_suite(
}
async fn benchmark_append(
runner: &aether_data::driver::redis::RedisStreamRunner,
stream: &aether_data::driver::redis::RedisStreamName,
runner: &RedisStreamRunner,
stream: &RedisStreamName,
config: &RedisWorkerBaselineConfig,
) -> Result<OperationSummary, Box<dyn std::error::Error>> {
let next = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
@@ -182,8 +187,8 @@ async fn benchmark_append(
}
async fn benchmark_read_group(
runner: &aether_data::driver::redis::RedisStreamRunner,
stream: &aether_data::driver::redis::RedisStreamName,
runner: &RedisStreamRunner,
stream: &RedisStreamName,
group: &RedisConsumerGroup,
consumer: &RedisConsumerName,
config: &RedisWorkerBaselineConfig,
@@ -211,8 +216,8 @@ async fn benchmark_read_group(
}
async fn benchmark_reclaim(
runner: &aether_data::driver::redis::RedisStreamRunner,
stream: &aether_data::driver::redis::RedisStreamName,
runner: &RedisStreamRunner,
stream: &RedisStreamName,
group: &RedisConsumerGroup,
consumer_a: &RedisConsumerName,
consumer_b: &RedisConsumerName,
@@ -283,8 +288,8 @@ async fn benchmark_reclaim(
}
async fn benchmark_ack(
runner: &aether_data::driver::redis::RedisStreamRunner,
stream: &aether_data::driver::redis::RedisStreamName,
runner: &RedisStreamRunner,
stream: &RedisStreamName,
group: &RedisConsumerGroup,
ids: &[String],
) -> Result<OperationSummary, Box<dyn std::error::Error>> {
@@ -1,12 +1,12 @@
use aether_gateway::build_execution_runtime_router_with_request_gates;
use aether_runtime::DistributedConcurrencyGate;
use aether_runtime_state::RuntimeSemaphore;
use crate::server::SpawnedServer;
#[derive(Debug, Clone, Default)]
pub struct ExecutionRuntimeHarnessConfig {
pub max_in_flight_requests: Option<usize>,
pub distributed_request_gate: Option<DistributedConcurrencyGate>,
pub distributed_request_gate: Option<RuntimeSemaphore>,
}
#[derive(Debug)]
+2 -2
View File
@@ -1,5 +1,5 @@
use aether_gateway::{build_router_with_state, AppState, GatewayDataConfig};
use aether_runtime::DistributedConcurrencyGate;
use aether_runtime_state::RuntimeSemaphore;
use crate::server::SpawnedServer;
@@ -8,7 +8,7 @@ pub struct GatewayHarnessConfig {
pub upstream_base_url: String,
pub data_config: Option<GatewayDataConfig>,
pub max_in_flight_requests: Option<usize>,
pub distributed_request_gate: Option<DistributedConcurrencyGate>,
pub distributed_request_gate: Option<RuntimeSemaphore>,
pub tunnel_instance_id: Option<String>,
pub tunnel_relay_base_url: Option<String>,
}
+2 -2
View File
@@ -4,7 +4,7 @@ use aether_gateway::{
build_tunnel_runtime_router_with_state, TunnelConnConfig, TunnelControlPlaneClient,
TunnelRuntimeState,
};
use aether_runtime::DistributedConcurrencyGate;
use aether_runtime_state::RuntimeSemaphore;
use crate::server::SpawnedServer;
@@ -14,7 +14,7 @@ pub struct TunnelHarnessConfig {
pub ping_interval: Duration,
pub outbound_queue_capacity: usize,
pub max_in_flight_requests: Option<usize>,
pub distributed_request_gate: Option<DistributedConcurrencyGate>,
pub distributed_request_gate: Option<RuntimeSemaphore>,
}
impl Default for TunnelHarnessConfig {