Run the timing task efficiently in erlang?

vincent
vincent
Oct 2 · 3 min read

In most projects, It’s inevitable that you’ll write a few scheduled tasks, such as mail that periodically sends node status on weekdays. How to complete the timing task quickly and efficiently? Here are three common ways to implement timing tasks and their advantages and disadvantages.

  1. Use erlang:send_after/4 to trigger the next timer:

I’ve used this approach a lot in my past projects.

Advantages:Easy to understand and maintain,Erlang style.

Disadvantages:Each task starts a new process with its own timer. These processes spend most of their time waiting and doing nothing, which seems like a waste of resources when there are a lot of scheduled tasks.

2. receive after primitive method :

We can use gen_server callback function’s timeout(The internal implementation is receive after).

Advantages: Easy to understand, don’t need to send message likesend_after.

Disadvantages:As with the send_after, these task processes spend most of their time dealing with waiting, causing unnecessary waste, and must be careful to recalculate a timeout after handling each message。

3. Use timer:send_interval/3to set the event interval:

Another similar approach is timer:apply_after/4 timer:apply_interval/4.

The disadvantage is that the time interval cannot be modified, it can only be fixed.


Next, Let’s see some a few specific examples.

Timer in stdlib

timer is a stand-alone gen_server process, Using the timeout method,But it is a process that manages all timers (not one timer per process) using ordered set’s ets to manage all-timers.

  1. Examine the task entry at the head of the ets, compute how far in the future it must run.
  2. Sleep for that period of time.
  3. On awakening and after verifying the correct time, execute the task at the head of the ets (in the background) with spawn.
  4. Determine the next time in the future to run this command and place it back on the ets at that time value.

erlcron

erlcron,Each job is assigned to a single process. The time until it is to run next is calculated, and the process sleeps for exactly that long. All task processes are waiting for most of the time.

leader_cron’s core is the same, but with standard crontab syntax and cluster support. The same is true for ecron v0.1.0.

ecron

Its core is exactly the same as that of the standard library timer. A single process uses an ordered_set ets to manage all-timers.

  1. On application start-up, start a standalone gen_server ecron under supervision tree(ecron_sup).
  2. Look for configuration {jobs, Jobs} when ecron process initialization.
  3. For each crontab job found, determine the next time in the future that each command must run.
  4. Place those commands on the ordered_set ets with their {Corresponding_time, Name} to run as key.
  5. Enter the main loop:
  • Examine the task entry at the head of the ets, compute how far in the future it must run.
  • Sleep for that period of time by gen_server timeout feature.
  • On awakening and after verifying the correct time, execute the task at the head of the ets (spawn in the background).
  • Delete old key in ets.
  • Determine the next time in the future to run this command and place it back on the ets at that time value.
  1. No permanent process is required to execute the task, only spawn temporary execution is required.

2. Efficient time decision mechanism without complex time conversion.

3. Both cron-like scheduling and interval-based scheduling.

4. property-based testing with PropEr. 100% code coverage.

5. ecron’s code is less than 600 lines without dependence.

quantum

quantum is one of the most widely used and feature-rich libraries in elixir, relying on gen stage’s producer-consumer model.

The core is that all tasks as consumers subscribe to the Quantum.ClockBroadcaster producer, which broadcasts a tick event to the task process per second, the task processes determine if it is time to execute when receiving the tick. In other words, each task receives a message every second and determines if it is available for the job at that moment。

This makes a lot of messages flying around, such as: If you define a task to perform at the end of each month, it will receive messages every second to see if it’s time to run. This is too heavy for a system with only a few tasks.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade