lighthouse/callbacks.rs
1// src/callbacks.rs
2
3use async_trait::async_trait;
4use std::collections::HashMap;
5
6use crate::error::LighthouseResult;
7use crate::types::{ResourceMetrics, ScaleAction};
8
9/// Context provided to callbacks with additional information
10#[derive(Debug, Clone)]
11pub struct CallbackContext {
12 /// Current timestamp when callback is invoked
13 pub timestamp: u64,
14 /// Any additional metadata from the engine
15 pub metadata: HashMap<String, String>,
16}
17
18/// Trait for handling metric collection and validation
19///
20/// Implement this to provide metrics to the lighthouse engine.
21/// This allows you to integrate with any monitoring system.
22#[async_trait]
23pub trait MetricsProvider: Send + Sync {
24 /// Fetch current metrics for a specific resource
25 ///
26 /// # Arguments
27 /// * `resource_id` - The resource to get metrics for
28 /// * `context` - Additional context for the callback
29 ///
30 /// # Returns
31 /// * `Ok(Some(metrics))` - Successfully fetched metrics
32 /// * `Ok(None)` - Resource exists but no metrics available
33 /// * `Err(error)` - Failed to fetch metrics
34 async fn get_metrics(
35 &self,
36 resource_id: &str,
37 context: &CallbackContext,
38 ) -> LighthouseResult<Option<ResourceMetrics>>;
39
40 /// Validate that metrics are reasonable/expected
41 ///
42 /// This is called before metrics are processed by scaling policies.
43 /// Use this to filter out bad data, apply smoothing, etc.
44 ///
45 /// # Arguments
46 /// * `metrics` - The raw metrics to validate
47 /// * `context` - Additional context for the callback
48 ///
49 /// # Returns
50 /// * `Ok(Some(validated_metrics))` - Metrics are valid (possibly modified)
51 /// * `Ok(None)` - Metrics should be ignored
52 /// * `Err(error)` - Validation failed
53 async fn validate_metrics(
54 &self,
55 metrics: &ResourceMetrics,
56 _context: &CallbackContext,
57 ) -> LighthouseResult<Option<ResourceMetrics>> {
58 // Default implementation: accept all metrics as-is
59 Ok(Some(metrics.clone()))
60 }
61}
62
63/// Trait for executing scaling actions
64///
65/// Implement this to actually perform scaling operations.
66/// This is where you integrate with your infrastructure (K8s, AWS, etc.).
67#[async_trait]
68pub trait ScalingExecutor: Send + Sync {
69 /// Execute a scaling action
70 ///
71 /// # Arguments
72 /// * `action` - The scaling action to perform
73 /// * `context` - Additional context for the callback
74 ///
75 /// # Returns
76 /// * `Ok(true)` - Action was successfully executed
77 /// * `Ok(false)` - Action was skipped (e.g., already at target scale)
78 /// * `Err(error)` - Failed to execute action
79 async fn execute_scale_action(
80 &self,
81 action: &ScaleAction,
82 context: &CallbackContext,
83 ) -> LighthouseResult<bool>;
84
85 /// Check if a scaling action is safe to execute
86 ///
87 /// This is called before `execute_scale_action` to provide a safety check.
88 /// Use this to implement business rules, maintenance windows, etc.
89 ///
90 /// # Arguments
91 /// * `action` - The proposed scaling action
92 /// * `context` - Additional context for the callback
93 ///
94 /// # Returns
95 /// * `Ok(true)` - Action is safe to execute
96 /// * `Ok(false)` - Action should be skipped
97 /// * `Err(error)` - Safety check failed
98 async fn is_safe_to_scale(
99 &self,
100 _action: &ScaleAction,
101 _context: &CallbackContext,
102 ) -> LighthouseResult<bool> {
103 // Default implementation: all actions are safe
104 Ok(true)
105 }
106
107 /// Get current capacity/state of a resource
108 ///
109 /// This helps the engine understand the current state before scaling.
110 ///
111 /// # Arguments
112 /// * `resource_id` - The resource to check
113 /// * `context` - Additional context for the callback
114 ///
115 /// # Returns
116 /// * `Ok(Some(capacity))` - Current capacity (e.g., number of instances)
117 /// * `Ok(None)` - Unable to determine current capacity
118 /// * `Err(error)` - Failed to check capacity
119 async fn get_current_capacity(
120 &self,
121 _resource_id: &str,
122 _context: &CallbackContext,
123 ) -> LighthouseResult<Option<u32>> {
124 // Default implementation: unknown capacity
125 Ok(None)
126 }
127}
128
129/// Trait for receiving scaling events and decisions
130///
131/// Implement this to get notified about scaling decisions.
132/// Useful for logging, alerting, or custom business logic.
133#[async_trait]
134pub trait ScalingObserver: Send + Sync {
135 /// Called when a scaling decision is made (before execution)
136 async fn on_scaling_decision(
137 &self,
138 _action: &ScaleAction,
139 _context: &CallbackContext,
140 ) -> LighthouseResult<()> {
141 // Default implementation: do nothing
142 Ok(())
143 }
144
145 /// Called after a scaling action is executed
146 async fn on_scaling_executed(
147 &self,
148 _action: &ScaleAction,
149 _success: bool,
150 _context: &CallbackContext,
151 ) -> LighthouseResult<()> {
152 // Default implementation: do nothing
153 Ok(())
154 }
155
156 /// Called when scaling is skipped (e.g., due to cooldown)
157 async fn on_scaling_skipped(
158 &self,
159 _action: &ScaleAction,
160 _reason: &str,
161 _context: &CallbackContext,
162 ) -> LighthouseResult<()> {
163 // Default implementation: do nothing
164 Ok(())
165 }
166
167 /// Called when an error occurs during scaling
168 async fn on_scaling_error(
169 &self,
170 _action: &ScaleAction,
171 _error: &crate::error::LighthouseError,
172 _context: &CallbackContext,
173 ) -> LighthouseResult<()> {
174 // Default implementation: do nothing
175 Ok(())
176 }
177}
178
179/// Combine all callbacks into a single struct for easier management
180#[derive(Clone)]
181pub struct LighthouseCallbacks {
182 pub metrics_provider: std::sync::Arc<dyn MetricsProvider>,
183 pub scaling_executor: std::sync::Arc<dyn ScalingExecutor>,
184 pub observers: Vec<std::sync::Arc<dyn ScalingObserver>>,
185}
186
187impl LighthouseCallbacks {
188 /// Create a new callback configuration
189 pub fn new(
190 metrics_provider: std::sync::Arc<dyn MetricsProvider>,
191 scaling_executor: std::sync::Arc<dyn ScalingExecutor>,
192 ) -> Self {
193 Self {
194 metrics_provider,
195 scaling_executor,
196 observers: Vec::new(),
197 }
198 }
199
200 /// Add an observer to receive scaling events
201 pub fn add_observer(mut self, observer: std::sync::Arc<dyn ScalingObserver>) -> Self {
202 self.observers.push(observer);
203 self
204 }
205
206 /// Add multiple observers at once
207 pub fn add_observers(mut self, observers: Vec<std::sync::Arc<dyn ScalingObserver>>) -> Self {
208 self.observers.extend(observers);
209 self
210 }
211}