pub struct AutoscaleController { /* private fields */ }Expand description
Controller that connects autoscaling decisions to actual container scaling
The AutoscaleController periodically collects metrics from running containers,
evaluates whether scaling is needed using the Autoscaler, and executes scaling
decisions through the ServiceManager. On the same tick it runs the
scale-to-zero idle reaper and the vertical right-sizing pass.
Implementations§
Source§impl AutoscaleController
impl AutoscaleController
Sourcepub fn new(
service_manager: Arc<RwLock<ServiceManager>>,
runtime: Arc<dyn Runtime + Send + Sync>,
interval: Duration,
) -> Self
pub fn new( service_manager: Arc<RwLock<ServiceManager>>, runtime: Arc<dyn Runtime + Send + Sync>, interval: Duration, ) -> Self
Create a new autoscale controller
§Arguments
service_manager- The service manager used to execute scaling operationsruntime- The container runtime for collecting metrics and applying vertical resource updatesinterval- How often to evaluate scaling decisions
The runtime handle is retained on the controller (for the vertical
apply / rolling-restart paths) and moved into the metrics stats
provider, so it is cloned once here.
§Example
let controller = AutoscaleController::new(
service_manager,
runtime,
Duration::from_secs(10),
);Sourcepub fn with_custom_metrics(
service_manager: Arc<RwLock<ServiceManager>>,
runtime: Arc<dyn Runtime + Send + Sync>,
metrics: MetricsCollector,
interval: Duration,
) -> Self
pub fn with_custom_metrics( service_manager: Arc<RwLock<ServiceManager>>, runtime: Arc<dyn Runtime + Send + Sync>, metrics: MetricsCollector, interval: Duration, ) -> Self
Create with a custom metrics collector (useful for testing).
Requires a runtime handle so the vertical-apply path is still wired in
tests; pass a mock runtime when only the horizontal/scale-to-zero paths
are under test.
Sourcepub fn with_extra_metrics_source(self, source: Arc<dyn MetricsSource>) -> Self
pub fn with_extra_metrics_source(self, source: Arc<dyn MetricsSource>) -> Self
Push an additional MetricsSource into the controller’s collector.
The daemon uses this to feed real requests-per-second from the L7 proxy
(wrapped in a zlayer_scheduler::metrics::ProxyRpsMetricsSource) into
the same MetricsCollector that drives the horizontal pass, so
zlayer_scheduler::metrics::AggregatedMetrics::total_rps is populated
and RPS targets / triggers fire on live traffic.
This is a builder method that must be called before the controller is shared (the collector is still uniquely owned at construction time, so the in-place mutation always succeeds). If the collector has already been cloned, the source is dropped with a warning rather than panicking.
Sourcepub async fn register_service(
&self,
name: &str,
spec: &ScaleSpec,
initial_replicas: u32,
)
pub async fn register_service( &self, name: &str, spec: &ScaleSpec, initial_replicas: u32, )
Register a service for autoscaling
Only services with ScaleSpec::Adaptive will be evaluated for autoscaling.
Services with Fixed or Manual scaling are ignored by the autoscaler loop.
Beyond the horizontal registration, this captures the Phase 2 / Phase 3
configuration carried on the adaptive spec: idle_window + min (for
scale-to-zero) and vertical (for right-sizing). The service is seeded
as freshly-active so a just-registered service is not immediately reaped.
§Arguments
name- Service namespec- The service’s scale specificationinitial_replicas- Current number of replicas
Sourcepub async fn set_service_template(&self, name: &str, spec: ServiceSpec)
pub async fn set_service_template(&self, name: &str, spec: ServiceSpec)
Supply (or refresh) the full base ServiceSpec for a service.
Optional, but required for the vertical-apply rolling restart
fallback (update_container_resources → Unsupported): recreating a
replica needs its endpoints / env / volumes, which the ScaleSpec
alone does not carry. The daemon wires this alongside
AutoscaleController::register_service. Without a template, the
fallback degrades to a full scale 0 → scale n bounce.
Sourcepub fn mark_active(&self, service: &str)
pub fn mark_active(&self, service: &str)
Mark a service as active now, resetting its scale-to-zero idle clock.
Called by the proxy activator when an inbound request wakes (or keeps awake) a service so the idle reaper does not tear it down while it is actively serving traffic. Safe to call for services that are not registered for scale-to-zero — it simply records a timestamp that the idle pass ignores.
Sourcepub async fn mark_active_async(&self, service: &str)
pub async fn mark_active_async(&self, service: &str)
Async variant of AutoscaleController::mark_active for callers already
inside an async context (avoids the try_write/spawn dance).
Sourcepub async fn unregister_service(&self, name: &str)
pub async fn unregister_service(&self, name: &str)
Unregister a service from autoscaling
Sourcepub async fn is_registered(&self, name: &str) -> bool
pub async fn is_registered(&self, name: &str) -> bool
Check if a service is registered for autoscaling
Sourcepub async fn run_loop(&self) -> Result<()>
pub async fn run_loop(&self) -> Result<()>
Run the autoscaling loop
This method should be spawned as a background task. It will continuously evaluate scaling decisions at the configured interval until shutdown is signaled.
§Returns
Returns Ok(()) when shutdown is signaled, or an error if something
goes wrong.
§Example
let controller = Arc::new(AutoscaleController::new(...));
let controller_clone = controller.clone();
// Spawn the autoscale loop
let handle = tokio::spawn(async move {
controller_clone.run_loop().await
});
// Later, shutdown
controller.shutdown();
handle.await.unwrap();§Errors
Returns an error if the autoscale loop encounters an unrecoverable error.
Sourcepub async fn registered_service_count(&self) -> usize
pub async fn registered_service_count(&self) -> usize
Get registered service count
Auto Trait Implementations§
impl !RefUnwindSafe for AutoscaleController
impl !UnwindSafe for AutoscaleController
impl Freeze for AutoscaleController
impl Send for AutoscaleController
impl Sync for AutoscaleController
impl Unpin for AutoscaleController
impl UnsafeUnpin for AutoscaleController
Blanket Implementations§
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> FutureExt for T
impl<T> FutureExt for T
Source§fn with_context(self, otel_cx: Context) -> WithContext<Self>
fn with_context(self, otel_cx: Context) -> WithContext<Self>
Source§fn with_current_context(self) -> WithContext<Self>
fn with_current_context(self) -> WithContext<Self>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Requestimpl<T> OptionalSend for T
impl<T> OptionalSync for T
Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
Source§impl<T> ServiceExt for T
impl<T> ServiceExt for T
Source§fn propagate_header(self, header: HeaderName) -> PropagateHeader<Self>where
Self: Sized,
fn propagate_header(self, header: HeaderName) -> PropagateHeader<Self>where
Self: Sized,
Source§fn add_extension<T>(self, value: T) -> AddExtension<Self, T>where
Self: Sized,
fn add_extension<T>(self, value: T) -> AddExtension<Self, T>where
Self: Sized,
Source§fn map_request_body<F>(self, f: F) -> MapRequestBody<Self, F>where
Self: Sized,
fn map_request_body<F>(self, f: F) -> MapRequestBody<Self, F>where
Self: Sized,
Source§fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>where
Self: Sized,
fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>where
Self: Sized,
Source§fn compression(self) -> Compression<Self>where
Self: Sized,
fn compression(self) -> Compression<Self>where
Self: Sized,
Source§fn decompression(self) -> Decompression<Self>where
Self: Sized,
fn decompression(self) -> Decompression<Self>where
Self: Sized,
Source§fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
Source§fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
Source§fn follow_redirects(self) -> FollowRedirect<Self>where
Self: Sized,
fn follow_redirects(self) -> FollowRedirect<Self>where
Self: Sized,
Source§fn sensitive_headers(
self,
headers: impl IntoIterator<Item = HeaderName>,
) -> SetSensitiveRequestHeaders<SetSensitiveResponseHeaders<Self>>where
Self: Sized,
fn sensitive_headers(
self,
headers: impl IntoIterator<Item = HeaderName>,
) -> SetSensitiveRequestHeaders<SetSensitiveResponseHeaders<Self>>where
Self: Sized,
Source§fn sensitive_request_headers(
self,
headers: impl IntoIterator<Item = HeaderName>,
) -> SetSensitiveRequestHeaders<Self>where
Self: Sized,
fn sensitive_request_headers(
self,
headers: impl IntoIterator<Item = HeaderName>,
) -> SetSensitiveRequestHeaders<Self>where
Self: Sized,
Source§fn sensitive_response_headers(
self,
headers: impl IntoIterator<Item = HeaderName>,
) -> SetSensitiveResponseHeaders<Self>where
Self: Sized,
fn sensitive_response_headers(
self,
headers: impl IntoIterator<Item = HeaderName>,
) -> SetSensitiveResponseHeaders<Self>where
Self: Sized,
Source§fn override_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
fn override_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
Source§fn append_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
fn append_request_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
Source§fn insert_request_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
fn insert_request_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetRequestHeader<Self, M>where
Self: Sized,
Source§fn override_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
fn override_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
Source§fn append_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
fn append_response_header<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
Source§fn insert_response_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
fn insert_response_header_if_not_present<M>(
self,
header_name: HeaderName,
make: M,
) -> SetResponseHeader<Self, M>where
Self: Sized,
Source§fn set_request_id<M>(
self,
header_name: HeaderName,
make_request_id: M,
) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
fn set_request_id<M>(
self,
header_name: HeaderName,
make_request_id: M,
) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
Source§fn set_x_request_id<M>(self, make_request_id: M) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
fn set_x_request_id<M>(self, make_request_id: M) -> SetRequestId<Self, M>where
Self: Sized,
M: MakeRequestId,
x-request-id as the header name. Read moreSource§fn propagate_request_id(
self,
header_name: HeaderName,
) -> PropagateRequestId<Self>where
Self: Sized,
fn propagate_request_id(
self,
header_name: HeaderName,
) -> PropagateRequestId<Self>where
Self: Sized,
Source§fn propagate_x_request_id(self) -> PropagateRequestId<Self>where
Self: Sized,
fn propagate_x_request_id(self) -> PropagateRequestId<Self>where
Self: Sized,
x-request-id as the header name. Read moreSource§fn catch_panic(self) -> CatchPanic<Self, DefaultResponseForPanic>where
Self: Sized,
fn catch_panic(self) -> CatchPanic<Self, DefaultResponseForPanic>where
Self: Sized,
500 Internal Server responses. Read moreSource§fn request_body_limit(self, limit: usize) -> RequestBodyLimit<Self>where
Self: Sized,
fn request_body_limit(self, limit: usize) -> RequestBodyLimit<Self>where
Self: Sized,
413 Payload Too Large responses. Read more