Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/seniorjoinu/ic-cron
Task scheduler for the Internet Computer
https://github.com/seniorjoinu/ic-cron
cron cronjob dfinity internet-computer rust scheduler
Last synced: 3 months ago
JSON representation
Task scheduler for the Internet Computer
- Host: GitHub
- URL: https://github.com/seniorjoinu/ic-cron
- Owner: seniorjoinu
- License: mit
- Created: 2021-08-07T13:43:46.000Z (over 3 years ago)
- Default Branch: master
- Last Pushed: 2022-11-11T10:58:00.000Z (almost 2 years ago)
- Last Synced: 2024-06-21T18:10:56.921Z (5 months ago)
- Topics: cron, cronjob, dfinity, internet-computer, rust, scheduler
- Language: Rust
- Homepage:
- Size: 81.1 KB
- Stars: 39
- Watchers: 3
- Forks: 9
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-icp - seniorjoinu/ic-cron
README
## IC Cron
Task scheduler rust library for the Internet Computer
### Motivation
The IC provides built-in "heartbeat" functionality which is basically a special function that gets executed each time
consensus ticks. But this is not enough for a comprehensive task scheduling - you still have to implement scheduling
logic by yourself. This rust library does exactly that - provides you with simple APIs for complex background scheduling
scenarios to execute your code at any specific time, as many times as you want.### Installation
Make sure you're using `dfx 0.8.4` or higher.
```toml
# Cargo.toml[dependencies]
ic-cron = "0.7"
```### Usage
```rust
// somewhere in your canister's code
ic_cron::implement_cron!();#[derive(CandidType, Deserialize)]
enum TaskKind {
SendGoodMorning(String),
DoSomethingElse,
}// enqueue a task
#[ic_cdk_macros::update]
pub fn enqueue_task_1() {
cron_enqueue(
// set a task payload - any CandidType is supported
TaskKind::SendGoodMorning(String::from("sweetie")),
// set a scheduling interval (how often and how many times to execute)
ic_cron::types::SchedulingOptions {
1_000_000_000 * 60 * 5, // after waiting for 5 minutes delay once
1_000_000_000 * 10, // each 10 seconds
iterations: Iterations::Exact(20), // until executed 20 times
},
);
}// enqueue another task
#[ic_cdk_macros::update]
pub fn enqueue_task_2() {
cron_enqueue(
TaskKind::DoSomethingElse,
ic_cron::types::SchedulingOptions {
0, // start immediately
1_000_000_000 * 60 * 5, // each 5 minutes
iterations: Iterations::Infinite, // repeat infinitely
},
);
}// in a canister heartbeat function get all tasks ready for execution at this exact moment and use it
#[ic_cdk_macros::heartbeat]
fn heartbeat() {
// cron_ready_tasks will only return tasks which should be executed right now
for task in cron_ready_tasks() {
let kind = task.get_payload::().expect("Serialization error");
match kind {
TaskKind::SendGoodMorning(name) => {
// will print "Good morning, sweetie!"
println!("Good morning, {}!", name);
},
TaskKind::DoSomethingElse => {
...
},
};
}
}
```### How many cycles does it consume?
Since this library is just a fancy task queue, there is no significant overhead in terms of cycles.
## How does it work?
This library uses built-in canister heartbeat functionality. Each time you enqueue a task it gets added to the task
queue. Tasks could be scheduled in different ways - they can be executed some exact number of times or infinitely. It is
very similar to how you use `setTimeout()` and `setInterval()` in javascript, but more flexible. Each
time `canister_heartbeat` function is called, you have to call `cron_ready_tasks()` function which efficiently iterates
over the task queue and pops tasks which scheduled execution timestamp is <= current timestamp. Rescheduled tasks get
their next execution timestamp relative to their previous planned execution timestamp - this way the scheduler
compensates an error caused by unstable consensus intervals.## Limitations
Since `ic-cron` can't pulse faster than the consensus ticks, it has an error of ~2s.
## Tutorials
* [Introduction To ic-cron Library](https://dev.to/seniorjoinu/introduction-to-ic-cron-library-17g1)
* [Extending Sonic With Limit Orders Using ic-cron Library](https://hackernoon.com/tutorial-extending-sonic-with-limit-orders-using-ic-cron-library)
* [How to Execute Background Tasks on Particular Weekdays with IC-Cron and Chrono](https://hackernoon.com/how-to-execute-background-tasks-on-particular-weekdays-with-ic-cron-and-chrono)
* [How To Build A Token With Recurrent Payments On The Internet Computer Using ic-cron Library](https://dev.to/seniorjoinu/tutorial-how-to-build-a-token-with-recurrent-payments-on-the-internet-computer-using-ic-cron-library-3l2h)## API
See the [example](./example) project for better understanding.
### implement_cron!()
This macro will implement all the functions you will use: `get_cron_state()`, `cron_enqueue()`, `cron_dequeue()`
and `cron_ready_tasks()`.Basically, this macro implements an inheritance pattern. Just like in a regular object-oriented programming language.
Check the [source code](ic-cron-rs/src/macros.rs) for further info.### cron_enqueue()
Schedules a new task. Returns task id, which then can be used in `cron_dequeue()` to de-schedule the task.
Params:
* `payload: CandidType` - the data you want to provide with the task
* `scheduling_interval: SchedulingInterval` - how often your task should be executed and how many times it should be
rescheduledReturns:
* `ic_cdk::export::candid::Result` - `Ok(task id)` if everything is fine, and `Err` if there is a serialization
issue with your `payload`### cron_dequeue()
Deschedules the task, removing it from the queue.
Params:
* `task_id: u64` - an id of the task you want to delete from the queue
Returns:
* `Option` - `Some(task)`, if the operation was a success; `None`, if there was no such task.
### cron_ready_tasks()
Returns a vec of tasks ready to be executed right now.
Returns:
* `Vec` - vec of tasks to handle
### get_cron_state()
Returns a static mutable reference to object which can be used to observe scheduler's state and modify it. Mostly
intended for advanced users who want to extend `ic-cron`. See the [source code](ic-cron-rs/src/task_scheduler.rs) for
further info.### _take_cron_state()
Returns (moved) the cron state. Used to upgrade a canister without state cloning. Make sure you're not using `get_cron_state()`
before `_put_cron_state()` after you call this function.### _put_cron_state()
Sets the global state of the task scheduler, so this new state is accessible from `get_cron_state()` function.
Params:
* `Option` - state object you can get from `get_cron_state()` function
These two functions could be used to persist scheduled tasks between canister upgrades:
```rust
#[ic_cdk_macros::pre_upgrade]
fn pre_upgrade_hook() {
let cron_state = _take_cron_state();stable_save((cron_state,)).expect("Unable to save the state to stable memory");
}#[ic_cdk_macros::post_upgrade]
fn post_upgrade_hook() {
let (cron_state,): (Option,) =
stable_restore().expect("Unable to restore the state from stable memory");_put_cron_state(cron_state);
}
```## Candid
You don't need to modify your `.did` file for this library to work.
## Contribution
You can reach me out here on Github opening an issue, or you could start a thread on Dfinity developer forum.
You're also welcome to suggest new features and open PR's.