backyard_core/job.rs
1use crate::error::Result;
2use async_trait::async_trait;
3use std::sync::Arc;
4
5pub type JobId = uuid::Uuid;
6
7#[derive(Clone)]
8pub struct JobContext {
9 pub queue: Arc<dyn crate::queue::Queue>,
10 pub worker_id: String,
11}
12
13impl JobContext {
14 pub async fn enqueue<J: Job>(&self, job: J, opts: crate::options::JobOptions) -> Result<JobId> {
15 let payload = serde_json::to_vec(&job)?;
16 let scheduled_at = opts.scheduled_at();
17
18 let req = crate::queue::EnqueueRequest {
19 job_type: J::NAME.to_string(),
20 queue: opts.queue,
21 payload,
22 max_retries: opts.max_retries,
23 priority: opts.priority,
24 scheduled_at,
25 };
26
27 self.queue.push(req).await
28 }
29}
30
31/// A background job that can be enqueued and executed by workers.
32///
33/// # Implementing Job
34///
35/// Your job struct must:
36/// 1. Implement `Serialize` and `Deserialize` (for storage)
37/// 2. Implement `Send + Sync` (for Tokio task boundaries)
38/// 3. Provide a `const NAME: &'static str` unique identifier
39/// 4. Implement async `execute(self, ctx) -> Result<()>`
40///
41/// # Example
42///
43/// ```ignore
44/// use backyard_core::{Job, JobContext, Result};
45/// use async_trait::async_trait;
46/// use serde::{Serialize, Deserialize};
47///
48/// #[derive(Serialize, Deserialize)]
49/// struct SendEmail {
50/// to: String,
51/// subject: String,
52/// }
53///
54/// #[async_trait]
55/// impl Job for SendEmail {
56/// const NAME: &'static str = "SendEmail";
57///
58/// async fn execute(self, _ctx: &JobContext) -> Result<()> {
59/// println!("Sending {} to {}", self.subject, self.to);
60/// Ok(())
61/// }
62/// }
63/// ```
64#[async_trait]
65pub trait Job: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static {
66 /// Unique identifier for this job type.
67 /// Must be stable and unique across your entire application.
68 const NAME: &'static str;
69
70 /// Default enqueue options for this job type.
71 /// Override to customize queue name, retries, priority, etc.
72 fn options() -> crate::options::JobOptions {
73 crate::options::JobOptions::default()
74 }
75
76 /// Execute the job.
77 ///
78 /// # Arguments
79 /// * `ctx` - Job context including the queue (for enqueueing other jobs from within)
80 ///
81 /// # Returns
82 /// * `Ok(())` - Job succeeded
83 /// * `Err(BackyardError)` - Job failed; will be retried or moved to dead letter queue
84 async fn execute(self, ctx: &JobContext) -> Result<()>;
85}
86
87#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
88pub struct RawJob {
89 pub id: JobId,
90 pub job_type: String,
91 pub queue: String,
92 pub payload: Vec<u8>,
93 pub attempts: u32,
94 pub max_retries: u32,
95 pub scheduled_at: chrono::DateTime<chrono::Utc>,
96 pub created_at: chrono::DateTime<chrono::Utc>,
97 pub error: Option<String>,
98}