temp save

This commit is contained in:
master 2025-07-04 05:59:56 +08:00
parent 147df00155
commit a1c2eeded1
17 changed files with 207 additions and 127 deletions

32
Cargo.lock generated
View File

@ -4846,15 +4846,6 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "nanoid"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ffa00dec017b5b1a8b7cf5e2c008bfda1aa7e0697ac1508b491fdf2622fb4d8"
dependencies = [
"rand 0.8.5",
]
[[package]] [[package]]
name = "native-tls" name = "native-tls"
version = "0.2.14" version = "0.2.14"
@ -6797,7 +6788,6 @@ dependencies = [
"mime_guess", "mime_guess",
"mockito", "mockito",
"moka", "moka",
"nanoid",
"nom 8.0.0", "nom 8.0.0",
"num-traits", "num-traits",
"num_cpus", "num_cpus",
@ -6836,6 +6826,7 @@ dependencies = [
"tracing", "tracing",
"tracing-appender", "tracing-appender",
"tracing-subscriber", "tracing-subscriber",
"tracing-test",
"tracing-tree", "tracing-tree",
"ts-rs", "ts-rs",
"typed-builder 0.21.0", "typed-builder 0.21.0",
@ -9244,6 +9235,27 @@ dependencies = [
"tracing-serde", "tracing-serde",
] ]
[[package]]
name = "tracing-test"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68"
dependencies = [
"tracing-core",
"tracing-subscriber",
"tracing-test-macro",
]
[[package]]
name = "tracing-test-macro"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568"
dependencies = [
"quote",
"syn 2.0.104",
]
[[package]] [[package]]
name = "tracing-tree" name = "tracing-tree"
version = "0.4.0" version = "0.4.0"

View File

@ -64,7 +64,7 @@ convert_case = "0.8"
color-eyre = "0.6.5" color-eyre = "0.6.5"
inquire = "0.7.5" inquire = "0.7.5"
image = "0.25.6" image = "0.25.6"
uuid = { version = "1.6.0", features = ["v4"] } uuid = { version = "1.6.0", features = ["v7"] }
maplit = "1.0.2" maplit = "1.0.2"
once_cell = "1.20.2" once_cell = "1.20.2"
rand = "0.9.1" rand = "0.9.1"
@ -83,5 +83,6 @@ typed-builder = "0.21.0"
nanoid = "0.4.0" nanoid = "0.4.0"
webp = "0.3.0" webp = "0.3.0"
[patch.crates-io] [patch.crates-io]
seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "9f7fc7c" } seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "9f7fc7c" }

View File

@ -97,7 +97,6 @@ tracing-appender = { workspace = true }
clap = { workspace = true } clap = { workspace = true }
ipnetwork = { workspace = true } ipnetwork = { workspace = true }
typed-builder = { workspace = true } typed-builder = { workspace = true }
nanoid = { workspace = true }
webp = { workspace = true } webp = { workspace = true }
sea-orm = { version = "1.1", features = [ sea-orm = { version = "1.1", features = [
@ -176,5 +175,6 @@ inquire = { workspace = true }
color-eyre = { workspace = true } color-eyre = { workspace = true }
serial_test = "3" serial_test = "3"
insta = { version = "1", features = ["redactions", "toml", "filters"] } insta = { version = "1", features = ["redactions", "toml", "filters"] }
rstest = "0.25"
ctor = "0.4.0" ctor = "0.4.0"
tracing-test = "0.2.5"
rstest = "0.25"

View File

@ -3,6 +3,7 @@ use std::sync::Arc;
use async_graphql::dynamic::ResolverContext; use async_graphql::dynamic::ResolverContext;
use sea_orm::Value as SeaValue; use sea_orm::Value as SeaValue;
use seaography::{Builder as SeaographyBuilder, BuilderContext, SeaResult}; use seaography::{Builder as SeaographyBuilder, BuilderContext, SeaResult};
use uuid::Uuid;
use crate::{ use crate::{
graphql::{ graphql::{
@ -35,7 +36,9 @@ pub fn register_feeds_to_schema_context(context: &mut BuilderContext) {
if field_name == entity_create_one_mutation_field_name.as_str() if field_name == entity_create_one_mutation_field_name.as_str()
|| field_name == entity_create_batch_mutation_field_name.as_str() || field_name == entity_create_batch_mutation_field_name.as_str()
{ {
Ok(Some(SeaValue::String(Some(Box::new(nanoid::nanoid!()))))) Ok(Some(SeaValue::String(Some(Box::new(
Uuid::now_v7().to_string(),
)))))
} else { } else {
Ok(None) Ok(None)
} }

View File

@ -139,7 +139,7 @@ impl MigrationTrait for Migration {
IF NEW.{next_run} IS NOT NULL IF NEW.{next_run} IS NOT NULL
AND NEW.{next_run} <= CURRENT_TIMESTAMP AND NEW.{next_run} <= CURRENT_TIMESTAMP
AND NEW.{enabled} = true AND NEW.{enabled} = true
AND NEW.{status} = '{pending}' AND NEW.{status} = '{pending}'::{status_type}
AND NEW.{attempts} < NEW.{max_attempts} AND NEW.{attempts} < NEW.{max_attempts}
-- Check if not locked or lock timeout -- Check if not locked or lock timeout
AND ( AND (
@ -171,6 +171,7 @@ impl MigrationTrait for Migration {
pending = &CronStatus::Pending.to_value(), pending = &CronStatus::Pending.to_value(),
attempts = &Cron::Attempts.to_string(), attempts = &Cron::Attempts.to_string(),
max_attempts = &Cron::MaxAttempts.to_string(), max_attempts = &Cron::MaxAttempts.to_string(),
status_type = &CronStatus::name().to_string(),
)) ))
.await?; .await?;
@ -194,7 +195,7 @@ impl MigrationTrait for Migration {
WHERE {next_run} IS NOT NULL WHERE {next_run} IS NOT NULL
AND {next_run} <= CURRENT_TIMESTAMP AND {next_run} <= CURRENT_TIMESTAMP
AND {enabled} = true AND {enabled} = true
AND {status} = '{pending}' AND {status} = '{pending}'::{status_type}
AND {attempts} < {max_attempts} AND {attempts} < {max_attempts}
AND ( AND (
{locked_at} IS NULL {locked_at} IS NULL
@ -222,6 +223,7 @@ impl MigrationTrait for Migration {
priority = &Cron::Priority.to_string(), priority = &Cron::Priority.to_string(),
attempts = &Cron::Attempts.to_string(), attempts = &Cron::Attempts.to_string(),
max_attempts = &Cron::MaxAttempts.to_string(), max_attempts = &Cron::MaxAttempts.to_string(),
status_type = &CronStatus::name().to_string(),
)) ))
.await?; .await?;

View File

@ -102,7 +102,7 @@ impl ActiveModelBehavior for ActiveModel {
C: ConnectionTrait, C: ConnectionTrait,
{ {
if insert && let ActiveValue::NotSet = self.token { if insert && let ActiveValue::NotSet = self.token {
let token = nanoid::nanoid!(10); let token = Uuid::now_v7().to_string();
self.token = ActiveValue::Set(token); self.token = ActiveValue::Set(token);
} }
Ok(self) Ok(self)

View File

@ -278,7 +278,7 @@ impl StorageService {
if let Some(mut ranges) = ranges { if let Some(mut ranges) = ranges {
if ranges.len() > 1 { if ranges.len() > 1 {
let boundary = Uuid::new_v4().to_string(); let boundary = Uuid::now_v7().to_string();
let reader = self.reader(storage_path.as_ref()).await?; let reader = self.reader(storage_path.as_ref()).await?;
let stream: impl Stream<Item = Result<Bytes, RecorderError>> = { let stream: impl Stream<Item = Result<Bytes, RecorderError>> = {
let boundary = boundary.clone(); let boundary = boundary.clone();

View File

@ -14,6 +14,8 @@ pub struct TaskConfig {
pub system_task_reenqueue_orphaned_after: Duration, pub system_task_reenqueue_orphaned_after: Duration,
#[serde(default = "default_cron_retry_duration")] #[serde(default = "default_cron_retry_duration")]
pub cron_retry_duration: Duration, pub cron_retry_duration: Duration,
#[serde(default = "default_cron_interval_duration")]
pub cron_interval_duration: Duration,
} }
impl Default for TaskConfig { impl Default for TaskConfig {
@ -25,6 +27,7 @@ impl Default for TaskConfig {
default_subscriber_task_reenqueue_orphaned_after(), default_subscriber_task_reenqueue_orphaned_after(),
system_task_reenqueue_orphaned_after: default_system_task_reenqueue_orphaned_after(), system_task_reenqueue_orphaned_after: default_system_task_reenqueue_orphaned_after(),
cron_retry_duration: default_cron_retry_duration(), cron_retry_duration: default_cron_retry_duration(),
cron_interval_duration: default_cron_interval_duration(),
} }
} }
} }
@ -45,6 +48,10 @@ pub fn default_system_task_workers() -> u32 {
} }
} }
pub fn default_cron_interval_duration() -> Duration {
Duration::from_secs(60)
}
pub fn default_subscriber_task_reenqueue_orphaned_after() -> Duration { pub fn default_subscriber_task_reenqueue_orphaned_after() -> Duration {
Duration::from_secs(3600) Duration::from_secs(3600)
} }

View File

@ -11,7 +11,7 @@ pub use core::{
pub use config::TaskConfig; pub use config::TaskConfig;
pub use registry::{ pub use registry::{
OptimizeImageTask, SubscriberTask, SubscriberTaskInput, SubscriberTaskType, EchoTask, OptimizeImageTask, SubscriberTask, SubscriberTaskInput, SubscriberTaskType,
SubscriberTaskTypeEnum, SubscriberTaskTypeVariant, SubscriberTaskTypeVariantIter, SubscriberTaskTypeEnum, SubscriberTaskTypeVariant, SubscriberTaskTypeVariantIter,
SyncOneSubscriptionFeedsFullTask, SyncOneSubscriptionFeedsIncrementalTask, SyncOneSubscriptionFeedsFullTask, SyncOneSubscriptionFeedsIncrementalTask,
SyncOneSubscriptionSourcesTask, SystemTask, SystemTaskInput, SystemTaskType, SyncOneSubscriptionSourcesTask, SystemTask, SystemTaskInput, SystemTaskType,

View File

@ -9,6 +9,6 @@ pub use subscriber::{
}; };
pub(crate) use system::register_system_task_type; pub(crate) use system::register_system_task_type;
pub use system::{ pub use system::{
OptimizeImageTask, SystemTask, SystemTaskInput, SystemTaskType, SystemTaskTypeEnum, EchoTask, OptimizeImageTask, SystemTask, SystemTaskInput, SystemTaskType, SystemTaskTypeEnum,
SystemTaskTypeVariant, SystemTaskTypeVariantIter, SystemTaskTypeVariant, SystemTaskTypeVariantIter,
}; };

View File

@ -0,0 +1,29 @@
use std::sync::Arc;
use chrono::Utc;
use crate::{
app::AppContextTrait,
errors::RecorderResult,
task::{AsyncTaskTrait, register_system_task_type},
};
register_system_task_type! {
#[derive(Debug, Clone, PartialEq)]
pub struct EchoTask {
pub task_id: String,
}
}
#[async_trait::async_trait]
impl AsyncTaskTrait for EchoTask {
async fn run_async(self, _ctx: Arc<dyn AppContextTrait>) -> RecorderResult<()> {
tracing::info!(
"EchoTask {} start running at {}",
self.task_id,
Utc::now().to_rfc3339()
);
Ok(())
}
}

View File

@ -1,8 +1,10 @@
mod base; mod base;
mod media; mod media;
mod misc;
pub(crate) use base::register_system_task_type; pub(crate) use base::register_system_task_type;
pub use media::OptimizeImageTask; pub use media::OptimizeImageTask;
pub use misc::EchoTask;
use sea_orm::{DeriveActiveEnum, DeriveDisplay, EnumIter, FromJsonQueryResult}; use sea_orm::{DeriveActiveEnum, DeriveDisplay, EnumIter, FromJsonQueryResult};
macro_rules! register_system_task_types { macro_rules! register_system_task_types {
@ -131,30 +133,6 @@ macro_rules! register_system_task_types {
}; };
} }
#[cfg(not(any(test, feature = "test-utils")))]
register_system_task_types! {
task_type_enum: {
#[derive(
Clone,
Debug,
Copy,
DeriveActiveEnum,
DeriveDisplay,
EnumIter,
)]
pub enum SystemTaskType {
OptimizeImage => "optimize_image"
}
},
task_enum: {
#[derive(Clone, Debug, FromJsonQueryResult)]
pub enum SystemTask {
OptimizeImage(OptimizeImageTask)
}
}
}
#[cfg(any(test, feature = "test-utils"))]
register_system_task_types! { register_system_task_types! {
task_type_enum: { task_type_enum: {
#[derive( #[derive(
@ -174,7 +152,7 @@ register_system_task_types! {
#[derive(Clone, Debug, FromJsonQueryResult)] #[derive(Clone, Debug, FromJsonQueryResult)]
pub enum SystemTask { pub enum SystemTask {
OptimizeImage(OptimizeImageTask), OptimizeImage(OptimizeImageTask),
Test(crate::test_utils::task::TestSystemTask), Echo(EchoTask),
} }
} }
} }

View File

@ -6,8 +6,9 @@ use apalis_sql::{
context::SqlContext, context::SqlContext,
postgres::{PgListen as ApalisPgListen, PostgresStorage as ApalisPostgresStorage}, postgres::{PgListen as ApalisPgListen, PostgresStorage as ApalisPostgresStorage},
}; };
use sea_orm::sqlx::postgres::PgListener; use sea_orm::{ActiveModelTrait, sqlx::postgres::PgListener};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use uuid::Uuid;
use crate::{ use crate::{
app::AppContextTrait, app::AppContextTrait,
@ -53,7 +54,7 @@ impl TaskService {
Ok(Self { Ok(Self {
config, config,
cron_worker_id: nanoid::nanoid!(), cron_worker_id: Uuid::now_v7().to_string(),
ctx, ctx,
subscriber_task_storage: Arc::new(RwLock::new(subscriber_task_storage)), subscriber_task_storage: Arc::new(RwLock::new(subscriber_task_storage)),
system_task_storage: Arc::new(RwLock::new(system_task_storage)), system_task_storage: Arc::new(RwLock::new(system_task_storage)),
@ -136,6 +137,21 @@ impl TaskService {
Ok(task_id) Ok(task_id)
} }
pub async fn add_subscriber_task_cron(
&self,
cm: cron::ActiveModel,
) -> RecorderResult<cron::Model> {
let db = self.ctx.db();
let m = cm.insert(db).await?;
Ok(m)
}
pub async fn add_system_task_cron(&self, cm: cron::ActiveModel) -> RecorderResult<cron::Model> {
let db = self.ctx.db();
let m = cm.insert(db).await?;
Ok(m)
}
pub async fn run<F, Fut>(&self, shutdown_signal: Option<F>) -> RecorderResult<()> pub async fn run<F, Fut>(&self, shutdown_signal: Option<F>) -> RecorderResult<()>
where where
F: Fn() -> Fut + Send + 'static, F: Fn() -> Fut + Send + 'static,
@ -167,30 +183,31 @@ impl TaskService {
Ok::<_, RecorderError>(()) Ok::<_, RecorderError>(())
}, },
async { async {
let listener = self.setup_cron_due_listening().await?; let mut listener = self.setup_cron_due_listening().await?;
let ctx = self.ctx.clone();
let cron_worker_id = self.cron_worker_id.clone(); let cron_worker_id = self.cron_worker_id.clone();
let retry_duration = chrono::Duration::milliseconds( let retry_duration = chrono::Duration::milliseconds(
self.config.cron_retry_duration.as_millis() as i64, self.config.cron_retry_duration.as_millis() as i64,
); );
let cron_interval_duration = self.config.cron_interval_duration;
listener.listen(CRON_DUE_EVENT).await?;
tracing::debug!("Listening for cron due event...");
tokio::task::spawn(async move { tokio::task::spawn({
let ctx = self.ctx.clone();
async move {
if let Err(e) = if let Err(e) =
Self::listen_cron_due(listener, ctx, &cron_worker_id, retry_duration).await Self::listen_cron_due(listener, ctx, &cron_worker_id, retry_duration)
.await
{ {
tracing::error!("Error listening to cron due: {e}"); tracing::error!("Error listening to cron due: {e}");
} }
}
}); });
Ok::<_, RecorderError>(()) tokio::task::spawn({
},
async {
let ctx = self.ctx.clone(); let ctx = self.ctx.clone();
let retry_duration = chrono::Duration::milliseconds( async move {
self.config.cron_retry_duration.as_millis() as i64, let mut interval = tokio::time::interval(cron_interval_duration);
);
tokio::task::spawn(async move {
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(60));
loop { loop {
interval.tick().await; interval.tick().await;
if let Err(e) = cron::Model::check_and_cleanup_expired_cron_locks( if let Err(e) = cron::Model::check_and_cleanup_expired_cron_locks(
@ -203,11 +220,13 @@ impl TaskService {
"Error checking and cleaning up expired cron locks: {e}" "Error checking and cleaning up expired cron locks: {e}"
); );
} }
if let Err(e) = cron::Model::check_and_trigger_due_crons(ctx.as_ref()).await if let Err(e) =
cron::Model::check_and_trigger_due_crons(ctx.as_ref()).await
{ {
tracing::error!("Error checking and triggering due crons: {e}"); tracing::error!("Error checking and triggering due crons: {e}");
} }
} }
}
}); });
Ok::<_, RecorderError>(()) Ok::<_, RecorderError>(())
@ -267,6 +286,7 @@ impl TaskService {
async fn setup_cron_due_listening(&self) -> RecorderResult<PgListener> { async fn setup_cron_due_listening(&self) -> RecorderResult<PgListener> {
let pool = self.ctx.db().get_postgres_connection_pool().clone(); let pool = self.ctx.db().get_postgres_connection_pool().clone();
let listener = PgListener::connect_with(&pool).await?; let listener = PgListener::connect_with(&pool).await?;
tracing::debug!("Cron due listener connected to postgres");
Ok(listener) Ok(listener)
} }
@ -277,10 +297,9 @@ impl TaskService {
worker_id: &str, worker_id: &str,
retry_duration: chrono::Duration, retry_duration: chrono::Duration,
) -> RecorderResult<()> { ) -> RecorderResult<()> {
listener.listen(CRON_DUE_EVENT).await?;
loop { loop {
let notification = listener.recv().await?; let notification = listener.recv().await?;
tracing::debug!("Received cron due event: {:?}", notification);
if let Err(e) = cron::Model::handle_cron_notification( if let Err(e) = cron::Model::handle_cron_notification(
ctx.as_ref(), ctx.as_ref(),
notification, notification,
@ -298,13 +317,20 @@ impl TaskService {
#[cfg(test)] #[cfg(test)]
#[allow(unused_variables)] #[allow(unused_variables)]
mod tests { mod tests {
use std::time::Duration;
use rstest::{fixture, rstest}; use rstest::{fixture, rstest};
use sea_orm::ActiveValue;
use tracing::Level; use tracing::Level;
use super::*; use super::*;
use crate::test_utils::{ use crate::{
// app::TestingPreset, models::cron,
task::EchoTask,
test_utils::{
app::{TestingAppContextConfig, TestingPreset},
tracing::try_init_testing_tracing, tracing::try_init_testing_tracing,
},
}; };
#[fixture] #[fixture]
@ -314,7 +340,40 @@ mod tests {
#[rstest] #[rstest]
#[tokio::test] #[tokio::test]
// #[tracing_test::traced_test]
async fn test_cron_due_listening(before_each: ()) -> RecorderResult<()> { async fn test_cron_due_listening(before_each: ()) -> RecorderResult<()> {
todo!() let preset = TestingPreset::default_with_config(
TestingAppContextConfig::builder()
.task_config(TaskConfig {
cron_interval_duration: Duration::from_secs(1),
..Default::default()
})
.build(),
)
.await?;
let app_ctx = preset.app_ctx;
let task_service = app_ctx.task();
let task_id = Uuid::now_v7().to_string();
let echo_cron = cron::ActiveModel {
cron_expr: ActiveValue::Set("*/1 * * * * *".to_string()),
system_task_cron: ActiveValue::Set(Some(
EchoTask::builder().task_id(task_id.clone()).build().into(),
)),
..Default::default()
};
let _ = task_service
.run(Some(async move || {
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
}))
.await;
// assert!(logs_contain(&format!(
// "EchoTask {task_id} start running at"
// )));
Ok(())
} }
} }

View File

@ -6,6 +6,7 @@ use typed_builder::TypedBuilder;
use crate::{ use crate::{
app::AppContextTrait, app::AppContextTrait,
errors::RecorderResult, errors::RecorderResult,
task::TaskConfig,
test_utils::{ test_utils::{
crypto::build_testing_crypto_service, crypto::build_testing_crypto_service,
database::{TestingDatabaseServiceConfig, build_testing_database_service}, database::{TestingDatabaseServiceConfig, build_testing_database_service},
@ -43,10 +44,11 @@ impl TestingAppContext {
self.task.get_or_init(|| task); self.task.get_or_init(|| task);
} }
pub async fn from_preset(preset: TestingAppContextPreset) -> RecorderResult<Arc<Self>> { pub async fn from_config(config: TestingAppContextConfig) -> RecorderResult<Arc<Self>> {
let mikan_client = build_testing_mikan_client(preset.mikan_base_url).await?; let mikan_base_url = config.mikan_base_url.expect("mikan_base_url is required");
let mikan_client = build_testing_mikan_client(mikan_base_url).await?;
let db_service = let db_service =
build_testing_database_service(preset.database_config.unwrap_or_default()).await?; build_testing_database_service(config.database_config.unwrap_or_default()).await?;
let crypto_service = build_testing_crypto_service().await?; let crypto_service = build_testing_crypto_service().await?;
let storage_service = build_testing_storage_service().await?; let storage_service = build_testing_storage_service().await?;
let media_service = build_testing_media_service().await?; let media_service = build_testing_media_service().await?;
@ -132,9 +134,12 @@ impl AppContextTrait for TestingAppContext {
} }
} }
pub struct TestingAppContextPreset { #[derive(TypedBuilder)]
pub mikan_base_url: String, #[builder(field_defaults(default, setter(strip_option)))]
pub struct TestingAppContextConfig {
pub mikan_base_url: Option<String>,
pub database_config: Option<TestingDatabaseServiceConfig>, pub database_config: Option<TestingDatabaseServiceConfig>,
pub task_config: Option<TaskConfig>,
} }
#[derive(TypedBuilder)] #[derive(TypedBuilder)]
@ -144,15 +149,15 @@ pub struct TestingPreset {
} }
impl TestingPreset { impl TestingPreset {
pub async fn default() -> RecorderResult<Self> { pub async fn default_with_config(config: TestingAppContextConfig) -> RecorderResult<Self> {
let mikan_server = MikanMockServer::new().await?; let mikan_server = MikanMockServer::new().await?;
let database_config = TestingDatabaseServiceConfig::default();
let app_ctx = TestingAppContext::from_preset(TestingAppContextPreset { let mixed_config = TestingAppContextConfig {
mikan_base_url: mikan_server.base_url().to_string(), mikan_base_url: Some(mikan_server.base_url().to_string()),
database_config: Some(database_config), ..config
}) };
.await?;
let app_ctx = TestingAppContext::from_config(mixed_config).await?;
let preset = Self::builder() let preset = Self::builder()
.mikan_server(mikan_server) .mikan_server(mikan_server)
@ -160,4 +165,13 @@ impl TestingPreset {
.build(); .build();
Ok(preset) Ok(preset)
} }
pub async fn default() -> RecorderResult<Self> {
Self::default_with_config(TestingAppContextConfig {
mikan_base_url: None,
database_config: None,
task_config: None,
})
.await
}
} }

View File

@ -52,7 +52,7 @@ pub async fn build_testing_database_service(
uri: connection_string, uri: connection_string,
enable_logging: true, enable_logging: true,
min_connections: 1, min_connections: 1,
max_connections: 1, max_connections: 5,
connect_timeout: 5000, connect_timeout: 5000,
idle_timeout: 10000, idle_timeout: 10000,
acquire_timeout: None, acquire_timeout: None,

View File

@ -1,42 +1,17 @@
use std::sync::Arc; use std::sync::Arc;
use chrono::Utc;
use crate::{ use crate::{
app::AppContextTrait, app::AppContextTrait,
errors::RecorderResult, errors::RecorderResult,
task::{AsyncTaskTrait, TaskConfig, TaskService, register_system_task_type}, task::{TaskConfig, TaskService},
}; };
register_system_task_type! {
#[derive(Debug, Clone, PartialEq)]
pub struct TestSystemTask {
pub task_id: String,
}
}
#[async_trait::async_trait]
impl AsyncTaskTrait for TestSystemTask {
async fn run_async(self, ctx: Arc<dyn AppContextTrait>) -> RecorderResult<()> {
let storage = ctx.storage();
storage
.write(
storage.build_test_path(self.task_id),
serde_json::json!({ "exec_time": Utc::now().timestamp_millis() })
.to_string()
.into(),
)
.await?;
Ok(())
}
}
pub async fn build_testing_task_service( pub async fn build_testing_task_service(
ctx: Arc<dyn AppContextTrait>, ctx: Arc<dyn AppContextTrait>,
) -> RecorderResult<TaskService> { ) -> RecorderResult<TaskService> {
let config = TaskConfig::default(); let config = TaskConfig {
..Default::default()
};
let task_service = TaskService::from_config_and_ctx(config, ctx).await?; let task_service = TaskService::from_config_and_ctx(config, ctx).await?;
Ok(task_service) Ok(task_service)

View File

@ -110,7 +110,7 @@ fn make_request_id(maybe_request_id: Option<HeaderValue>) -> String {
}); });
id.filter(|s| !s.is_empty()) id.filter(|s| !s.is_empty())
}) })
.unwrap_or_else(|| Uuid::new_v4().to_string()) .unwrap_or_else(|| Uuid::now_v7().to_string())
} }
#[cfg(test)] #[cfg(test)]