Coravel v1.6 Release Notes
Release Date: 2018-08-28 // over 5 years ago-
⏱ Scheduler Overhaul
⏱ The scheduler was the first Coravel feature - which was just a prototype built one day while I had some time to kill while my kids were asleep ;) I just wanted to see if it was possible for me to get a really basic version coded.
⏱ As the dotnet community has indicated there is some real interest in this project, the scheduler has naturally been due for a re-write - if it is to be used in actual projects.
⏱ Internal Scheduler Changes (Briefly)
- 👀 Internally - to decide whether a task was "due" - the time of a previously executed task was stored and then compared every 30 seconds against "Now" in order to see if the task should be executed again (yes - a naive implementation. This is why it needed a re-write!)
There are issues with this such as:- Initially, every task has to run at startup to get a value for the "previous executed time."
- Won't work when building a distributed scheduler (maybe a future feature)
- How do we handle intervals like "every month"? Once you restart or re-deploy your app the counter resets! So it's really "every month only if you haven't re-deployed for a month".
✅ Unit Tests
✅ These changes have also increased the number of unit tests in Coravel from over 100 to almost 600! Something that should help the community feel like Coravel is a reliable library to use.
Cron
⏱ Now the scheduler works like cron. Every minute the scheduled task checks the time and uses a cron expression to determine if it should run.
⏱ A side benefit is that we now have a
Cron
method available for scheduling tasks.I battled over whether or not to implement this myself. In the end, I decided that it was better not to add another dependency (especially since I couldn't find a library that was considered really trustworthy in this area).
I don't want to re-invent the wheel - I'm all for using third-party / NuGet packages others have made. But I want to ensure that whatever I do use has already been considered a trustworthy and reliable package (for example, I decided to include MailKit for the Mailer feature. MailKit is considered by everyone to be the .Net Core SMTP mailing go-to)
Threading Enhancements
⏱ The collection of scheduled tasks were simply held in a
List
. The scheduler allows scheduling tasks dynamically - while this isn't necessarily recommended (since these scheduled tasks will disappear after the application is closed) - it is possible. There may be some use-cases for this.⏱ Anyways, using a
List
in multi-threaded use cases is bad. So now the scheduler is using aConcurrentBag
(among other internal enhancements such as usingTask.WhenAll
, etc.).⏱ I'm still considering other parallel processing enhancements (to fire scheduled tasks in true parallel), but for now, the changes are needed and moving the right direction.
⏱ Schedule Configuration
⏱ Previously, all the scheduler config was done in
ConfigureServices
. This has changed to allow additional features and an implementation that uses .Net Core's DI container more properly.🔧 In
ConfigureServices
:services.AddScheduler()
🔧 Then in
Configure
- all the existing configuration methods can be used:app.UseScheduler(scheduler =\>{ scheduler.Schedule( () =\> Console.WriteLine("Every minute during the week.") ) .EveryMinute(); .Weekday(); });
🔧 Queue Configuration Changes
Just like the point above, the queue had the same changes made:
🔧 In
ConfigureServices
:services.AddQueue();
🔧 In
Configure
:app .ConfigureQueue() .OnError(e =\> { //.... handle the error });
Other New Features
Invocables
Coravel now exposes a concept of "Invocables". Basically, it's a class that can be "Invoked" (i.e. executed) asynchronously.
⏱ This allows us to create specific classes to do a certain "Job" in our system. These "jobs" can then be either scheduled or queued using a super simple syntax.
this.\_queue.QueueInvocable\<GrabDataFromApiAndPutInDBInvocable\>();// orscheduler .Schedule\<GrabDataFromApiAndPutInDBInvocable\>() .EveryTenMinutes();
Cli: Generate Invocable
🆕 New command:
coravel invocable new [yourInvocable]
will generate a clean invocable all ready to go.Prevent Overlap
Sometimes you may have long running tasks (longer than 1 minute?). The normal behaviour of the scheduler is to simply fire off a task if it is due. What if you don't want a "due" task to execute if the previously executed one is still running?
scheduler .Schedule\<SomeInvocable\>() .EveryMinute() .PreventOverlapping("SomeInvocable");
- 👀 Internally - to decide whether a task was "due" - the time of a previously executed task was stored and then compared every 30 seconds against "Now" in order to see if the task should be executed again (yes - a naive implementation. This is why it needed a re-write!)