openzeppelin_relayer/jobs/handlers/
mod.rs

1use eyre::Report;
2use tracing::{debug, error, warn};
3
4use crate::{
5    observability::request_id::get_request_id,
6    queues::{HandlerError, WorkerContext},
7};
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    ctx: &WorkerContext,
44    job_type: &str,
45    max_attempts: usize,
46) -> Result<(), HandlerError> {
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 = %ctx.attempt,
62        max_attempts = %max_attempts,
63        "request failed"
64    );
65
66    if ctx.attempt >= 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(HandlerError::Abort("Failed to handle request".into()));
74    }
75
76    Err(HandlerError::Retry(
77        "Failed to handle request. Retrying".into(),
78    ))
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn test_handle_result_success() {
87        let result: Result<(), Report> = Ok(());
88        let ctx = WorkerContext::new(0, "test-task".into());
89
90        let handled = handle_result(result, &ctx, "test_job", 3);
91        assert!(handled.is_ok());
92    }
93
94    #[test]
95    fn test_handle_result_retry() {
96        let result: Result<(), Report> = Err(Report::msg("Test error"));
97        let ctx = WorkerContext::new(0, "test-task".into());
98
99        let handled = handle_result(result, &ctx, "test_job", 3);
100
101        assert!(handled.is_err());
102        assert!(matches!(handled, Err(HandlerError::Retry(_))));
103    }
104
105    #[test]
106    fn test_handle_result_abort() {
107        let result: Result<(), Report> = Err(Report::msg("Test error"));
108        let ctx = WorkerContext::new(3, "test-task".into());
109
110        let handled = handle_result(result, &ctx, "test_job", 3);
111
112        assert!(handled.is_err());
113        assert!(matches!(handled, Err(HandlerError::Abort(_))));
114    }
115
116    #[test]
117    fn test_handle_result_max_attempts_exceeded() {
118        let result: Result<(), Report> = Err(Report::msg("Test error"));
119        let ctx = WorkerContext::new(5, "test-task".into());
120
121        let handled = handle_result(result, &ctx, "test_job", 3);
122
123        assert!(handled.is_err());
124        assert!(matches!(handled, Err(HandlerError::Abort(_))));
125    }
126}