Add job trait and state data objects
Signed-off-by: trivernis <trivernis@protonmail.com>feature/jobs
parent
8b342f6aac
commit
530fbd7606
@ -0,0 +1,20 @@
|
|||||||
|
use crate::dao::job::JobDao;
|
||||||
|
use crate::dto::JobStateDto;
|
||||||
|
use mediarepo_core::error::RepoResult;
|
||||||
|
use mediarepo_database::entities::job_state;
|
||||||
|
use sea_orm::prelude::*;
|
||||||
|
|
||||||
|
impl JobDao {
|
||||||
|
/// Returns all job states for a given job id
|
||||||
|
pub async fn states_for_job_id(&self, job_id: i64) -> RepoResult<Vec<JobStateDto>> {
|
||||||
|
let states = job_state::Entity::find()
|
||||||
|
.filter(job_state::Column::JobId.eq(job_id))
|
||||||
|
.all(&self.ctx.db)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(JobStateDto::new)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(states)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use mediarepo_database::entities::job;
|
||||||
|
use mediarepo_database::entities::job::JobType;
|
||||||
|
|
||||||
|
pub struct JobDto {
|
||||||
|
model: job::Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobDto {
|
||||||
|
pub(crate) fn new(model: job::Model) -> Self {
|
||||||
|
Self { model }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> i64 {
|
||||||
|
self.model.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn job_type(&self) -> JobType {
|
||||||
|
self.model.job_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Option<&String> {
|
||||||
|
self.model.name.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_run(&self) -> Option<NaiveDateTime> {
|
||||||
|
self.model.next_run
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interval(&self) -> Option<i64> {
|
||||||
|
self.model.interval
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
use mediarepo_database::entities::job_state;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct JobStateDto {
|
||||||
|
model: job_state::Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobStateDto {
|
||||||
|
pub(crate) fn new(model: job_state::Model) -> Self {
|
||||||
|
Self { model }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn job_id(&self) -> i64 {
|
||||||
|
self.model.job_id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn key(&self) -> &String {
|
||||||
|
&self.model.key
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn value(&self) -> &[u8] {
|
||||||
|
&self.model.value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_value(self) -> Vec<u8> {
|
||||||
|
self.model.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct UpsertJobStateDto {
|
||||||
|
pub job_id: i64,
|
||||||
|
pub key: String,
|
||||||
|
pub value: Vec<u8>,
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "mediarepo-worker"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
async-trait = "0.1.52"
|
||||||
|
|
||||||
|
[dependencies.mediarepo-core]
|
||||||
|
path = "../mediarepo-core"
|
||||||
|
|
||||||
|
[dependencies.mediarepo-logic]
|
||||||
|
path = "../mediarepo-logic"
|
||||||
|
|
||||||
|
[dependencies.tokio]
|
||||||
|
version = "1.17.0"
|
||||||
|
features = []
|
||||||
|
|
||||||
|
[dependencies.chrono]
|
||||||
|
version = "0.4.19"
|
||||||
|
features = ["serde"]
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0.136"
|
||||||
|
features = ["derive"]
|
@ -0,0 +1,23 @@
|
|||||||
|
use crate::state_data::StateData;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use mediarepo_core::error::RepoResult;
|
||||||
|
use mediarepo_logic::dao::repo::Repo;
|
||||||
|
use mediarepo_logic::dto::JobDto;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait ScheduledJob {
|
||||||
|
fn new(dto: JobDto) -> Self;
|
||||||
|
|
||||||
|
async fn set_state(&self, state: StateData) -> RepoResult<()>;
|
||||||
|
|
||||||
|
async fn run(&self, repo: Repo) -> RepoResult<()>;
|
||||||
|
|
||||||
|
fn execution_state(&self) -> JobExecutionState;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
|
pub enum JobExecutionState {
|
||||||
|
Scheduled,
|
||||||
|
Running,
|
||||||
|
Finished,
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
pub mod jobs;
|
||||||
|
pub mod scheduler;
|
||||||
|
pub mod state_data;
|
@ -0,0 +1,7 @@
|
|||||||
|
use mediarepo_logic::dao::repo::Repo;
|
||||||
|
|
||||||
|
pub struct Scheduler {
|
||||||
|
pub repo: Repo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {}
|
@ -0,0 +1,84 @@
|
|||||||
|
use mediarepo_core::bincode;
|
||||||
|
use mediarepo_core::error::RepoResult;
|
||||||
|
use mediarepo_logic::dao::job::JobDao;
|
||||||
|
use mediarepo_logic::dto::UpsertJobStateDto;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StateData {
|
||||||
|
job_id: i64,
|
||||||
|
inner: Arc<RwLock<HashMap<String, Vec<u8>>>>,
|
||||||
|
changed_keys: Arc<RwLock<HashSet<String>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StateData {
|
||||||
|
/// Loads the state from the database
|
||||||
|
pub async fn load(job_dao: JobDao, job_id: i64) -> RepoResult<Self> {
|
||||||
|
let states = job_dao.states_for_job_id(job_id).await?;
|
||||||
|
let states_map = states
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| (s.key().to_owned(), s.into_value()))
|
||||||
|
.collect::<HashMap<String, Vec<u8>>>();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
job_id,
|
||||||
|
inner: Arc::new(RwLock::new(states_map)),
|
||||||
|
changed_keys: Default::default(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the deserialized copy of a state object from the inner map
|
||||||
|
pub async fn entry<S: AsRef<str>, T: DeserializeOwned>(&self, key: S) -> RepoResult<Option<T>> {
|
||||||
|
let entries = self.inner.read().await;
|
||||||
|
let entry = entries.get(key.as_ref());
|
||||||
|
|
||||||
|
if let Some(bytes) = entry {
|
||||||
|
let value = bincode::deserialize(bytes)?;
|
||||||
|
|
||||||
|
Ok(Some(value))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores an entry in inner map
|
||||||
|
pub async fn store_entry<T: Serialize>(&self, key: String, value: &T) -> RepoResult<()> {
|
||||||
|
let entry_bytes = bincode::serialize(value)?;
|
||||||
|
let mut entries = self.inner.write().await;
|
||||||
|
entries.insert(key.clone(), entry_bytes);
|
||||||
|
let mut changed_entries = self.changed_keys.write().await;
|
||||||
|
changed_entries.insert(key);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list of all changed state objects as an upsert list
|
||||||
|
pub async fn changed_states(&self) -> Vec<UpsertJobStateDto> {
|
||||||
|
let mut upsert_list = Vec::new();
|
||||||
|
|
||||||
|
{
|
||||||
|
let changed_keys = self.changed_keys.read().await;
|
||||||
|
let entries = self.inner.read().await;
|
||||||
|
|
||||||
|
for key in &*changed_keys {
|
||||||
|
if let Some(value) = entries.get(key) {
|
||||||
|
upsert_list.push(UpsertJobStateDto {
|
||||||
|
job_id: self.job_id,
|
||||||
|
key: key.to_owned(),
|
||||||
|
value: value.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut changed_keys = self.changed_keys.write().await;
|
||||||
|
changed_keys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
upsert_list
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue