mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-12 14:10:19 +08:00
refactor: extract runtime state backends
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user