0

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
  File.delete(report_filename)
}
bk5115545
  • 61
  • 7
  • Your question is an XY problem. You're asking how to do your solution, but the question should be "What is the right way to generate and delete reports after n days". This shouldn't be done inside Sinatra, instead let it serve the output of the report generator. If the reports run quickly then do it inline. If they take a while then do them outside Sinatra from some sort of cron. Either way, write something that runs at an interval to purge the old files outside of Sinatra. Report age is determined by looking at its timestamp on disk, which will survive restarting the server. – the Tin Man Aug 03 '16 at 18:16

3 Answers3

3

How is this kind of thing normally done?

A couple of ways in the event you don't have cron or similar schedulers on the system.

  1. A messaging server such as RabbitMQ, onto which you place a message with some sort of property that prevents the message from being processed and consumed until after a certain amount of time has elapsed.

  2. A service such as resque

  3. A daemon job in your server which checks the reports folder once a day (perhaps at 2am or some other less busy time) and removes files created more than a week prior

  4. Similar to above, a daemon job which checks a db table that lists the filenames, and issues deletes for files created more than a week ago.

mcfinnigan
  • 10,367
  • 30
  • 28
1

Either

  1. Use the windows task scheduler (there is an equivalent to cronjobs on windows, see https://stackoverflow.com/a/132975/232838 for more info)
  2. Just check for old files to be deleted every time you create a new report. I don't assume that it's a problem for reports to lie around a bit too long (I assume you want to delete the old files for storage reasons).
Community
  • 1
  • 1
koffeinfrei
  • 1,905
  • 13
  • 19
1

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
  end

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

    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
      end
    rescue Errno::ENOENT
      # another thread is working
    end
  end

  post '/generate_report' do
    send_file generate
  end

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

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.

mwp
  • 7,595
  • 17
  • 25