1

I daemonized a Ruby scheduler script (using Rufus) with Rufus-Scheduler DaemonKit and I'm trying to trap the TERM or INT signals to have the application try to save state before quitting.

DaemonKit has its own trap_state (private) method and it catches the signal before the daemon script so even though I have this block, it doesn't do much.

DaemonKit::Application.running! do |config|

  surprise = Surprise.new(interval, frequency, false)
  surprise.start

  config.trap( 'SIGINT' ) do #tried INT and TERM as well
    puts 'Exiting'
    surprise.stop
    File.delete($lock)
  end
end

As a side effect (maybe a mistake in my implementation ?) after sigterm the .rufus lockfile is still there

The behavior on ctrl-c right now is this

[daemon-kit]: DaemonKit (0.3.1) booted, now running surprise
log writing failed. can't be called from trap context
[daemon-kit]: Running signal traps for INT
log writing failed. can't be called from trap context
[daemon-kit]: Running shutdown hooks
log writing failed. can't be called from trap context
[daemon-kit]: Shutting down surprise

The start method is a pretty simple schedule

def start

@scheduler = Rufus::Scheduler.new(:lockfile =>  $lock)

@scheduler.every '1d', :first_at => @first, :overlap => false do |job|
  ... # some work
end

 @scheduler.join
end

def stop
  # save state
  @scheduler.shutdown
end
blackbird
  • 1,129
  • 1
  • 23
  • 48
  • Sorry, but there is no trap in rufus-scheduler 3.x (rufus-scheduler 2.x had one, but limited to its special SignalScheduler implementation). Are you sure your `File.delete($lock)` line is reached? Are you sure it would succeed if reached? Place a `puts 'Exited'` at the end of your trap... Wolf fencing. – jmettraux Oct 09 '14 at 21:15
  • 1
    @jmettraux ugh I'm stupid, I mixed up DaemonKit and Rufus. It's DaemonKit trapping TERM, my bad – blackbird Oct 10 '14 at 01:56
  • Please update your explanation then. Thanks in advance! – jmettraux Oct 10 '14 at 01:59
  • @jmettraux yep, done. I found the problem too. – blackbird Oct 10 '14 at 02:27

2 Answers2

2

Looking at your own answer, and the following code you pasted:

def start
  @scheduler = Rufus::Scheduler.new(:lockfile =>  $lock)
  # ...
  @scheduler.join # <- NOT NEEDED
end

DaemonKit's DaemonKit::Application.running! block actually never finishes running, so you could safely skip calling #join on any thread.

We should work on making this use-case more clear, as I would love see it used more widely for this kinda work.

Kenneth Kalmer
  • 341
  • 2
  • 5
  • Thanks ! That might be a leftover when I used Rufus on its own before DK. When would I need to join the threads though ? – blackbird Oct 10 '14 at 14:01
  • In a vanilla Ruby project you'd need to do that. DaemonKit abstracts away the need to keep your script up. It will by hook, or by crook, keep your process alive and running. – Kenneth Kalmer Oct 13 '14 at 07:02
1

So it's very simple, I need to configure the trap proc (or block in my case) BEFORE I run the scheduler in the start method. Not feeling very clever right about now, but the following code works as expected. For reference, the set_trap is private in DK but the public trap method overrides the defaults that come with the DK startup.

DaemonKit::Application.running! do |config|

  surprise = Surprise.new(interval, frequency, false)

  config.trap("TERM") { surprise.stop }
  config.trap( "INT" ) { surprise.stop }

  surprise.start
end

Interestingly I saw this line on startup that I hadn't noticed before

[daemon-kit]: Trapping SIGINT signals not supported on this platform

INT and TERM both work though

blackbird
  • 1,129
  • 1
  • 23
  • 48
  • I see, in `Surprise#start` you call `Scheduler#join` which blocks the running code and the following traps never got set. That is why in this version it works. – Kenneth Kalmer Oct 10 '14 at 11:30