Compare commits
No commits in common. "main" and "background-jobs-v0.18.0" have entirely different histories.
main
...
background
17 changed files with 88 additions and 107 deletions
14
Cargo.toml
14
Cargo.toml
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs"
|
||||
description = "asynchronous background jobs implemented with pluggable backends and runtimes"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -44,30 +44,30 @@ completion-logging = [
|
|||
error-logging = ["background-jobs-core/error-logging"]
|
||||
|
||||
[dependencies.background-jobs-core]
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
path = "jobs-core"
|
||||
|
||||
[dependencies.background-jobs-actix]
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
path = "jobs-actix"
|
||||
optional = true
|
||||
|
||||
[dependencies.background-jobs-metrics]
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
path = "jobs-metrics"
|
||||
optional = true
|
||||
|
||||
[dependencies.background-jobs-postgres]
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
path = "jobs-postgres"
|
||||
optional = true
|
||||
|
||||
[dependencies.background-jobs-sled]
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
path = "jobs-sled"
|
||||
optional = true
|
||||
|
||||
[dependencies.background-jobs-tokio]
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
path = "jobs-tokio"
|
||||
optional = true
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
actix-rt = "2.0.0"
|
||||
background-jobs = { version = "0.19.0", path = "../..", features = [ "error-logging", "sled" ] }
|
||||
background-jobs = { version = "0.18.0", path = "../..", features = [ "error-logging", "sled" ] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
actix-rt = "2.0.0"
|
||||
background-jobs = { version = "0.19.0", path = "../..", features = [
|
||||
background-jobs = { version = "0.18.0", path = "../..", features = [
|
||||
"error-logging",
|
||||
"sled",
|
||||
] }
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
actix-rt = "2.0.0"
|
||||
background-jobs = { version = "0.19.0", path = "../..", features = [ "error-logging", "sled" ] }
|
||||
background-jobs = { version = "0.18.0", path = "../..", features = [ "error-logging", "sled" ] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
actix-rt = "2.0.0"
|
||||
background-jobs = { version = "0.19.0", path = "../..", features = [ "error-logging", "sled"] }
|
||||
background-jobs = { version = "0.18.0", path = "../..", features = [ "error-logging", "sled"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
actix-rt = "2.0.0"
|
||||
background-jobs = { version = "0.19.0", path = "../..", features = [ "error-logging", "sled" ] }
|
||||
background-jobs = { version = "0.18.0", path = "../..", features = [ "error-logging", "sled" ] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
background-jobs = { version = "0.19.0", path = "../..", default-features = false, features = [ "error-logging", "sled", "tokio" ] }
|
||||
background-jobs = { version = "0.18.0", path = "../..", default-features = false, features = [ "error-logging", "sled", "tokio" ] }
|
||||
time = "0.3"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
actix-rt = "2.9.0"
|
||||
background-jobs = { version = "0.19.0", features = ["postgres"], path = "../.." }
|
||||
background-jobs = { version = "0.18.0", features = ["postgres"], path = "../.." }
|
||||
serde = { version = "1.0.195", features = ["derive"] }
|
||||
tokio = { version = "1.35.1", features = ["full"] }
|
||||
tracing = "0.1.40"
|
||||
|
|
|
@ -7,7 +7,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
background-jobs = { version = "0.19.0", path = "../..", default-features = false, features = [ "error-logging", "sled", "tokio"] }
|
||||
background-jobs = { version = "0.18.0", path = "../..", default-features = false, features = [ "error-logging", "sled", "tokio"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt"] }
|
||||
|
|
12
flake.lock
12
flake.lock
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1720418205,
|
||||
"narHash": "sha256-cPJoFPXU44GlhWg4pUk9oUPqurPlCFZ11ZQPk21GTPU=",
|
||||
"lastModified": 1704194953,
|
||||
"narHash": "sha256-RtDKd8Mynhe5CFnVT8s0/0yqtWFMM9LmCzXv/YKxnq4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "655a58a72a6601292512670343087c2d75d859c1",
|
||||
"rev": "bd645e8668ec6612439a9ee7e71f7eac4099d4f6",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs-actix"
|
||||
description = "in-process jobs processor based on Actix"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -12,8 +12,8 @@ edition = "2021"
|
|||
[dependencies]
|
||||
actix-rt = "2.5.1"
|
||||
async-trait = "0.1.24"
|
||||
background-jobs-core = { version = "0.19.0", path = "../jobs-core" }
|
||||
metrics = "0.23.0"
|
||||
background-jobs-core = { version = "0.18.0", path = "../jobs-core" }
|
||||
metrics = "0.22.0"
|
||||
tracing = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs-core"
|
||||
description = "Core types for implementing an asynchronous jobs processor"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -19,8 +19,8 @@ error-logging = []
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.24"
|
||||
event-listener = "5.3.1"
|
||||
metrics = "0.23.0"
|
||||
event-listener = "4"
|
||||
metrics = "0.22.0"
|
||||
time = { version = "0.3", features = ["serde-human-readable"] }
|
||||
tracing = "0.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -41,6 +41,7 @@ pub mod memory_storage {
|
|||
convert::Infallible,
|
||||
future::Future,
|
||||
ops::Bound,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
sync::Mutex,
|
||||
time::Duration,
|
||||
|
@ -64,23 +65,13 @@ pub mod memory_storage {
|
|||
inner: Arc<Mutex<Inner>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct QueueTimeId(Uuid);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct JobId(Uuid);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
struct RunnerId(Uuid);
|
||||
|
||||
type OrderedKey = (String, QueueTimeId);
|
||||
type JobState = Option<(RunnerId, OffsetDateTime)>;
|
||||
type JobMeta = (JobId, time::Duration, JobState);
|
||||
type QueueMeta = (JobInfo, QueueTimeId);
|
||||
type OrderedKey = (String, Uuid);
|
||||
type JobState = Option<(Uuid, OffsetDateTime)>;
|
||||
type JobMeta = (Uuid, time::Duration, JobState);
|
||||
|
||||
struct Inner {
|
||||
queues: HashMap<String, Event>,
|
||||
jobs: HashMap<JobId, QueueMeta>,
|
||||
jobs: HashMap<Uuid, JobInfo>,
|
||||
queue_jobs: BTreeMap<OrderedKey, JobMeta>,
|
||||
}
|
||||
|
||||
|
@ -98,16 +89,11 @@ pub mod memory_storage {
|
|||
}
|
||||
|
||||
fn get(&self, job_id: Uuid) -> Option<JobInfo> {
|
||||
self.inner
|
||||
.lock()
|
||||
.unwrap()
|
||||
.jobs
|
||||
.get(&JobId(job_id))
|
||||
.map(|(job_info, _)| job_info.clone())
|
||||
self.inner.lock().unwrap().jobs.get(&job_id).cloned()
|
||||
}
|
||||
|
||||
fn listener(&self, pop_queue: String) -> (EventListener, Duration) {
|
||||
let lower_bound = QueueTimeId(Uuid::new_v7(Timestamp::from_unix(NoContext, 0, 0)));
|
||||
fn listener(&self, pop_queue: String) -> (Pin<Box<EventListener>>, Duration) {
|
||||
let lower_bound = Uuid::new_v7(Timestamp::from_unix(NoContext, 0, 0));
|
||||
let now = OffsetDateTime::now_utc();
|
||||
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
@ -122,8 +108,8 @@ pub mod memory_storage {
|
|||
))
|
||||
.filter(|(_, (_, _, meta))| meta.is_none())
|
||||
.filter_map(|(_, (id, _, _))| inner.jobs.get(id))
|
||||
.take_while(|(JobInfo { queue, .. }, _)| queue.as_str() == pop_queue.as_str())
|
||||
.map(|(JobInfo { next_queue, .. }, _)| {
|
||||
.take_while(|JobInfo { queue, .. }| queue.as_str() == pop_queue.as_str())
|
||||
.map(|JobInfo { next_queue, .. }| {
|
||||
if *next_queue > now {
|
||||
*next_queue - now
|
||||
} else {
|
||||
|
@ -136,10 +122,8 @@ pub mod memory_storage {
|
|||
}
|
||||
|
||||
fn try_pop(&self, queue: &str, runner_id: Uuid) -> Option<JobInfo> {
|
||||
let runner_id = RunnerId(runner_id);
|
||||
|
||||
let lower_bound = QueueTimeId(Uuid::new_v7(Timestamp::from_unix(NoContext, 0, 0)));
|
||||
let upper_bound = QueueTimeId(Uuid::now_v7());
|
||||
let lower_bound = Uuid::new_v7(Timestamp::from_unix(NoContext, 0, 0));
|
||||
let upper_bound = Uuid::now_v7();
|
||||
let now = time::OffsetDateTime::now_utc();
|
||||
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
@ -151,9 +135,7 @@ pub mod memory_storage {
|
|||
Bound::Included((queue.to_string(), upper_bound)),
|
||||
)) {
|
||||
if job_meta.is_none()
|
||||
|| job_meta.is_some_and(|(_, heartbeat_timestamp)| {
|
||||
heartbeat_timestamp + (5 * *heartbeat_interval) < now
|
||||
})
|
||||
|| job_meta.is_some_and(|(_, h)| h + (5 * *heartbeat_interval) < now)
|
||||
{
|
||||
*job_meta = Some((runner_id, now));
|
||||
pop_job = Some(*job_id);
|
||||
|
@ -162,61 +144,73 @@ pub mod memory_storage {
|
|||
}
|
||||
|
||||
if let Some(job_id) = pop_job {
|
||||
return inner
|
||||
.jobs
|
||||
.get(&job_id)
|
||||
.map(|(job_info, _)| job_info.clone());
|
||||
return inner.jobs.get(&job_id).cloned();
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn set_heartbeat(&self, job_id: Uuid, runner_id: Uuid) {
|
||||
let job_id = JobId(job_id);
|
||||
let runner_id = RunnerId(runner_id);
|
||||
let lower_bound = Uuid::new_v7(Timestamp::from_unix(NoContext, 0, 0));
|
||||
let upper_bound = Uuid::now_v7();
|
||||
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
let queue_key = if let Some((job, queue_time_id)) = inner.jobs.get(&job_id) {
|
||||
(job.queue.clone(), *queue_time_id)
|
||||
let queue = if let Some(job) = inner.jobs.get(&job_id) {
|
||||
job.queue.clone()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some((_, _, found_job_meta)) = inner.queue_jobs.get_mut(&queue_key) {
|
||||
for (_, (found_job_id, _, found_job_meta)) in inner.queue_jobs.range_mut((
|
||||
Bound::Excluded((queue.clone(), lower_bound)),
|
||||
Bound::Included((queue, upper_bound)),
|
||||
)) {
|
||||
if *found_job_id == job_id {
|
||||
*found_job_meta = Some((runner_id, OffsetDateTime::now_utc()));
|
||||
} else {
|
||||
metrics::counter!("background-jobs.memory.heartbeat.missing-queue-job")
|
||||
.increment(1);
|
||||
tracing::warn!("Missing job meta for {queue_key:?}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_job(&self, job_id: Uuid) -> Option<JobInfo> {
|
||||
let job_id = JobId(job_id);
|
||||
let lower_bound = Uuid::new_v7(Timestamp::from_unix(NoContext, 0, 0));
|
||||
let upper_bound = Uuid::now_v7();
|
||||
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
let (job, queue_time_id) = inner.jobs.remove(&job_id)?;
|
||||
let queue_key = (job.queue.clone(), queue_time_id);
|
||||
let job = inner.jobs.remove(&job_id)?;
|
||||
|
||||
if inner.queue_jobs.remove(&queue_key).is_none() {
|
||||
metrics::counter!("background-jobs.memory.remove.missing-queue-job").increment(1);
|
||||
tracing::warn!("failed to remove job meta for {queue_key:?}");
|
||||
let mut key = None;
|
||||
|
||||
for (found_key, (found_job_id, _, _)) in inner.queue_jobs.range_mut((
|
||||
Bound::Excluded((job.queue.clone(), lower_bound)),
|
||||
Bound::Included((job.queue.clone(), upper_bound)),
|
||||
)) {
|
||||
if *found_job_id == job_id {
|
||||
key = Some(found_key.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(key) = key {
|
||||
if inner.queue_jobs.remove(&key).is_none() {
|
||||
tracing::warn!("failed to remove {key:?}");
|
||||
}
|
||||
}
|
||||
|
||||
Some(job)
|
||||
}
|
||||
|
||||
fn insert(&self, job: JobInfo) -> Uuid {
|
||||
let id = JobId(job.id);
|
||||
let id = job.id;
|
||||
let queue = job.queue.clone();
|
||||
let queue_time_id = QueueTimeId(job.next_queue_id());
|
||||
let queue_time_id = job.next_queue_id();
|
||||
let heartbeat_interval = job.heartbeat_interval;
|
||||
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
inner.jobs.insert(id, (job, queue_time_id));
|
||||
inner.jobs.insert(id, job);
|
||||
|
||||
inner.queue_jobs.insert(
|
||||
(queue.clone(), queue_time_id),
|
||||
|
@ -229,23 +223,10 @@ pub mod memory_storage {
|
|||
|
||||
inner.queues.entry(queue).or_default().notify(1);
|
||||
|
||||
metrics::gauge!("background-jobs.memory.insert.queues")
|
||||
.set(recordable(inner.queues.len()));
|
||||
metrics::gauge!("background-jobs.memory.insert.jobs").set(recordable(inner.jobs.len()));
|
||||
metrics::gauge!("background-jobs.memory.insert.queue-jobs")
|
||||
.set(recordable(inner.queue_jobs.len()));
|
||||
|
||||
id.0
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
fn recordable(value: usize) -> u32 {
|
||||
let value = value as u64;
|
||||
let value = value % u64::from(u32::MAX);
|
||||
|
||||
value as _
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<T: Timer + Send + Sync + Clone> super::Storage for Storage<T> {
|
||||
type Error = Infallible;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs-metrics"
|
||||
description = "Metrics subscriber for accessing metrics produced by background jobs"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -13,8 +13,8 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.24"
|
||||
background-jobs-core = { version = "0.19.0", path = "../jobs-core" }
|
||||
metrics = "0.23.0"
|
||||
metrics-util = "0.17.0"
|
||||
background-jobs-core = { version = "0.18.0", path = "../jobs-core" }
|
||||
metrics = "0.22.0"
|
||||
metrics-util = "0.16.0"
|
||||
tracing = "0.1"
|
||||
uuid = { version = "1.6", features = ["serde", "v7"] }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs-postgres"
|
||||
description = "Postgres storage backend for background-jobs"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -14,7 +14,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.24"
|
||||
background-jobs-core = { version = "0.19.0", path = "../jobs-core" }
|
||||
background-jobs-core = { version = "0.18.0", path = "../jobs-core" }
|
||||
barrel = { version = "0.7.0", features = ["pg"] }
|
||||
dashmap = "5.5.3"
|
||||
deadpool = { version = "0.9", features = ["rt_tokio_1"] }
|
||||
|
@ -23,7 +23,7 @@ diesel-async = { version = "0.4.1", default-features = false, features = ["deadp
|
|||
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
|
||||
flume = "0.11.0"
|
||||
futures-core = "0.3.30"
|
||||
metrics = "0.23.0"
|
||||
metrics = "0.22.0"
|
||||
pin-project-lite = "0.2.13"
|
||||
refinery = { version = "0.8.11", features = ["postgres", "tokio-postgres"] }
|
||||
serde_json = "1.0.111"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs-sled"
|
||||
description = "Sled storage backend for background-jobs"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -13,7 +13,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.24"
|
||||
background-jobs-core = { version = "0.19.0", path = "../jobs-core" }
|
||||
background-jobs-core = { version = "0.18.0", path = "../jobs-core" }
|
||||
bincode = "1.2"
|
||||
sled = "0.34"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "background-jobs-tokio"
|
||||
description = "in-process jobs processor based on Tokio"
|
||||
version = "0.19.0"
|
||||
version = "0.18.0"
|
||||
license = "AGPL-3.0"
|
||||
authors = ["asonix <asonix@asonix.dog>"]
|
||||
repository = "https://git.asonix.dog/asonix/background-jobs"
|
||||
|
@ -13,8 +13,8 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.77"
|
||||
background-jobs-core = { version = "0.19.0", path = "../jobs-core" }
|
||||
metrics = "0.23.0"
|
||||
background-jobs-core = { version = "0.18.0", path = "../jobs-core" }
|
||||
metrics = "0.22.0"
|
||||
serde = "1.0.195"
|
||||
serde_json = "1.0.111"
|
||||
tokio = { version = "1.35.1", features = ["macros", "rt", "sync", "time", "tracing"] }
|
||||
|
|
Loading…
Reference in a new issue