Scheduling of Ruby Agents using Cron

by viz on February 7th, 2008

One of the main principles of agent-oriented modeling is agent’s proactivness - living and acting continuously on its own. Technically, this means that the piece of code is running in a kind of loop receiving messages, acting upon them and sending out other messages. This is the heartbeat of each agent.

The most naive approach is to run a piece of code as a separate process (or thread). The code spins a loop where it receives messages, dispatches them and sleeps for some time. You could see such a pattern in some of the previous posts ( Pattern: Agent Life Cycle).

Another approach is to use scheduling. An agents schedules its next activities to certain times. This approach is usually advantageous for long- and sparsely-running agents. A good example can be monitoring agent, a news-sending agent, or even plain-old UNIX maintenance scripts, etc..
Here, again, we can choose from two methods of scheduling:

  1. The agent determines only the very next time of its execution.
  2. The agents uses a full static schedule - execution plan.

The former method is very dynamic, flexible allowing the agent adopt its behavior immediately. However, it can be vulnerable to possible errors. An error can cause the agent won’t register anymore. In fact it becomes dead. The later method is predictable and suitable for static environments.

In the rest of this post I demonstrate how easy it can be to operate a cron driven “agent” directly from your Ruby code.

Cron Scheduling

You can decide to use Ruby native scheduler (like rufus-scheduler), or rely on an external service (like Unix Cron, or other dedicated middleware). This time I show you how to engage with Unix Cron scheduling (next time I tell you more about other schedulers.) Well, for simple cases you can edit cron manually, i.e. install your schedule using crontab. But for more complex and dynamic cases this is a pain in the ass. Imagine you have dozens (or hundreds) of autonomous agents running on your system, or you have very frequent schedule modifications. The CronEdit library help us out here.
Install it as a gem:

sudo gem install cronedit

or get it from its project home.

CronEdit library associates each cron entry with an id. This makes it uniquely manipulable and not interfering with other entries. It is because the rest of the actual crontab is left unchanged. So you can manipulate the crontab as comfortable as it was a Hash. Your cron entry definition itself can have a standard cron text format, or alternatively you can use Hash notation. Let’s look at few simple examples.

Simple operations using class methods

require 'cronedit'
include CronEdit
Crontab.Add  :agent1, '5,35 0-23/2 * * * echo 123'
Crontab.Add  :agent2, {:minute=>5, :command=>'ruby /agents/agent.rb 42'}
Crontab.Remove 'agent1', 'agent2'

The first line registers a cron entry in standard text notation under id ‘agent1 ‘. The second line registers entry ‘agent2′ using Hash notation. The last line deletes both entries. The changes are effective immediately. You can list all actual entries by:

p Crontab.List

Batch/transactional updates
In case you need to do more changes at once use the following approach

cm = CronEdit::Crontab.new
cm.add 'agent1', '5,35 0-23/2 * * * echo agent1'...
cm.add 'agent2', {:minute=>5, :command=>'echo 42'}
cm.remove 'agentKiller'
cm.commit

Inversely, you can also rollback the operations.

Bulk update/removal using file definitions
In more complex scenarios (communities of agenst) you often need to manipulate more agents at once with their definitions in files. Imagine you have a crowd of news-gathering agents and mail-analysis agents. The cron definitions for these groups are stored as files (previously generated by CronEdit)

fc = FileCrontab.new '/agents/news-gathering.cron'
Crontab.Merge fc...
Crontab.Subtract fc

The first line uses FileCrontab to read the cron definition of all news-gathering agents. The file crontab is merged in to the actual system crontab on the next line. Similarly the definitions can be ‘unloaded’ using ’subtract’ method.

The library allows you do even more (e.g. when you definitions are in DB; combining multiple definitions)
See more examples and documentation.

In the next posts I present another simple piece of the infrastructure helpful for this type of agents.

2 Comments
  1. naniter permalink

    have you ever seen nanite?

  2. of course (a few months later)

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS