Compare commits

...

12 Commits
v0.3.3 ... main

Author SHA1 Message Date
680d3dbff3 chore(deps): update rust crate tokio to v1.46.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-07-03 00:26:26 +00:00
f24fb270e4 chore(release): v0.3.5 (#25)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
chore(release): 0.3.5

Co-authored-by: cuddle-please <bot@cuddle.sh>
Reviewed-on: #25
2025-05-26 13:18:37 +02:00
499fa820e9 fix: external loop
All checks were successful
continuous-integration/drone/push Build is passing
2025-05-26 13:17:08 +02:00
7018dc6dfa feat: dont spam on err 2025-05-26 13:14:01 +02:00
6b4c839596 chore(deps): update rust crate tokio to v1.45.1
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-05-25 00:24:56 +00:00
299a84e8d4 chore(deps): update rust crate tokio to v1.45.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-05-07 00:25:56 +00:00
8de06f1e62 fix(deps): update rust crate chrono to v0.4.41
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-04-30 00:25:55 +00:00
d9d94fad8e fix(deps): update rust crate tokio-util to v0.7.15
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-04-24 00:23:10 +00:00
8342822e81 chore(deps): update rust crate anyhow to v1.0.98
Some checks failed
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is failing
2025-04-14 03:24:47 +00:00
0608fbbfe6 chore(deps): update rust crate tokio to v1.44.2
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-04-06 00:24:21 +00:00
a7b5394d08 chore(release): v0.3.4 (#23)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/tag Build is passing
chore(release): 0.3.4

Co-authored-by: cuddle-please <bot@cuddle.sh>
Reviewed-on: #23
2025-03-27 15:26:54 +01:00
af10f48d8d feat: add cron
All checks were successful
continuous-integration/drone/push Build is passing
2025-03-27 15:16:57 +01:00
5 changed files with 297 additions and 51 deletions

View File

@ -6,6 +6,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.3.5] - 2025-05-26
### Added
- dont spam on err
### Fixed
- external loop
- *(deps)* update rust crate chrono to v0.4.41
- *(deps)* update rust crate tokio-util to v0.7.15
### Other
- *(deps)* update rust crate tokio to v1.45.1
- *(deps)* update rust crate tokio to v1.45.0
- *(deps)* update rust crate anyhow to v1.0.98
- *(deps)* update rust crate tokio to v1.44.2
## [0.3.4] - 2025-03-27
### Added
- add cron
## [0.3.3] - 2025-03-27
### Added

185
Cargo.lock generated
View File

@ -43,9 +43,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.97"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
[[package]]
name = "async-trait"
@ -66,9 +66,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.74"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
@ -81,9 +81,9 @@ dependencies = [
[[package]]
name = "bitflags"
version = "2.9.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bumpalo"
@ -99,9 +99,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "cc"
version = "1.2.17"
version = "1.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a"
checksum = "16595d3be041c03b09d08d0858631facccee9221e579704070e6e9e4915d3bc7"
dependencies = [
"shlex",
]
@ -114,9 +114,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.40"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
@ -132,6 +132,17 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "cron"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5877d3fbf742507b66bc2a1945106bd30dd8504019d596901ddd012a4dd01740"
dependencies = [
"chrono",
"once_cell",
"winnow",
]
[[package]]
name = "futures-core"
version = "0.3.31"
@ -152,9 +163,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "iana-time-zone"
version = "0.1.62"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2fd658b06e56721792c5df4475705b6cda790e9298d19d2f8af083457bcd127"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
@ -174,6 +185,17 @@ dependencies = [
"cc",
]
[[package]]
name = "io-uring"
version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "js-sys"
version = "0.3.77"
@ -192,9 +214,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.171"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "lock_api"
@ -229,31 +251,32 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.5"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5"
checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "nodrift"
version = "0.3.2"
version = "0.3.5"
dependencies = [
"anyhow",
"async-trait",
"chrono",
"cron",
"thiserror",
"tokio",
"tokio-util",
@ -291,9 +314,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.21.1"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "overload"
@ -332,9 +355,9 @@ checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "proc-macro2"
version = "1.0.94"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
@ -350,9 +373,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.10"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags",
]
@ -409,9 +432,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustversion"
version = "1.0.20"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
[[package]]
name = "scopeguard"
@ -436,34 +459,40 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.14.0"
name = "slab"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
[[package]]
name = "smallvec"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "socket2"
version = "0.5.8"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "syn"
version = "2.0.100"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
@ -502,20 +531,22 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.44.1"
version = "1.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
checksum = "1140bb80481756a8cbe10541f37433b459c5aa1e727b4c020fbfebdc25bf3ec4"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"slab",
"socket2",
"tokio-macros",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -531,9 +562,9 @@ dependencies = [
[[package]]
name = "tokio-util"
version = "0.7.14"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034"
checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df"
dependencies = [
"bytes",
"futures-core",
@ -725,11 +756,37 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.52.0"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-targets",
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -738,6 +795,24 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@ -747,6 +822,15 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@ -810,3 +894,12 @@ name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.6.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28"
dependencies = [
"memchr",
]

View File

@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"
[workspace.package]
version = "0.3.3"
version = "0.3.5"
[workspace.dependencies]
drift = { path = "crates/drift" }

View File

@ -9,6 +9,7 @@ edition = "2021"
anyhow.workspace = true
async-trait = "0.1.81"
chrono = "0.4.40"
cron = "0.15.0"
thiserror = "2.0.0"
tokio.workspace = true
tokio-util = "0.7.11"

View File

@ -1,8 +1,7 @@
use std::{sync::Arc, time::Duration};
use std::{str::FromStr, sync::Arc, time::Duration};
use anyhow::Context;
use async_trait::async_trait;
use chrono::{DateTime, Local, TimeDelta};
use chrono::{DateTime, Local, TimeDelta, Utc};
use std::future::Future;
use tokio::time;
use tokio_util::sync::CancellationToken;
@ -23,6 +22,86 @@ where
schedule_drifter(interval, drifter)
}
pub fn schedule_cron<F, Fut>(cron: &str, func: F) -> anyhow::Result<CancellationToken>
where
F: Fn() -> Fut + Send + Sync + 'static,
Fut: Future<Output = Result<(), DriftError>> + Send + 'static,
{
let drifter = FuncDrifter::new(func);
schedule_drifter_cron(cron, drifter)
}
pub fn schedule_drifter_cron<FDrifter>(
cron: &str,
drifter: FDrifter,
) -> anyhow::Result<CancellationToken>
where
FDrifter: Drifter + Send + 'static,
FDrifter: Clone,
{
let schedule = ::cron::Schedule::from_str(cron)?;
let cancellation_token = CancellationToken::new();
tokio::spawn({
let cancellation_token = cancellation_token.clone();
let drifter = drifter.clone();
async move {
let upcoming = schedule.upcoming(Utc {});
let child_token = cancellation_token.child_token();
for datetime in upcoming {
let now = Utc::now();
let diff = datetime - now;
if diff <= TimeDelta::zero() {
tracing::info!(
"job schedule for {} was in the past: {}, skipping iteration",
datetime.to_string(),
now.to_string()
);
continue;
}
let diff = diff.to_std().expect("to be able to get diff time");
let sleep = time::sleep(diff);
tokio::pin!(sleep);
tracing::debug!(
"schedule job: {}, waiting: {}s for execution",
datetime.to_string(),
diff.as_secs()
);
tokio::select! {
_ = cancellation_token.cancelled() => {
tracing::trace!("stopping drift job");
break
}
_ = &mut sleep => {
let start = std::time::Instant::now();
tracing::debug!("running job");
if let Err(e) = drifter.execute(child_token.child_token()).await {
tracing::error!("drift job failed with error: {}", e);
continue
}
let elapsed = start.elapsed();
tracing::debug!("job took: {}ms ", elapsed.as_millis());
}
}
}
}
});
Ok(cancellation_token)
}
pub fn schedule_drifter<FDrifter>(interval: Duration, drifter: FDrifter) -> CancellationToken
where
FDrifter: Drifter + Send + 'static,
@ -53,7 +132,9 @@ where
tracing::debug!("running job");
if let Err(e) = drifter.execute(child_token).await {
tracing::error!("drift job failed with error: {}", e);
let elapsed = start.elapsed();
wait = interval.saturating_sub(elapsed);
tracing::error!("drift job failed with error: {}, waiting: {}s before trying again", e, wait.as_secs());
continue
}
@ -231,4 +312,54 @@ mod tests {
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_cron() -> anyhow::Result<()> {
let token = schedule_cron("* * * * * *", || async {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
Ok(())
})?;
tokio::time::sleep(Duration::from_secs(5)).await;
assert!(!token.is_cancelled());
assert!(logs_contain("running job"));
assert!(logs_contain("job took:"));
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_cron_no_wait() -> anyhow::Result<()> {
let token = schedule_cron("* * * * * *", || async { Ok(()) })?;
tokio::time::sleep(Duration::from_secs(5)).await;
assert!(!token.is_cancelled());
assert!(logs_contain("running job"));
assert!(logs_contain("job took:"));
Ok(())
}
#[tokio::test]
#[traced_test]
async fn test_cron_job_taking_longer_than_cycle() -> anyhow::Result<()> {
let token = schedule_cron("* * * * * *", || async {
tokio::time::sleep(std::time::Duration::from_millis(1500)).await;
Ok(())
})?;
tokio::time::sleep(Duration::from_secs(5)).await;
assert!(!token.is_cancelled());
Ok(())
}
}