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 a ConcurrentBag (among other internal enhancements such as using Task.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");