rustapi-jobs 0.1.397

Robust background job processing for RustAPI. Support for Redis and PostgreSQL backends, retries, and scheduled tasks.
Documentation
use super::{JobBackend, JobRequest};
use crate::error::{JobError, Result};
use async_trait::async_trait;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex};

/// In-memory job backend (not persistent, for testing/dev)
#[derive(Debug, Clone, Default)]
pub struct InMemoryBackend {
    queue: Arc<Mutex<VecDeque<JobRequest>>>,
    // In a real system we'd track processing jobs separately for reliability
}

impl InMemoryBackend {
    pub fn new() -> Self {
        Self::default()
    }
}

#[async_trait]
impl JobBackend for InMemoryBackend {
    async fn push(&self, job: JobRequest) -> Result<()> {
        let mut q = self
            .queue
            .lock()
            .map_err(|_| JobError::BackendError("Lock poisoned".to_string()))?;
        q.push_back(job);
        Ok(())
    }

    async fn pop(&self) -> Result<Option<JobRequest>> {
        let mut q = self
            .queue
            .lock()
            .map_err(|_| JobError::BackendError("Lock poisoned".to_string()))?;

        let now = chrono::Utc::now();
        let mut index_to_remove = None;

        // Scan the queue for the first ready job
        for (i, job) in q.iter().enumerate() {
            if let Some(run_at) = job.run_at {
                if run_at > now {
                    continue;
                }
            }
            // Found a ready job (no run_at, or run_at <= now)
            index_to_remove = Some(i);
            break;
        }

        if let Some(i) = index_to_remove {
            Ok(q.remove(i))
        } else {
            Ok(None)
        }
    }

    async fn complete(&self, _job_id: &str) -> Result<()> {
        // No-op for simple in-memory queue that removes on pop
        Ok(())
    }

    async fn fail(&self, _job_id: &str, _error: &str) -> Result<()> {
        // In a real implementation we might move to DLQ or re-queue
        Ok(())
    }
}