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}