zirv_queue/models/
failed_job.rs

1use diesel::prelude::*;
2use serde::{Deserialize, Serialize};
3use zirv_db::DB;
4
5use super::job::Job;
6
7/// Represents a failed job record in the database.
8///
9/// Corresponds to the `failed_jobs` table in the database, and
10/// stores information about jobs that have failed execution.
11///
12/// # Fields
13///
14/// - `id`: Primary key of the failed job record.
15/// - `job_id`: The original `id` of the failed job from the `jobs` table.
16/// - `payload`: The serialized data for the job (e.g. parameters or settings).
17/// - `attempts`: Number of attempts that were made to run the job.
18/// - `reason`: Description or reason why the job failed.
19/// - `failed_at`: Timestamp (UTC or local) of when the job failed.
20#[derive(Queryable, Insertable, Serialize, Deserialize, Debug)]
21#[diesel(table_name = crate::schema::failed_jobs)]
22pub struct FailedJob {
23    pub id: i32,
24    pub job_id: i32,
25    pub payload: String,
26    pub attempts: i32,
27    pub reason: String,
28    pub failed_at: chrono::NaiveDateTime,
29}
30
31/// Represents the data needed to insert a new record into the `failed_jobs` table.
32///
33/// This is used in scenarios where a job fails and needs to be recorded.
34/// Unlike [`FailedJob`], it does not include the `id` field because that
35/// is managed by the database.
36///
37/// # Fields
38///
39/// - `job_id`: The ID of the original job that failed.
40/// - `payload`: The serialized data for the job (e.g. parameters or settings).
41/// - `attempts`: Number of attempts made to run the job before failure.
42/// - `reason`: Description or reason why the job failed.
43/// - `failed_at`: Timestamp of when the job failure occurred.
44#[derive(Insertable, Debug, Serialize, Deserialize)]
45#[diesel(table_name = crate::schema::failed_jobs)]
46pub struct NewFailedJob {
47    pub job_id: i32,
48    pub payload: String,
49    pub attempts: i32,
50    pub reason: String,
51    pub failed_at: chrono::NaiveDateTime,
52}
53
54impl FailedJob {
55    /// Creates a new record in the `failed_jobs` table based on a failed [`Job`].
56    ///
57    /// This function:
58    /// 1. Constructs a [`NewFailedJob`] using information from the provided [`Job`].
59    /// 2. Attempts to obtain a database connection via [`DB::get_conn`].
60    /// 3. Inserts the new record into the `failed_jobs` table using Diesel.
61    ///
62    /// # Parameters
63    ///
64    /// - `job`: The job that failed, containing its ID, payload, and attempt count.
65    /// - `_reason`: A string that describes the reason for the failure.
66    ///
67    /// # Returns
68    ///
69    /// - `Ok(())` if the record was inserted successfully.
70    /// - `Err(diesel::result::Error)` if the operation fails (e.g. unable to get
71    ///   a DB connection or insert fails).
72    ///
73    /// # Errors
74    ///
75    /// Returns [`diesel::result::Error::NotFound`] if a database connection cannot be acquired.
76    /// Otherwise, returns a more specific Diesel error if the insert fails.
77    ///
78    /// # Example
79    ///
80    /// ```rust,no_run
81    /// // Pseudocode for handling a failed job:
82    /// use zirv_queue::models::job::Job;
83    /// use zirv_queue::models::failed_job::FailedJob;
84    ///
85    /// // Suppose `some_failed_job` is an instance of `Job`
86    /// let some_failed_job = Job {
87    ///     id: 1,
88    ///     payload: "some payload".to_string(),
89    ///     attempts: 3,
90    ///     available_at: chrono::Local::now().naive_local(),
91    ///     created_at: chrono::Local::now().naive_local(),
92    ///     updated_at: chrono::Local::now().naive_local(),
93    ///     status: "failed".to_string(),
94    /// };
95    ///
96    /// let reason = "Timed out.".to_string();
97    ///
98    /// match FailedJob::create(some_failed_job, reason) {
99    ///     Ok(_) => println!("Failed job record inserted."),
100    ///     Err(e) => eprintln!("Error inserting failed job: {:?}", e),
101    /// }
102    /// ```
103    pub fn create(
104        job: &Job,
105        _reason: &str,
106    ) -> Result<(), diesel::result::Error> {
107        use crate::schema::failed_jobs::dsl::*;
108
109        let new_failed_job = NewFailedJob {
110            job_id: job.id,
111            payload: job.payload.to_owned(),
112            attempts: job.attempts,
113            reason: _reason.to_owned(),
114            failed_at: chrono::Local::now().naive_local(),
115        };
116
117        println!("{:?}", new_failed_job);
118
119        // Attempt to acquire a database connection
120        let mut conn = match DB::get_conn() {
121            Ok(conn) => conn,
122            Err(e) => {
123                eprintln!("Error getting DB connection: {:?}", e);
124                return Err(diesel::result::Error::NotFound);
125            }
126        };
127
128        // Attempt to insert the failed job record
129        match diesel::insert_into(failed_jobs)
130            .values(new_failed_job)
131            .execute(&mut conn)
132        {
133            Ok(_) => Ok(()),
134            Err(e) => {
135                eprintln!("Error inserting failed job: {:?}", e);
136                Err(e)
137            }
138        }
139    }
140}