Add three bins using crate tokio-cron-scheduler.
This commit is contained in:
		
							parent
							
								
									c76a20e37c
								
							
						
					
					
						commit
						692756e2cf
					
				
					 5 changed files with 393 additions and 0 deletions
				
			
		
							
								
								
									
										49
									
								
								src/bin/using-crate-tokio-cron-scheduler-postgres_job.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/bin/using-crate-tokio-cron-scheduler-postgres_job.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
use jobs::tcs_helpers::{run_example, stop_example};
 | 
			
		||||
use tokio_cron_scheduler::{
 | 
			
		||||
    JobScheduler, PostgresMetadataStore, PostgresNotificationStore, SimpleJobCode,
 | 
			
		||||
    SimpleNotificationCode,
 | 
			
		||||
};
 | 
			
		||||
use tracing::{info, Level};
 | 
			
		||||
use tracing_subscriber::FmtSubscriber;
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() {
 | 
			
		||||
    let subscriber = FmtSubscriber::builder()
 | 
			
		||||
        .with_max_level(Level::DEBUG)
 | 
			
		||||
        .finish();
 | 
			
		||||
    tracing::subscriber::set_global_default(subscriber).expect("Setting default subscriber failed");
 | 
			
		||||
 | 
			
		||||
    info!("Remember to have a running Postgres instance to connect to. For example:\n");
 | 
			
		||||
    info!("docker run --rm -it -p 5432:5432 -e POSTGRES_USER=\"postgres\" -e POSTGRES_PASSWORD=\"\" -e POSTGRES_HOST_AUTH_METHOD=\"trust\" postgres:14.1");
 | 
			
		||||
 | 
			
		||||
    let metadata_storage = Box::new(PostgresMetadataStore::default());
 | 
			
		||||
    let notification_storage = Box::new(PostgresNotificationStore::default());
 | 
			
		||||
    if std::env::var("POSTGRES_INIT_METADATA").is_err() {
 | 
			
		||||
        info!("Set to not initialize the job metadata tables. POSTGRES_INIT_METADATA=false");
 | 
			
		||||
    }
 | 
			
		||||
    if std::env::var("POSTGRES_INIT_NOTIFICATIONS").is_err() {
 | 
			
		||||
        info!(
 | 
			
		||||
            "Set to not initialization of notification tables. POSTGRES_INIT_NOTIFICATIONS=false"
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let simple_job_code = Box::new(SimpleJobCode::default());
 | 
			
		||||
    let simple_notification_code = Box::new(SimpleNotificationCode::default());
 | 
			
		||||
 | 
			
		||||
    let mut sched = JobScheduler::new_with_storage_and_code(
 | 
			
		||||
        metadata_storage,
 | 
			
		||||
        notification_storage,
 | 
			
		||||
        simple_job_code,
 | 
			
		||||
        simple_notification_code,
 | 
			
		||||
        200,
 | 
			
		||||
    )
 | 
			
		||||
    .await
 | 
			
		||||
    .unwrap();
 | 
			
		||||
 | 
			
		||||
    let jobs = run_example(&mut sched)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Could not run example");
 | 
			
		||||
    stop_example(&mut sched, jobs)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Could not stop example");
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								src/bin/using-crate-tokio-cron-scheduler-simple_job.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/bin/using-crate-tokio-cron-scheduler-simple_job.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
use jobs::tcs_helpers::{run_example, stop_example};
 | 
			
		||||
use tokio_cron_scheduler::JobScheduler;
 | 
			
		||||
use tracing::Level;
 | 
			
		||||
use tracing_subscriber::FmtSubscriber;
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() {
 | 
			
		||||
    let subscriber = FmtSubscriber::builder()
 | 
			
		||||
        .with_max_level(Level::TRACE)
 | 
			
		||||
        .finish();
 | 
			
		||||
    tracing::subscriber::set_global_default(subscriber).expect("Setting default subscriber failed");
 | 
			
		||||
    let sched = JobScheduler::new_with_channel_size(1000).await;
 | 
			
		||||
    let mut sched = sched.unwrap();
 | 
			
		||||
    let jobs = run_example(&mut sched)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Could not run example");
 | 
			
		||||
    stop_example(&mut sched, jobs)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Could not stop example");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
use jobs::tcs_helpers::{run_example, stop_example};
 | 
			
		||||
use std::error::Error;
 | 
			
		||||
use tokio_cron_scheduler::JobScheduler;
 | 
			
		||||
use tracing::{info, Level};
 | 
			
		||||
use tracing_subscriber::FmtSubscriber;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let handle = std::thread::Builder::new()
 | 
			
		||||
        .name("schedule thread".to_string())
 | 
			
		||||
        .spawn(move || {
 | 
			
		||||
            // tokio::runtime::Builder::new_current_thread()    <- This hangs
 | 
			
		||||
            tokio::runtime::Builder::new_multi_thread()
 | 
			
		||||
                .enable_all()
 | 
			
		||||
                .build()
 | 
			
		||||
                .expect("build runtime failed")
 | 
			
		||||
                .block_on(start())
 | 
			
		||||
                .expect("TODO: panic message");
 | 
			
		||||
        })
 | 
			
		||||
        .expect("spawn thread failed");
 | 
			
		||||
    handle.join().expect("join failed");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn start() -> Result<(), Box<dyn Error>> {
 | 
			
		||||
    let subscriber = FmtSubscriber::builder()
 | 
			
		||||
        .with_max_level(Level::TRACE)
 | 
			
		||||
        .finish();
 | 
			
		||||
    tracing::subscriber::set_global_default(subscriber).expect("Setting default subscriber failed");
 | 
			
		||||
    info!("Creating scheduler");
 | 
			
		||||
    let mut sched = JobScheduler::new().await?;
 | 
			
		||||
    info!("Run example");
 | 
			
		||||
    let jobs = run_example(&mut sched)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Could not run example");
 | 
			
		||||
    stop_example(&mut sched, jobs)
 | 
			
		||||
        .await
 | 
			
		||||
        .expect("Could not stop example");
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/lib.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
pub mod tcs_helpers;
 | 
			
		||||
							
								
								
									
										285
									
								
								src/tcs_helpers.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								src/tcs_helpers.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,285 @@
 | 
			
		|||
use chrono::Utc;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use tokio_cron_scheduler::{Job, JobBuilder, JobScheduler, JobSchedulerError};
 | 
			
		||||
use tracing::{error, info, warn};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
pub async fn run_example(sched: &mut JobScheduler) -> Result<Vec<Uuid>, JobSchedulerError> {
 | 
			
		||||
    #[cfg(feature = "signal")]
 | 
			
		||||
    sched.shutdown_on_ctrl_c();
 | 
			
		||||
 | 
			
		||||
    sched.set_shutdown_handler(Box::new(|| {
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            info!("Shut down done");
 | 
			
		||||
        })
 | 
			
		||||
    }));
 | 
			
		||||
 | 
			
		||||
    let mut five_s_job = Job::new("1/5 * * * * *", |uuid, _l| {
 | 
			
		||||
        info!(
 | 
			
		||||
            "{:?} I run every 5 seconds id {:?}",
 | 
			
		||||
            chrono::Utc::now(),
 | 
			
		||||
            uuid
 | 
			
		||||
        );
 | 
			
		||||
    })
 | 
			
		||||
    .unwrap();
 | 
			
		||||
 | 
			
		||||
    // Adding a job notification without it being added to the scheduler will automatically add it to
 | 
			
		||||
    // the job store, but with stopped marking
 | 
			
		||||
    five_s_job
 | 
			
		||||
        .on_removed_notification_add(
 | 
			
		||||
            &sched,
 | 
			
		||||
            Box::new(|job_id, notification_id, type_of_notification| {
 | 
			
		||||
                Box::pin(async move {
 | 
			
		||||
                    info!(
 | 
			
		||||
                        "5s Job {:?} was removed, notification {:?} ran ({:?})",
 | 
			
		||||
                        job_id, notification_id, type_of_notification
 | 
			
		||||
                    );
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
    let five_s_job_guid = five_s_job.guid();
 | 
			
		||||
    sched.add(five_s_job).await?;
 | 
			
		||||
 | 
			
		||||
    let mut four_s_job_async = Job::new_async_tz("1/4 * * * * *", Utc, |uuid, mut l| {
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            info!("I run async every 4 seconds id {:?}", uuid);
 | 
			
		||||
            let next_tick = l.next_tick_for_job(uuid).await;
 | 
			
		||||
            match next_tick {
 | 
			
		||||
                Ok(Some(ts)) => info!("Next time for 4s is {:?}", ts),
 | 
			
		||||
                _ => warn!("Could not get next tick for 4s job"),
 | 
			
		||||
            }
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
    .unwrap();
 | 
			
		||||
    let four_s_job_async_clone = four_s_job_async.clone();
 | 
			
		||||
    let js = sched.clone();
 | 
			
		||||
    info!("4s job id {:?}", four_s_job_async.guid());
 | 
			
		||||
    four_s_job_async.on_start_notification_add(&sched, Box::new(move |job_id, notification_id, type_of_notification| {
 | 
			
		||||
        let four_s_job_async_clone = four_s_job_async_clone.clone();
 | 
			
		||||
        let js = js.clone();
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            info!("4s Job {:?} ran on start notification {:?} ({:?})", job_id, notification_id, type_of_notification);
 | 
			
		||||
            info!("This should only run once since we're going to remove this notification immediately.");
 | 
			
		||||
            info!("Removed? {:?}", four_s_job_async_clone.on_start_notification_remove(&js, ¬ification_id).await);
 | 
			
		||||
        })
 | 
			
		||||
    })).await?;
 | 
			
		||||
 | 
			
		||||
    four_s_job_async
 | 
			
		||||
        .on_done_notification_add(
 | 
			
		||||
            &sched,
 | 
			
		||||
            Box::new(|job_id, notification_id, type_of_notification| {
 | 
			
		||||
                Box::pin(async move {
 | 
			
		||||
                    info!(
 | 
			
		||||
                        "4s Job {:?} completed and ran notification {:?} ({:?})",
 | 
			
		||||
                        job_id, notification_id, type_of_notification
 | 
			
		||||
                    );
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    let four_s_job_guid = four_s_job_async.guid();
 | 
			
		||||
    sched.add(four_s_job_async).await?;
 | 
			
		||||
 | 
			
		||||
    sched
 | 
			
		||||
        .add(
 | 
			
		||||
            Job::new("1/30 * * * * *", |uuid, _l| {
 | 
			
		||||
                info!("I run every 30 seconds id {:?}", uuid);
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    info!(
 | 
			
		||||
        "Sched one shot for {:?}",
 | 
			
		||||
        chrono::Utc::now()
 | 
			
		||||
            .checked_add_signed(chrono::Duration::seconds(10))
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    );
 | 
			
		||||
    sched
 | 
			
		||||
        .add(
 | 
			
		||||
            Job::new_one_shot(Duration::from_secs(10), |_uuid, _l| {
 | 
			
		||||
                info!("I'm only run once");
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    info!(
 | 
			
		||||
        "Sched one shot async for {:?}",
 | 
			
		||||
        chrono::Utc::now()
 | 
			
		||||
            .checked_add_signed(chrono::Duration::seconds(16))
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    );
 | 
			
		||||
    sched
 | 
			
		||||
        .add(
 | 
			
		||||
            Job::new_one_shot_async(Duration::from_secs(16), |_uuid, _l| {
 | 
			
		||||
                Box::pin(async move {
 | 
			
		||||
                    info!("I'm only run once async");
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    let jj = Job::new_repeated(Duration::from_secs(8), |_uuid, _l| {
 | 
			
		||||
        info!("I'm repeated every 8 seconds");
 | 
			
		||||
    })
 | 
			
		||||
    .unwrap();
 | 
			
		||||
    let jj_guid = jj.guid();
 | 
			
		||||
    sched.add(jj).await?;
 | 
			
		||||
 | 
			
		||||
    let jja = Job::new_repeated_async(Duration::from_secs(7), |_uuid, _l| {
 | 
			
		||||
        Box::pin(async move {
 | 
			
		||||
            info!("I'm repeated async every 7 seconds");
 | 
			
		||||
        })
 | 
			
		||||
    })
 | 
			
		||||
    .unwrap();
 | 
			
		||||
    let jja_guid = jja.guid();
 | 
			
		||||
    sched.add(jja).await?;
 | 
			
		||||
 | 
			
		||||
    let utc_job = JobBuilder::new()
 | 
			
		||||
        .with_timezone(Utc)
 | 
			
		||||
        .with_cron_job_type()
 | 
			
		||||
        .with_schedule("*/2 * * * * *")
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .with_run_async(Box::new(|uuid, mut l| {
 | 
			
		||||
            Box::pin(async move {
 | 
			
		||||
                info!("UTC run async every 2 seconds id {:?}", uuid);
 | 
			
		||||
                let next_tick = l.next_tick_for_job(uuid).await;
 | 
			
		||||
                match next_tick {
 | 
			
		||||
                    Ok(Some(ts)) => info!("Next time for UTC 2s is {:?}", ts),
 | 
			
		||||
                    _ => warn!("Could not get next tick for 2s job"),
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }))
 | 
			
		||||
        .build()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let utc_job_guid = utc_job.guid();
 | 
			
		||||
    sched.add(utc_job).await.unwrap();
 | 
			
		||||
 | 
			
		||||
    let jhb_job = JobBuilder::new()
 | 
			
		||||
        .with_timezone(chrono_tz::Africa::Johannesburg)
 | 
			
		||||
        .with_cron_job_type()
 | 
			
		||||
        .with_schedule("*/2 * * * * *")
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .with_run_async(Box::new(|uuid, mut l| {
 | 
			
		||||
            Box::pin(async move {
 | 
			
		||||
                info!("JHB run async every 2 seconds id {:?}", uuid);
 | 
			
		||||
                let next_tick = l.next_tick_for_job(uuid).await;
 | 
			
		||||
                match next_tick {
 | 
			
		||||
                    Ok(Some(ts)) => info!("Next time for JHB 2s is {:?}", ts),
 | 
			
		||||
                    _ => warn!("Could not get next tick for 2s job"),
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        }))
 | 
			
		||||
        .build()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    let jhb_job_guid = jhb_job.guid();
 | 
			
		||||
    sched.add(jhb_job).await.unwrap();
 | 
			
		||||
 | 
			
		||||
    #[cfg(feature = "english")]
 | 
			
		||||
    let english_job_guid = {
 | 
			
		||||
        let english_job = JobBuilder::new()
 | 
			
		||||
            .with_timezone(Utc)
 | 
			
		||||
            .with_cron_job_type()
 | 
			
		||||
            // .with_schedule("every 10 seconds")
 | 
			
		||||
            .with_schedule("every 10 seconds")
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .with_run_async(Box::new(|uuid, mut l| {
 | 
			
		||||
                Box::pin(async move {
 | 
			
		||||
                    info!("English parsed job every 10 seconds id {:?}", uuid);
 | 
			
		||||
                    let next_tick = l.next_tick_for_job(uuid).await;
 | 
			
		||||
                    match next_tick {
 | 
			
		||||
                        Ok(Some(ts)) => info!("Next time for English parsed job is is {:?}", ts),
 | 
			
		||||
                        _ => warn!("Could not get next tick for English parsed job"),
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
            }))
 | 
			
		||||
            .build()
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        let english_job_guid = english_job.guid();
 | 
			
		||||
        sched.add(english_job).await.unwrap();
 | 
			
		||||
        english_job_guid
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let start = sched.start().await;
 | 
			
		||||
    if let Err(e) = start {
 | 
			
		||||
        error!("Error starting scheduler {}", e);
 | 
			
		||||
        return Err(e);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let ret = vec![
 | 
			
		||||
        five_s_job_guid,
 | 
			
		||||
        four_s_job_guid,
 | 
			
		||||
        jj_guid,
 | 
			
		||||
        jja_guid,
 | 
			
		||||
        utc_job_guid,
 | 
			
		||||
        jhb_job_guid,
 | 
			
		||||
        #[cfg(feature = "english")]
 | 
			
		||||
        english_job_guid,
 | 
			
		||||
    ];
 | 
			
		||||
    Ok(ret)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn stop_example(
 | 
			
		||||
    sched: &mut JobScheduler,
 | 
			
		||||
    jobs: Vec<Uuid>,
 | 
			
		||||
) -> Result<(), JobSchedulerError> {
 | 
			
		||||
    tokio::time::sleep(Duration::from_secs(20)).await;
 | 
			
		||||
 | 
			
		||||
    for i in jobs {
 | 
			
		||||
        sched.remove(&i).await?;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tokio::time::sleep(Duration::from_secs(40)).await;
 | 
			
		||||
 | 
			
		||||
    info!("Goodbye.");
 | 
			
		||||
    sched.shutdown().await?;
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    eprintln!("Should not be run on its own.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod test {
 | 
			
		||||
    use tokio_cron_scheduler::{Job, JobScheduler};
 | 
			
		||||
    use tracing::{info, Level};
 | 
			
		||||
    use tracing_subscriber::FmtSubscriber;
 | 
			
		||||
 | 
			
		||||
    // Needs multi_thread to test, otherwise it hangs on scheduler.add()
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
 | 
			
		||||
    // #[tokio::test]
 | 
			
		||||
    async fn test_schedule() {
 | 
			
		||||
        let subscriber = FmtSubscriber::builder()
 | 
			
		||||
            .with_max_level(Level::TRACE)
 | 
			
		||||
            .finish();
 | 
			
		||||
        tracing::subscriber::set_global_default(subscriber)
 | 
			
		||||
            .expect("Setting default subscriber failed");
 | 
			
		||||
 | 
			
		||||
        info!("Create scheduler");
 | 
			
		||||
        let scheduler = JobScheduler::new().await.unwrap();
 | 
			
		||||
        info!("Add job");
 | 
			
		||||
        scheduler
 | 
			
		||||
            .add(
 | 
			
		||||
                Job::new_async("*/1  * * * * *", |_, _| {
 | 
			
		||||
                    Box::pin(async {
 | 
			
		||||
                        info!("Run every seconds");
 | 
			
		||||
                    })
 | 
			
		||||
                })
 | 
			
		||||
                .unwrap(),
 | 
			
		||||
            )
 | 
			
		||||
            .await
 | 
			
		||||
            .expect("Should be able to add a job");
 | 
			
		||||
 | 
			
		||||
        scheduler.start().await.unwrap();
 | 
			
		||||
 | 
			
		||||
        tokio::time::sleep(core::time::Duration::from_secs(20)).await;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue