Soft deletes in Rails

Inspired by gist from DHH I bring you simple recipe for slightly more powerful soft deletes. Main feature is adding before_trash and after_trash callbacks by utilizing define_model_callbacks. Another nice-to-have is recording timestamp of deletion.

Recommended reading for this and another patterns for soft-deletes is Richard Dingwall’s article.

Filed under  //   callbacks   rails   soft deletes  

Comments [0]

Clockwork and delayed job and PostgreSQL

Clockwork is simple and very useful tool for scheduling tasks instead of using cron jobs in ruby.

First things first, add clockwork to your Gemfile:

gem 'clockwork'

Configuration is kept in config/clock.rb. It uses plain ruby DSL. Most amazing thing is to use custom handler which allows you to skip loading Rails environment and insert scheduled jobs directly into delyed job database table.

# Clockfile
require 'pg'
require 'yaml'
db = YAML.load_file(File.dirname(__FILE__) + "/database.yml")[ENV['RAILS_ENV'] || "development"]
conn = PGconn.connect(db['host'], nil, nil, nil, db['database'], db['username'], db['password'])

require 'clockwork'
include Clockwork


handler do |job|
  db_time = Time.now.utc.strftime("%Y-%m-%d %H:%M:%S")
  handler = "--- !ruby/object:ScheduledJob \njob: #{job}\n"
  conn.exec("INSERT INTO \"delayed_jobs\" (\"failed_at\", \"locked_by\", \"created_at\", \"handler\", \"updated_at\", \"priority\", \"run_at\", \"attempts\", \"locked_at\", \"last_error\") VALUES(NULL, NULL, '#{db_time}', '#{handler}', '#{db_time}', 0, '#{db_time}', 0, NULL, NULL)")
end

every(30.minutes, 'reactivate_paid_accounts')
# and more every blocks with definitions

And finally example of ScheduledJob:

# app/workers/scheduled_job.rb
class ScheduledJob
  attr_accessor :job
  SUPPORTED_JOBS = { 
                     :reactivate_paid_accounts    => 'Account.reactivate_paid'           
  }

  def initialize(job)
    self.job = job
    unless SUPPORTED_JOBS.has_key?(job.to_sym)
      raise "Unsupported scheduled action - #{job}"
    end
  end

  def perform
    klass, method = SUPPORTED_JOBS[job.to_sym].split('.')
    Kernel.const_get(klass).send(method.to_sym)
  end

  def display_name
    "Job/#{job}"
  end
end
Filed under  //   cron   delayed_job   postgresql   ruby  

Comments [0]

Capistrano task to generate Logrotate config

Motivation: your logs are growing request by request and your free disk space is smaller and smaller. Solution is log rotation with logrotate.

Requirements:

  • nginx + passenger (installed in /opt/nginx)
  • logrotate (aptitude install logrotate)
  • capistrano (deploying into /home/web/YOUR_APPLICATION_NAME)
  • logs to rotate (production.log, access.log, error.log, newrelic_agent.log, …)

Capistrano part

Logrotate configuration will be generated from ERB template:

# logrotate.erb.conf
# Logrotate config for <%= application %>
# Generated at <%= Time.now.strftime("%d.%m.%Y, %H:%M") %>
<%= shared_path %>/log/*.log {
  daily
  missingok
  rotate 30
  compress
  delaycompress
  sharedscripts
  olddir <%= shared_path %>/log/old
  postrotate
    test ! -f /opt/nginx/logs/nginx.pid || kill -USR1 `cat /opt/nginx/logs/nginx.pid`
  endscript
}

Notes:

  • old logs moved under old directory, compressed and kept for 30 days
  • no slow copytruncate, after rotation of all logs in directory (sharedscripts) send USR1 signal to nginx to reopen logs

To generate logrotate configuration use following capistrano task:

# logrotate.rb Capistrano task
namespace :logrotate do
  desc "Create logrotate configuration file"
  task :create_conf, :roles => :web do
    template = File.read(File.join(File.dirname(__FILE__), "logrotate.conf.erb"))
    buffer   = ERB.new(template).result(binding)
    put buffer, "#{shared_path}/config/logrotate.conf"
  end
end
after 'setup', 'logrotate:create_conf'

Server part

To include generated configuration file add following to server’s /etc/logrotate.conf

include /home/web/YOUR_APPLICATION_NAME/shared/config/logrotate.conf

To ensure periodic rotation logrotate relies on daily execution by cron. In Debian it is placed in /etc/cron.daily/logrotate.

Resume

I have seen examples promoting virtual host generation from ERB template by capistrano tasks but why not apply it to generating logrotate configuration? You got it bundled (and versioned) with your application sources and that is big win.

Filed under  //   capistrano   logrotate   logs  

Comments [0]

About

Web developer. Visit my links. Follow me on twitter.