Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🛠️ [TASK] : Cron Event Service #146

Closed
saibatizoku opened this issue Feb 22, 2024 · 0 comments · Fixed by #145
Closed

🛠️ [TASK] : Cron Event Service #146

saibatizoku opened this issue Feb 22, 2024 · 0 comments · Fixed by #145
Assignees
Labels
enhancement New feature or request

Comments

@saibatizoku
Copy link
Contributor

Summary

Continuation of Cron Rust Module Implementation

Description

Continuation of Cron Rust Module Implementation

SJ Notes

The issue with namespace is that we will have multiple apps, all of them potentially having their own crons.
A cron setup by one app, should not get sent as an event to another app, it should only target the specific app its destined for.

That should more look like :

pub(crate) struct State {
    /// The crontabs hash map.
    crontabs: DashMap<AppName,
                DashMap<CronEventTag, CronTab>>,
    cronqueue: SomeKindOfConcurrentList<(AppName, CronEventTage)> // Entries are in order from most recent to expire, to oldest to expire.
}

I am also not seeing a generator for the cron events themselves?

Your internal state would need to know the app name, in order to generate a cron event.
One way to do a cron task is to have an ordered list of cron events rather than a hashmap, although that could be used as an index.
The top entry in the list is the next cron to fire.
Your task sleeps on either a timeout or a trigger.
The timeout is the delay until the top item in the queue needs to be sent as an event.
When a cron is scheduled, its added to the queue at the appropriate sorted position. Insert sort works well here, just scan through the list until you find the position it fits between and add it.
If it was added to the head, you trigger the cron task, which will then check if it needs to immediately send it, or reschedule itself to sleep.
If its not at the head, its just added to the queue, because the task state hasn't changed.
When the task wakes up (on either timeout or trigger) it checks the head of the queue, if its ready to send, it sends it, and reschedules it back in the queue.
It then sleeps again, for the duration of the next events delay.
This is a very efficient implementation, your not polling the queue, you sleep. The only time you check the queue is if the head changes, or you have an event to send.
If the queue is empty, you sleep forever.
You only have one entry in the queue for every event, even if they recur, because they are reinserted once they reach the head at the next time they would expire.
One thing to be aware of with this scheme, and to check for is when you have multiple crons that end at the same time, the event handler should reliably send them all, for example, if there are 3 crons that fire once a second, every second all three should fire, and be rescheduled. If a cron is in the past, you need to send it, and reschedule it, because you may be delayed in processing under high CPU load (unlikely but it needs to be accounted for).

You need tests to check the event queue task is being run properly.
You also need tests that add/delete/list the cron tabs from multiple threads, to ensure its all threadsafe.
You only need one cron task, because each cron entry should know what app its being generated for. It needs to be information added to the crontab value inside the state, but never told to the applications themselves (its internal).

The cron task should be started when the state is initialized at start of the world.

The queue itself has multiple writers and readers, its more like a list where you are mostly interested in the head than a traditional queue, thats because you need to keep entries in it sorted as they are added, and a entry could be added/removed by any add/delete of a crontab or when one expires and is either removed or re-added by thge cron task.

There are a few libraries which do this kind of thing, but i am not in love with any of them, mostly because they are tickers, which is wildly inefficient.
If I know the next event is in 5 hours, it makes no sense to tick every 500ms to check if that 5 hours has elapsed.
But they could serve as inspiration:
https://github.com/mehcode/schedule-rs/tree/master
https://github.com/mvniekerk/tokio-cron-scheduler/tree/main
https://github.com/lholden/job_scheduler
But this is probably the cleanest/closest to what we want:
https://github.com/rs-god/rcron
I don't think any of these are exactly what we need, but the last one could form the basis of what we need adapted heavily to our use case.
We do not need a general cron API, just one that works how we want it to.
If you use this one, or any of the others as inspiration, just add a comment to the top of the file saying you used it for inspiration, just to keep the licensing/references clean.

@saibatizoku saibatizoku added this to the M2: Hermes Foundations milestone Feb 22, 2024
@saibatizoku saibatizoku self-assigned this Feb 22, 2024
@saibatizoku saibatizoku added the enhancement New feature or request label Feb 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
Status: ✅ Done
Development

Successfully merging a pull request may close this issue.

1 participant