magic.lambda.scheduler 9.9.9

There is a newer version of this package available.
See the version list below for details.
dotnet add package magic.lambda.scheduler --version 9.9.9                
NuGet\Install-Package magic.lambda.scheduler -Version 9.9.9                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="magic.lambda.scheduler" Version="9.9.9" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add magic.lambda.scheduler --version 9.9.9                
#r "nuget: magic.lambda.scheduler, 9.9.9"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install magic.lambda.scheduler as a Cake Addin
#addin nuget:?package=magic.lambda.scheduler&version=9.9.9

// Install magic.lambda.scheduler as a Cake Tool
#tool nuget:?package=magic.lambda.scheduler&version=9.9.9                

Magic Lambda Scheduler

TODO: Needs updating, but also needs better implementation, making it easier to create unit tests, etc.

This project provides the ability to create persisted, and/or scheduled Hyperlambda tasks, for Magic. More specifically it provides the following slots.

  • [tasks.create] - Creates a new task.
  • [tasks.get] - Returns an existing task.
  • [tasks.list] - Lists all tasks.
  • [tasks.execute] - Executes an existing task.
  • [tasks.delete] - Deletes a task.
  • [scheduler.stop] - Stops the scheduler, implying no tasks will be executed at their scheduled time.
  • [scheduler.start] - Starts the scheduler, if there are any upcoming scheduled tasks.
  • [scheduler.next] - Returns the date and time of the next scheduled task, if any.
  • [scheduler.running] - Returns true if the scheduler is running.

Creating a task

To create a task without an execution date and no repetition pattern, you can use something such as the following.

tasks.create:foo-bar-task-1
   .lambda

      /*
       * Your task's lambda object goes here
       */
      log.info:Executing foo-bar-task-1

The name or ID of your task in the above example becomes "foo-bar-task-1", and the task can be referenced later using this name. The name must be unique, otherwise any previously created tasks with the same name will be silently overwritten. A task can also optionally have a [description] argument, which is a humanly friendly written description, describing what your task does. Below is an example.

tasks.create:foo-bar-task-2
   description:This task will do a little bit of foo and some bar afterwards.
   .lambda
      log.info:Executing foo-bar-task-2

Notice - Your task's [id] argument, can only contain alpha numeric characters, a-z, 0-9 - In addition to the special characters ., - and _.

Executing a task

You can explicitly execute a persisted task at will by invoking [tasks.execute], and passing in the ID of your task. Below is an example, that assumes you have created the above "foo-bar-task-1" task first.

tasks.execute:foo-bar-task-1

Notice - This slot does not synchronize access to your tasks, such as when executing a scheduled task at its scheduled date does. This allows you to have multiple tasks executed simultaneously, contrary to a scheduled task that will never be executed simultaneously as another scheduled task is executing.

Workflows and Magic Tasks

The above allows you to persist a "function invocation" for later to execute it, once some specified condition occurs - Effectively giving you the most important features from Microsoft Workflow Foundation, without the ridiculous XML and WYSIWYG parts from MWF - In addition to that this also is a .Net Core library, contrary to MWF.

This allows you to create and persist a function invocation, for then to later execute it, as some condition occurs, arguably giving you "workflow capabilities" in your projects.

Notice - By creating your own ISlot implementation, you can easily create your own C# classes that are Magic Signals, allowing you to persist an invocation to your method/class - For then to later execute this method as some condition occurs. Refer to the documentation for Magic Lambda to see how this is done, and more specifically the "Extending Hyperlambda" section.

Scheduled tasks

If you want to create a scheduled task, you can choose to have the task execute once in the future, at a specified date and time, by applying a [due] argument.

tasks.create:foo-bar-task-3
   due:date:"2020-12-24T17:00"
   .lambda
      log.info:Executing foo-bar-task-3

The above [due] argument is a UTC date and time in the future for when you want your task to be scheduled for execution. After the task has been executed, it will never execute again, unless you manually execute it, or assign a [repeats] pattern to it by invoking the slot that schedules existing tasks.

Notice - You cannot create a task with a due date being in the past, and all dates are assumed to be in the UTC timezone.

Repeating tasks

There are 3 basic [repeats] patterns for the Magic Lambda Scheduler, in addition to that you can extend it with your own parametrized repeating IPattern implementation. The built in repetition patterns, are as follows.

  • x.units - Units can be one of "seconds", "minutes", "hours", "days", "weeks" or "months" - And x can be any integer value.
  • MM.dd.HH.mm.ss - Where the entities are in sequence months, days in months, hour, minute and second.
  • ww.HH.mm.ss - Where the entities are weekdays, hour, minute and second.

Notice, MM, dd, and ww can have double asterix (**) as their values, implying "whatever value". MM, dd and ww can also have multiple values, separated by the pipe character (|), to provide multiple values for these types. See examples of this further below in this documentation.

Intervals

Evaluating your task every second/minute/hour/etc can be done by using something such as the following.

tasks.create:task-id
   repeats:50.seconds
   .lambda
      log.info:Executing repeating task

The above will evaluate your task every 50 second. The above "seconds" can be exchanged with "minutes", "hours", "days", "weeks" or "months". Notice, this allows you to have very large values, to have tasks that are repeating very rarely, such as the following illustrates.

tasks.create:task-id
   repeats:3650.days
   .lambda
      log.info:Executing seldomly repeating task once every 10 year

The above task will only be evaluated every 3650 days, which becomes once every 10 years. Below is a list of all valid units types.

  • seconds
  • minutes
  • hours
  • days
  • weeks
  • months

Periodically scheduled tasks

To create a task that is executed on the first day of every month, at 5PM, you can use the following repetition pattern.

tasks.create:task-id
   repeats:**.01.05.00.00
   .lambda
      log.info:It is the 1st of the month, any month, and the time is 5AM at night.

Hours must be supplied as "military hours", implying from 00:00 to 23:59, where for instance 22 equals 10PM UTC time. Also notice how we provided a double asterix (**) for the month parts, implying "any month". We could also have provided multiple days, and/or months, such as the following illustrates. The Hyperlambda below will create a task that is executed in January and February, but only on the 5th and 15th of these months.

tasks.create:task-id
   repeats:01|02.5|15.05.00.00
   .lambda
      log.info:It is the 5th or the 15th of January or February, and the time is 5AM at night.

By using the double asterix for month and day of month, you can create a task that is executed every day, at some specific time of the day (UTC time). Below is an example.

tasks.create:task-id
   repeats:**.**.22.00.00
   .lambda
      log.info:It is the 10PM now.

Weekdays pattern

If you use the weekdays pattern, you can create any combinations of weekdays, allowing you to supply multiple weekdays in a single repetition pattern. Below is an exhaustive list of all possible weekdays.

  • Monday
  • Tuesday
  • Wednesday
  • Thursday
  • Friday
  • Saturday
  • Sunday

To evaluate a task every Saturday and Sunday for instance, you can use saturday|sunday as your weekday. Below is an example. Notice, weekdays are case insensitive.

tasks.create:task-id
   repeats:saturday|SUNDAY.22.00.00
   .lambda
      log.info:It is Saturday or Sunday, and the time is 22PM.

You can also provide a double asterix (**) for the weekdays pattern, implying "all days of the week".

Creating your own repetition patter

In addition to the above 3 types of repetition patterns, you can also create your own repetition pattern type, by implementing the IPattern interface on one of your own types, and registering your type create function by using the PatternFactory.AddExtensionPattern method. If you do, you'll have to reference your repetition pattern type using "ext:", combined with its resolver key. Implying if you register your IPattern type such that it resolves using for instance "my-pattern" as its key, you'll have to use _"ext:my-pattern:args" to reference it later, as you wish to create an instance of your custom pattern type. The "args" part are any arguments supplied to your pattern during creation. Below is an example that creates a custom repetition pattern.

private class ExtPattern : IPattern
{
    readonly string _args;
    public string Value => "ext:custom-pattern:" + _args;

    public ExtPattern(string args)
    {
        _args = args;
    }

    public DateTime Next()
    {
        return new DateTime(2030, 11, 11, 11, 11, 57);
    }
}

Of course, the above IPattern will statically resolve to the 11th of November 2030, at 23:11:57. But the idea is that the args supplied during creation, can be used to parametrize your pattern, and calculate the next due date for your schedule.

After you have declared your custom IPattern type, you'll need to inform the PatternFactory class that you want to use the above class, and resolve it using some specific key from your schedules. This is accomplished using something resembling the following code.

PatternFactory.AddExtensionPattern(
    "custom-type",
    str =>
    {
        return new ExtPattern(str);
    });

The above code will ensure that every time you use "custom-type" as a repetition pattern type, the create function above will be invoked, allowing you to create and decorate an instance of your custom IPattern type. The str argument to your above create function, will be everything after the ext:custom-type: parts, when creating an instance of your pattern.

To use the above pattern in your own code, you can use something such as the following.

tasks.create:custom-repetition-pattern
   repeats:"ext:custom-pattern:some-arguments-here"
   .lambda
      log.info:Executing custom-repetition-pattern

In the above [repeat] argument, the ext parts informs the scheduler that you want to use a custom repetition pattern, the custom-pattern parts resolves to your IPattern create function, and the "some-arguments-here" parts will be passed into your above ExtPattern constructor.

Internals

A background thread will be used for executing scheduled tasks, and only one background thread - Which implies that no tasks will ever be executing in parallel, to avoid thread starvation, due to logical errors in your schedules. All tasks are executed asynchronously, implying the execution thread will be released back to the operating system, as the thread is waiting for IO data, from socket connections, etc - Assuming you use the async slots where relevant.

When a repeating task has finished executed, the next due date for the task's execution will be calculated using its interval pattern - Implying that if you use a 5 second pattern, the schedule for its next execution, will be calculated 5 seconds from when the task finished executing, which might not necessarily imply that your tasks are executed exactly every 5 seconds, depending upon how much time your task requires to execute. The interval pattern declares how many units to count to before executing the task again, from when the task finished executing.

Deleting a task

Use the [tasks.delete] signal to delete a task. This will also delete all future schedules for your task. An example can be found below.

tasks.delete:task-id

Besides from the task ID, the delete task signal doesn't take any arguments.

Inspecting a task

To inspect a task you can use the following.

tasks.get:task-id

Besides from the task ID, the get task slot doesn't take any arguments. Using this signal, will return the task's due date(s) in addition to the task itself.

Listing tasks

To list tasks, you can use the [tasks.list] signal. This slot optionally handles an [offset] and a [limit] argument, allowing you to page, which might be useful if you have a lot of tasks in your system. If no [limit] is specified, this signal will only return the first 10 tasks, including the task's Hyperlambda, but not its repetition pattern, or due date. Below is an example.

tasks.list
   offset:20
   limit:10

Miscelaneous slots

The [scheduler.stop] will stop the scheduler, meaning no repeating tasks or tasks with a due date in the future will execute. Notice, if you create a new task with a due date, and/or a repetition pattern, the scheduler will automatically start again. When you start the scheduler again, using for instance [scheduler.start], all tasks will automatically resume, and tasks that have due dates in the past, will immediately start executing.

To determine if the task scheduler is running or not, you can invoke [scheduler.running], which will return true if the scheduler is running. Notice, if you have no scheduled tasks, it will always return false. And regardless of whether or not the scheduler is running or not, you can always explicitly execute a task by using [tasks.execute].

To return the date and time for the next scheduled task, you can raise the [scheduler.next] signal.

Persisting tasks

All tasks are persisted into your selected database type of choice, either MySQL or Microsoft SQL Server. Which implies that even if the server is stopped, all scheduled tasks and normal tasks will automatically load up again, and be available to the scheduler as the server is restarted. This might imply that all tasks in the past are immediately executed, which is important for you to understand.

Tasks are by default persisted into your magic.tasks table, and schedules are persisted into your magic.task_due table.

Project website

The source code for this repository can be found at github.com/polterguy/magic.lambda.scheduler, and you can provide feedback, provide bug reports, etc at the same place.

Quality gates

  • Build status
  • Quality Gate Status
  • Bugs
  • Code Smells
  • Coverage
  • Duplicated Lines (%)
  • Lines of Code
  • Maintainability Rating
  • Reliability Rating
  • Security Rating
  • Technical Debt
  • Vulnerabilities

License

This project is the copyright(c) 2020-2021 of Thomas Hansen thomas@servergardens.com, and is licensed under the terms of the LGPL version 3, as published by the Free Software Foundation. See the enclosed LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on magic.lambda.scheduler:

Package Downloads
magic.library

Helper project for Magic to wire up everything easily by simply adding one package, and invoking two simple methods. When using Magic, this is (probably) the only package you should actually add, since this package pulls in everything else you'll need automatically, and wires up everything sanely by default. To use package go to https://polterguy.github.io

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
17.2.0 440 1/22/2024
17.1.7 196 1/12/2024
17.1.6 158 1/11/2024
17.1.5 180 1/5/2024
17.0.1 220 1/1/2024
17.0.0 358 12/14/2023
16.11.5 340 11/12/2023
16.9.0 316 10/9/2023
16.7.0 580 7/11/2023
16.4.1 372 7/2/2023
16.4.0 401 6/22/2023
16.3.1 367 6/7/2023
16.3.0 342 5/28/2023
16.1.9 622 4/30/2023
16.1.1 351 4/20/2023
15.10.11 393 4/13/2023
15.9.1 612 3/27/2023
15.9.0 458 3/24/2023
15.8.2 490 3/20/2023
15.7.0 398 3/6/2023
15.5.0 1,595 1/28/2023
15.2.0 689 1/18/2023
15.1.0 1,143 12/28/2022
14.5.7 710 12/13/2022
14.5.5 821 12/6/2022
14.5.1 675 11/23/2022
14.5.0 614 11/18/2022
14.4.5 701 10/22/2022
14.4.1 782 10/22/2022
14.4.0 671 10/17/2022
14.3.1 1,261 9/12/2022
14.3.0 661 9/10/2022
14.1.3 942 8/7/2022
14.1.2 678 8/7/2022
14.1.1 641 8/7/2022
14.0.14 708 7/26/2022
14.0.12 685 7/24/2022
14.0.11 666 7/23/2022
14.0.10 651 7/23/2022
14.0.9 662 7/23/2022
14.0.8 721 7/17/2022
14.0.5 780 7/11/2022
14.0.4 797 7/6/2022
14.0.3 726 7/2/2022
14.0.2 671 7/2/2022
14.0.0 673 6/25/2022
13.4.19 1,538 6/8/2022
13.4.0 1,212 5/31/2022
13.3.4 1,449 5/9/2022
13.3.1 752 5/1/2022
13.3.0 689 5/1/2022
13.2.0 1,161 4/21/2022
13.1.0 1,023 4/7/2022
13.0.0 748 4/5/2022
11.0.5 1,423 3/2/2022
11.0.4 750 2/22/2022
11.0.3 784 2/9/2022
11.0.2 808 2/6/2022
11.0.1 769 2/5/2022
10.0.21 759 1/28/2022
10.0.20 774 1/27/2022
10.0.19 758 1/23/2022
10.0.18 754 1/17/2022
10.0.16 751 1/2/2022
10.0.15 500 12/31/2021
10.0.14 546 12/28/2021
10.0.7 1,434 12/22/2021
10.0.5 741 12/18/2021
9.9.9 1,654 11/29/2021
9.9.3 909 11/9/2021
9.9.2 611 11/4/2021
9.9.0 705 10/30/2021
9.8.9 702 10/29/2021
9.8.7 666 10/27/2021
9.8.6 632 10/27/2021
9.8.5 684 10/26/2021
9.8.0 1,353 10/20/2021
9.7.9 626 10/19/2021
9.7.5 1,479 10/14/2021
9.7.0 854 10/9/2021
9.6.6 1,224 8/14/2021
9.2.0 6,277 5/26/2021
9.1.4 1,265 4/21/2021
9.1.0 1,052 4/14/2021
9.0.0 841 4/5/2021
8.9.9 1,024 3/30/2021
8.9.3 1,530 3/19/2021
8.9.2 1,010 1/29/2021
8.9.1 1,026 1/24/2021
8.9.0 1,076 1/22/2021
8.6.9 2,973 11/8/2020
8.6.6 1,997 11/2/2020
8.6.0 4,029 10/28/2020
8.5.0 1,915 10/23/2020
8.4.0 5,614 10/13/2020
8.3.2 1,969 10/8/2020
8.3.1 1,254 10/5/2020
8.3.0 1,213 10/3/2020
8.2.2 2,033 9/26/2020
8.2.1 1,360 9/25/2020
8.2.0 1,383 9/25/2020
8.1.17 6,693 9/13/2020
8.1.16 605 9/13/2020
8.1.15 1,916 9/12/2020
8.1.11 2,510 9/11/2020
8.1.10 1,308 9/6/2020
8.1.9 1,323 9/3/2020
8.1.8 1,267 9/2/2020
8.1.7 1,192 8/28/2020
8.1.4 1,227 8/25/2020
8.1.3 1,289 8/18/2020
8.1.2 1,192 8/16/2020
8.1.1 1,278 8/15/2020
8.0.2 495 8/11/2020
8.0.1 2,700 8/7/2020
8.0.0 1,225 8/7/2020
7.0.1 1,390 6/28/2020
7.0.0 1,234 6/28/2020
5.0.0 7,472 2/25/2020
4.0.4 7,960 1/27/2020
4.0.3 1,287 1/27/2020
4.0.2 1,397 1/16/2020
4.0.1 1,378 1/11/2020
4.0.0 1,343 1/5/2020
3.1.3 4,209 12/4/2019
3.1.2 1,405 11/30/2019
3.1.1 1,486 11/26/2019
3.1.0 605 11/19/2019