runtime: add execution concurrency limits

This commit is contained in:
a.tolmachev
2026-05-01 12:11:53 +00:00
parent 3d2d97c1e6
commit 8ac55ebcc2
13 changed files with 448 additions and 15 deletions
+22
View File
@@ -403,6 +403,7 @@ fn runtime_test_failure_code(error: &RuntimeError) -> &'static str {
RuntimeError::SoapAdapter(_) => "runtime_soap_error",
RuntimeError::WebsocketAdapter(_) => "runtime_websocket_error",
RuntimeError::UnsupportedProtocol { .. } => "runtime_protocol_error",
RuntimeError::ConcurrencyLimitExceeded { .. } => "runtime_overloaded",
RuntimeError::InvalidPreparedRequest { .. } => "runtime_request_error",
RuntimeError::MissingStreamingConfig { .. } => "runtime_streaming_config_error",
RuntimeError::UnsupportedExecutionMode { .. } => "runtime_streaming_mode_error",
@@ -454,6 +455,10 @@ pub fn runtime_error_context(error: &RuntimeError) -> Option<Value> {
RuntimeError::UnsupportedProtocol { protocol } => Some(json!({
"protocol": protocol,
})),
RuntimeError::ConcurrencyLimitExceeded { kind, limit } => Some(json!({
"kind": kind,
"limit": limit,
})),
_ => None,
}
}
@@ -500,6 +505,23 @@ mod tests {
);
}
#[test]
fn runtime_test_failure_includes_runtime_overload_context() {
let payload = runtime_test_failure(&RuntimeError::ConcurrencyLimitExceeded {
kind: "window",
limit: 16,
});
assert_eq!(payload["code"], "runtime_overloaded");
assert_eq!(
payload["context"],
json!({
"kind": "window",
"limit": 16
})
);
}
#[test]
fn registry_errors_preserve_structured_context_in_api_error() {
let error = ApiError::from(RegistryError::InvalidAsyncJobTransition {
+14 -2
View File
@@ -20,7 +20,7 @@ use crate::{
service::AdminService,
state::AppState,
};
use crank_runtime::SecretCrypto;
use crank_runtime::{RuntimeExecutor, RuntimeLimits, SecretCrypto};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -57,8 +57,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.unwrap_or_else(|_| "Crank Owner".into()),
},
};
let runtime_limits = RuntimeLimits::from_env()?;
let secret_crypto = SecretCrypto::new(&env::var("CRANK_MASTER_KEY")?)?;
let service = AdminService::new(registry, storage_root, auth_settings, secret_crypto);
let runtime = RuntimeExecutor::with_limits(runtime_limits);
let service = AdminService::new_with_runtime(
registry,
storage_root,
auth_settings,
secret_crypto,
runtime,
);
service.bootstrap_admin_user().await?;
if env_flag("CRANK_DEMO_SEED") {
service.seed_demo_assets().await?;
@@ -68,6 +76,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind(socket_addr).await?;
info!(
runtime_max_concurrent_unary = runtime_limits.max_concurrent_unary,
runtime_max_concurrent_window = runtime_limits.max_concurrent_window,
runtime_max_concurrent_sessions = runtime_limits.max_concurrent_sessions,
runtime_max_concurrent_jobs = runtime_limits.max_concurrent_jobs,
max_connections = pool_config.max_connections,
min_connections = pool_config.min_connections,
acquire_timeout_ms = pool_config.acquire_timeout_ms,
+19 -1
View File
@@ -698,15 +698,32 @@ fn default_operation_category() -> String {
}
impl AdminService {
#[cfg(test)]
pub fn new(
registry: PostgresRegistry,
storage_root: PathBuf,
auth_settings: AuthSettings,
secret_crypto: SecretCrypto,
) -> Self {
Self::new_with_runtime(
registry,
storage_root,
auth_settings,
secret_crypto,
RuntimeExecutor::new(),
)
}
pub fn new_with_runtime(
registry: PostgresRegistry,
storage_root: PathBuf,
auth_settings: AuthSettings,
secret_crypto: SecretCrypto,
runtime: RuntimeExecutor,
) -> Self {
Self {
registry,
runtime: RuntimeExecutor::new(),
runtime,
storage: LocalArtifactStorage::new(storage_root),
auth_settings,
secret_crypto,
@@ -4881,6 +4898,7 @@ fn runtime_error_code(error: &RuntimeError) -> &'static str {
RuntimeError::SoapAdapter(_) => "soap_error",
RuntimeError::WebsocketAdapter(_) => "websocket_error",
RuntimeError::UnsupportedProtocol { .. } => "unsupported_protocol",
RuntimeError::ConcurrencyLimitExceeded { .. } => "runtime_overloaded",
RuntimeError::MissingStreamingConfig { .. } => "streaming_config_error",
RuntimeError::UnsupportedExecutionMode { .. } => "streaming_mode_error",
RuntimeError::InvalidStreamingPayload { .. } => "streaming_payload_error",