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}