bk5115545 bk5115545 - 1 year ago 42
Ruby Question

Long sleep in Ruby

I have a rather large Sinatra app (about 4k lines) and one portion of it involves generating reports for the user to download.

I want the reports to auto-delete after so many days even if the webserver is restarted. I don't have a daemon process on the server and it's Windows or else I would just throw up some cron.

How is this kind of thing normally done?

I was thinking about something like the below but it wouldn't persist after server restart.

report_filename = "asdasdsad.pdf"
generate report(report_filename)
Thread.new {
sleep((60*60*24) * 7) # wait 7 days

mwp mwp
Answer Source

Normally you would use cron or some kind of job queue like delayed_job or Sidekiq. You can do cron jobs in Windows with the Task Scheduler or the command-line AT command. If that isn't an option for some other reason, you can do something like wp-cron, which uses incoming requests as a kind of timer to see if there are any jobs that need to be run. It would look like:

require 'sinatra'
require 'date'

class App < Sinatra::Base
  configure do
    set :report_dir, '/path/to/reports'
    set :keep_for, 7 # days

  helpers do
    def generate
      # write .pdf to settings.report_dir and return path to pdf

    def cleanup
      Dir.glob(File.expand_path('*.pdf', settings.report_dir)) do |pdf|
        stat = File.lstat(pdf)
        File.unlink(pdf) if stat.file? and Date.today - stat.ctime.to_date > settings.keep_for
    rescue Errno::ENOENT
      # another thread is working

  post '/generate_report' do
    send_file generate

  after do
    Thread.new { cleanup } if rand < 0.01 # 1% of requests trigger cleanup

This is obviously a simplistic example to get you started. You might consider using a mutex as well if you're concerned about multiple cleanup threads running concurrently and interfering with each other, and catching the ENOENT becomes expensive.