4. History
• Previously we used the daemons gem plus
the daemon generator plugin.
• Headaches:
• Each daemon is a separate process.
• Not very DRY:
5. Not DRY
• Each daemon has two files:
• The actual code that runs as a daemon
(e.g. network_point_update_daemon.rb)
•
6. Not DRY
• Each daemon has two files:
• And a daemon control script
(e.g. network_point_update_daemon_ctl)
•
7. Not DRY
• The only bit of code that actually does our work:
AppUser.daily_amount_grant
• The Rails environment is loaded within each
daemon process! Not good for memory.
• No centralized logging/error handling; poignantly
recognized when we wanted exceptions emailed to
us.
• Management of each daemon process required
another entry into monit's config file (eww!):
11. Rooster
What does it do?
Scratches the itches of our previous
configuration
12. Rooster
It’s DRY
• One daemon, monitored by monit
• One Rails environment, loaded once
• Easier management: TCP server (easily
accessed by telnet), rake tasks, more to come...
• Centralized and configurable error handling and
logging:
Rooster::Runner.error_handler = lambda { |e| HoptoadNotifier.notify(e) }
13. How?
• Rooster leverages 3 (optionally 4) excellent open source bits of
software:
14. How?
• Rooster leverages 3 (optionally 4) excellent open source bits of
software:
• EventMachine: provides event-driven I/O using the Reactor
pattern. It lets you write network clients and servers without
handling sockets.
15. How?
• Rooster leverages 3 (optionally 4) excellent open source bits of
software:
• EventMachine: provides event-driven I/O using the Reactor
pattern. It lets you write network clients and servers without
handling sockets.
• Rufus Scheduler: a Ruby gem for scheduling pieces of
code (can leverage EventMachine if available).
16. How?
• Rooster leverages 3 (optionally 4) excellent open source bits of
software:
• EventMachine: provides event-driven I/O using the Reactor
pattern. It lets you write network clients and servers without
handling sockets.
• Rufus Scheduler: a Ruby gem for scheduling pieces of
code (can leverage EventMachine if available).
• daemons: A Ruby gem that provides an easy way to wrap
existing ruby code to be run as a daemon, and to be
controlled by simple start/stop/restart commands. (I know we
moved away from our previous daemons-based solution, but
not all daemons are bad).
17. How?
• Rooster leverages 3 (optionally 4) excellent open source bits of
software:
• EventMachine: provides event-driven I/O using the Reactor
pattern. It lets you write network clients and servers without
handling sockets.
• Rufus Scheduler: a Ruby gem for scheduling pieces of
code (can leverage EventMachine if available).
• daemons: A Ruby gem that provides an easy way to wrap
existing ruby code to be run as a daemon and to be controlled
by simple start/stop/restart commands. (I know we moved
away from our previous daemons-based solution, but not all
daemons are bad).
• Chronic (Optional) - A handy gem for natural language date/
time parsing.
19. How?
• These components fit together thusly:
• The rooster daemon is started, and then kicks off the
Rooster::Runner.
20. How?
• These components fit together thusly:
• The rooster daemon is started, and then kicks off the
Rooster::Runner.
• Rooster::Runner runs the main EventMachine reactor loop,
loads a Rufus::Scheduler, loads (and optionally schedules)
each rooster task, and starts the ControlServer.
21. How?
• These components fit together thusly:
• The rooster daemon is started, and then kicks off the
Rooster::Runner.
• Rooster::Runner runs the main EventMachine reactor loop,
loads a Rufus::Scheduler, loads (and optionally schedules)
each rooster task, and starts the ControlServer.
• Rooster::ControlServer is a TCP-based server that listens for
Rooster control commands (e.g. schedule, unschedule, exit,
etc.).
22. How?
• These components fit together thusly:
• The rooster daemon is started, and then kicks off the
Rooster::Runner.
• Rooster::Runner runs the main EventMachine reactor loop,
loads a Rufus::Scheduler, loads (and optionally schedules)
each rooster task, and starts the ControlServer.
• Rooster::ControlServer is a TCP-based server that listens for
Rooster control commands (e.g. schedule, unschedule, exit,
etc.).
• Rooster::ControlClient issues commands to the
ControlServer; used mainly as a rake helper.
24. Example
• I want a task that kills all kittens at 4:20pm every day
• > script/generate rooster_task KittenKiller
• Generates a new templated task in:
RAILS_ROOT/lib/rooster/tasks/kitten_killer_task.rb
• rake rooster:launch
(and then maybe `rake rooster:start TASK=KittenKillerTask`)
25. Commands
• Tag-based commands are handy for controlling only a subset of
available tasks
• For example, we have separate rooster tasks running on app1
and app3, and are controlled with those server-specific tags.
26. Future Goals
• Make rooster task scheduling blocks DRYer, especially by
abstracting away the ActiveRecord connection pool cleanup.
• Refactor Rooster::Runner (prettier code).
• Add scripts (e.g. script/rooster daemon:start)
• On daemon launch, autostart tasks having a certain tag (or
accept a lambda, e.g.
launch_if => lamba { |task| task.tags.include?(“app1”) } )