Git Product home page Git Product logo

ic-cron's Introduction

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.

# Cargo.toml

[dependencies]
ic-cron = "0.7"

Usage

// 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::<TaskKind>().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

API

See the 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 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 rescheduled

Returns:

  • ic_cdk::export::candid::Result<u64> - 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<ScheduledTask> - 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<ScheduledTask> - 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 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<TaskScheduler> - state object you can get from get_cron_state() function

These two functions could be used to persist scheduled tasks between canister upgrades:

#[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<TaskScheduler>,) =
          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.

ic-cron's People

Contributors

seniorjoinu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ic-cron's Issues

Unable to run yarn test

On ubuntu 20.04, dfx : 0.8.4

yarn test
yarn run v1.22.15
$ ts-mocha --paths --timeout 1000000 src/**/*.spec.ts

Error: Cannot find module 'dfx-idl/ic-cron-example/ic-cron-example'


yarn start seems to work in the prior step

yarn start
yarn run v1.22.15
$ dfx start --clean
Starting webserver for /_/
binding to: 127.0.0.1:39575
....
Dec 06 20:21:06.604 WARN s:gj7o2-tv6sz-egnnq-zjllt-paj4g-zlonj-jr7u5-bt632-5ymwn-g46ly-sae/n:zxe2u-fcrwj-jrji2-laj6m-xskov-ti2t4-nxped-teak3-qj4wg-jftrt-6qe/ic_state_layout/utils StateManager runs on a filesystem not supporting reflinks (attempted to reflink /home/xyz/src/ic/ic-cron/example/e2e-test/.dfx/state/replicated_state/node-100/state/tip/system_metadata.pbuf => /home/xyz/src/ic/ic-cron/example/e2e-test/.dfx/state/replicated_state/node-100/state/fs_tmp/scratchpad_0000000000000064/system_metadata.pbuf), running big canisters can be very slow

IC-Cron Service

Hey @seniorjoinu,

have you considered deploying a service based on this to the IC where canisters can register themselves (against a few) to be notified?

Would be cool to have economically self-sufficient service like this on the IC :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.