background-jobs/jobs-actix/src/server.rs

166 lines
4.5 KiB
Rust
Raw Normal View History

2020-03-21 02:31:03 +00:00
use crate::{
storage::{ActixStorage, StorageWrapper},
worker::Worker,
};
2020-04-23 18:16:56 +00:00
use actix_rt::{
time::{interval_at, Instant},
Arbiter,
2020-04-23 18:16:56 +00:00
};
2020-03-21 02:31:03 +00:00
use anyhow::Error;
2020-05-22 03:01:20 +00:00
use async_mutex::Mutex;
2020-03-21 02:31:03 +00:00
use background_jobs_core::{NewJobInfo, ReturnJobInfo, Stats, Storage};
use log::{error, trace};
2020-03-21 02:31:03 +00:00
use std::{
collections::{HashMap, VecDeque},
sync::Arc,
2020-04-23 18:16:56 +00:00
time::Duration,
2020-03-21 02:31:03 +00:00
};
type WorkerQueue = VecDeque<Box<dyn Worker + Send + Sync>>;
2020-04-25 22:12:43 +00:00
2020-03-21 02:31:03 +00:00
#[derive(Clone)]
pub(crate) struct ServerCache {
2020-04-25 22:12:43 +00:00
cache: Arc<Mutex<HashMap<String, WorkerQueue>>>,
2020-03-21 02:31:03 +00:00
}
2019-05-27 17:29:11 +00:00
2019-09-22 17:33:33 +00:00
/// The server Actor
///
/// This server guards access to Thee storage, and keeps a list of workers that are waiting for
/// jobs to process
2020-03-21 02:31:03 +00:00
#[derive(Clone)]
pub(crate) struct Server {
storage: Arc<dyn ActixStorage + Send + Sync>,
cache: ServerCache,
2019-05-27 17:29:11 +00:00
}
impl Server {
2020-03-21 02:31:03 +00:00
/// Create a new Server from a compatible storage implementation
pub(crate) fn new<S>(arbiter: &Arbiter, storage: S) -> Self
2020-03-21 02:31:03 +00:00
where
S: Storage + Sync + 'static,
{
let server = Server {
2020-03-21 02:31:03 +00:00
storage: Arc::new(StorageWrapper(storage)),
cache: ServerCache::new(),
};
let server2 = server.clone();
arbiter.send(Box::pin(async move {
let mut interval = interval_at(Instant::now(), Duration::from_secs(1));
loop {
interval.tick().await;
if let Err(e) = server.check_db().await {
error!("Error while checking database for new jobs, {}", e);
}
}
}));
server2
}
async fn check_db(&self) -> Result<(), Error> {
trace!("Checking db for ready jobs");
for queue in self.cache.keys().await {
'worker_loop: while let Some(worker) = self.cache.pop(queue.clone()).await {
2020-03-21 18:41:15 +00:00
if !self.try_turning(queue.clone(), worker).await? {
break 'worker_loop;
}
}
trace!("Finished job lookups for queue {}", queue);
2019-05-27 17:29:11 +00:00
}
Ok(())
2019-05-27 17:29:11 +00:00
}
2018-12-16 18:43:44 +00:00
2020-03-21 02:31:03 +00:00
pub(crate) async fn new_job(&self, job: NewJobInfo) -> Result<(), Error> {
let queue = job.queue().to_owned();
let ready = job.is_ready();
self.storage.new_job(job).await?;
2018-12-16 18:43:44 +00:00
2020-03-21 02:31:03 +00:00
if !ready {
trace!("New job is not ready for processing yet, returning");
2020-03-21 02:31:03 +00:00
return Ok(());
}
2020-03-21 02:31:03 +00:00
if let Some(worker) = self.cache.pop(queue.clone()).await {
2020-03-21 18:41:15 +00:00
self.try_turning(queue, worker).await?;
2018-12-16 18:43:44 +00:00
}
Ok(())
}
2020-03-21 02:31:03 +00:00
pub(crate) async fn request_job(
&self,
worker: Box<dyn Worker + Send + Sync + 'static>,
2020-03-21 02:31:03 +00:00
) -> Result<(), Error> {
trace!("Worker {} requested job", worker.id());
2020-03-21 18:41:15 +00:00
self.try_turning(worker.queue().to_owned(), worker).await?;
2018-12-16 18:43:44 +00:00
2020-03-21 18:41:15 +00:00
Ok(())
}
async fn try_turning(
&self,
queue: String,
worker: Box<dyn Worker + Send + Sync + 'static>,
2020-03-21 18:41:15 +00:00
) -> Result<bool, Error> {
trace!("Trying to find job for worker {}", worker.id());
2020-03-21 18:41:15 +00:00
if let Ok(Some(job)) = self.storage.request_job(&queue, worker.id()).await {
2020-04-21 00:30:56 +00:00
if let Err(job) = worker.process(job).await {
2020-03-21 02:31:03 +00:00
error!("Worker has hung up");
2020-03-21 18:41:15 +00:00
self.storage.return_job(job.unexecuted()).await?
2020-03-21 02:31:03 +00:00
}
2018-12-16 18:43:44 +00:00
} else {
trace!("No job exists, returning worker {}", worker.id());
2020-03-21 18:41:15 +00:00
self.cache.push(queue.clone(), worker).await;
return Ok(false);
2018-12-16 18:43:44 +00:00
}
2020-03-21 18:41:15 +00:00
Ok(true)
2018-12-16 18:43:44 +00:00
}
2020-03-21 02:31:03 +00:00
pub(crate) async fn return_job(&self, job: ReturnJobInfo) -> Result<(), Error> {
Ok(self.storage.return_job(job).await?)
}
pub(crate) async fn get_stats(&self) -> Result<Stats, Error> {
Ok(self.storage.get_stats().await?)
}
2018-12-16 18:43:44 +00:00
}
2020-03-21 02:31:03 +00:00
impl ServerCache {
fn new() -> Self {
ServerCache {
cache: Arc::new(Mutex::new(HashMap::new())),
2018-12-16 18:43:44 +00:00
}
}
async fn keys(&self) -> Vec<String> {
let cache = self.cache.lock().await;
cache.keys().cloned().collect()
}
async fn push(&self, queue: String, worker: Box<dyn Worker + Send + Sync>) {
2020-03-21 02:31:03 +00:00
let mut cache = self.cache.lock().await;
2020-04-25 22:12:43 +00:00
let entry = cache.entry(queue).or_insert_with(VecDeque::new);
2020-03-21 02:31:03 +00:00
entry.push_back(worker);
}
async fn pop(&self, queue: String) -> Option<Box<dyn Worker + Send + Sync>> {
2020-03-21 02:31:03 +00:00
let mut cache = self.cache.lock().await;
let mut vec_deque = cache.remove(&queue)?;
let item = vec_deque.pop_front()?;
if !vec_deque.is_empty() {
cache.insert(queue, vec_deque);
}
2020-03-21 02:31:03 +00:00
Some(item)
}
}