openzeppelin_relayer/jobs/handlers/
mod.rs

1use std::sync::Arc;
2
3use apalis::prelude::{Attempt, Error};
4use eyre::Report;
5use tracing::{debug, error, warn};
6
7use crate::observability::request_id::get_request_id;
8
9mod transaction_request_handler;
10pub use transaction_request_handler::*;
11
12mod transaction_submission_handler;
13pub use transaction_submission_handler::*;
14
15mod notification_handler;
16pub use notification_handler::*;
17
18mod transaction_status_handler;
19pub use transaction_status_handler::*;
20
21mod token_swap_request_handler;
22pub use token_swap_request_handler::*;
23
24mod transaction_cleanup_handler;
25pub use transaction_cleanup_handler::*;
26
27mod system_cleanup_handler;
28pub use system_cleanup_handler::*;
29
30// Handles job results for simple handlers (no transaction state management).
31//
32// Used by: notification_handler, solana_swap_request_handler, transaction_cleanup_handler
33//
34// # Retry Strategy
35// - On success: Job completes
36// - On error: Retry until max_attempts reached
37// - At max_attempts: Abort job
38mod relayer_health_check_handler;
39pub use relayer_health_check_handler::*;
40
41pub fn handle_result(
42    result: Result<(), Report>,
43    attempt: Attempt,
44    job_type: &str,
45    max_attempts: usize,
46) -> Result<(), Error> {
47    if result.is_ok() {
48        debug!(
49            job_type = %job_type,
50            request_id = ?get_request_id(),
51            "request handled successfully"
52        );
53        return Ok(());
54    }
55
56    let err = result.as_ref().unwrap_err();
57    warn!(
58        job_type = %job_type,
59        request_id = ?get_request_id(),
60        error = %err,
61        attempt = %attempt.current(),
62        max_attempts = %max_attempts,
63        "request failed"
64    );
65
66    if attempt.current() >= max_attempts {
67        error!(
68            job_type = %job_type,
69            request_id = ?get_request_id(),
70            max_attempts = %max_attempts,
71            "max attempts reached, failing job"
72        );
73        return Err(Error::Abort(Arc::new("Failed to handle request".into())));
74    }
75
76    Err(Error::Failed(Arc::new(
77        "Failed to handle request. Retrying".into(),
78    )))
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use apalis::prelude::Attempt;
85
86    #[test]
87    fn test_handle_result_success() {
88        let result: Result<(), Report> = Ok(());
89        let attempt = Attempt::default();
90
91        let handled = handle_result(result, attempt, "test_job", 3);
92        assert!(handled.is_ok());
93    }
94
95    #[test]
96    fn test_handle_result_retry() {
97        let result: Result<(), Report> = Err(Report::msg("Test error"));
98        let attempt = Attempt::default();
99
100        let handled = handle_result(result, attempt, "test_job", 3);
101
102        assert!(handled.is_err());
103        match handled {
104            Err(Error::Failed(_)) => {
105                // This is the expected error type for a retry
106            }
107            _ => panic!("Expected Failed error for retry"),
108        }
109    }
110
111    #[test]
112    fn test_handle_result_abort() {
113        let result: Result<(), Report> = Err(Report::msg("Test error"));
114        let attempt = Attempt::default();
115        for _ in 0..3 {
116            attempt.increment();
117        }
118
119        let handled = handle_result(result, attempt, "test_job", 3);
120
121        assert!(handled.is_err());
122        match handled {
123            Err(Error::Abort(_)) => {
124                // This is the expected error type for an abort
125            }
126            _ => panic!("Expected Abort error for max attempts"),
127        }
128    }
129
130    #[test]
131    fn test_handle_result_max_attempts_exceeded() {
132        let result: Result<(), Report> = Err(Report::msg("Test error"));
133        let attempt = Attempt::default();
134        for _ in 0..5 {
135            attempt.increment();
136        }
137
138        let handled = handle_result(result, attempt, "test_job", 3);
139
140        assert!(handled.is_err());
141        match handled {
142            Err(Error::Abort(_)) => {
143                // This is the expected error type for exceeding max attempts
144            }
145            _ => panic!("Expected Abort error for exceeding max attempts"),
146        }
147    }
148}