loop_hard

Build Status Coverage Status Code Climate Inline docs Gem Version

Add timeouts to your long-running loops, and response to signals gracefully.

LoopHard allows you to have long-running worker loops that will stop after a while, and also stop if they get an external signal to stop (for example, if Sidekiq stops due to a USR1, or a TERM signal is trapped).

Possible use cases:

  • You have a background job that runs every 10 minutes and loops through a bunch of records until there are none left. Use LoopHard.loop(timeout: 9.5.minutes) to generally prevent overlapping.
  • You don't have a time limit, but you're running a long job inside Sidekiq, and you want to exit gracefully when Sidekiq decides it's terminating (and before Heroku kills the process!). You shouldn't have long-running jobs on Sidekiq, but hey! it happens!
  • Same case as before, but not inside Sidekiq. You're either handling signals yourself, or have some other library (in which case, please do a PR!)

The assumption is, obviously, that your loop will run for a long time, but each iteration of your loop is going to be quick, otherwise, we can't really exit gracefully. But if we do exit gracefully, you know you're exiting in a known state.

Download

Gem:

gem install loop_hard

Installation

Load the gem in your GemFile.

gem "loop_hard"

Loop Hard!

Replace your:

loop do
  # do stuff until we break
end

with

LoopHard.loop(timeout: 10.minutes) do
  # do stuff until we break, or until 10 minutes go by, or until something tells us to stop
end

or, don't really specify a timeout if you're going to run forever, but just want to trap signals / Sidekiq shutdown gracefully.

Set your Logger

LoopHard logs to LoopHard.logger every time it exits a loop, since normally these aren't normal conditions and you might want to be notified about it. By default, it logs to stdout.

You probably want to set it to LoopHard.logger = Rails.logger, or whatever logger you're using in your app.

Trap your own signals

If you want LoopHard to trap signals itself, you'll need to call LoopHard::SignalTrap.trap_signals

By default it'll trap INT, TERM and USR1, but you can pass the signals you'd like trapped as a parameter to trap_signals.

Only do this if you are sure nothing else in your app is trapping signals, because there can only be one signal handler per process. For example, if you are using Siekiq, do not do this, or you will get in the way of the Sidekiq shutdown process.

If you are already trapping signals yourself, you can call LoopHard::SignalTrap.signal_trapped when you trap a signal that should stop your loops.

Version Compatibility and Continuous Integration

Tested with Travis using Ruby 2.1.1, 2.1.5 and 2.2.2.

LoopHard does work with Ruby 1.9, however, Sidekiq doesn't, so the Sidekiq trap won't work, but the rest will.

To locally run tests do:

rake test

Copyright (c) 2015, Daniel Magliola

See LICENSE for details.

Users

This gem is being used by:

  • MSTY
  • You? please, let us know if you are using this gem.

Changelog

Version 0.1.0 (Oct 20th, 2015)

  • Newly released gem

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Code your thing
  4. Write and run tests: bundle install rake test
  5. Write documentation and make sure it looks good: yard server --reload
  6. Add items to the changelog, in README.
  7. Commit your changes (git commit -am "Add some feature")
  8. Push to the branch (git push origin my-new-feature)
  9. Create new Pull Request