Skip to content

Instantly share code, notes, and snippets.

@smazhara
Created March 30, 2011 16:26
Show Gist options
  • Save smazhara/894730 to your computer and use it in GitHub Desktop.
Save smazhara/894730 to your computer and use it in GitHub Desktop.
#!/usr/bin/env script/runner
# vim:ft=ruby
#
# Script runs endless loop, picks least recently updated wmid, pulls its
# Customer, performs complete import rests 1 second and repeats.
#
# Start:
#
# app/bin/satrap
#
# Stop:
#
# app/bin/satrap stop
#
class Satrap
# For how many seconds to sleep between jobs
#
SLEEP = 1
# For how many seconds to sleep after each timeout error
#
TIMEOUT_SLEEP = 10
# How many times to retry job after it's timed out
#
TIMEOUT_RETRIES = 3
# max timeout for any job - as a safety measure
# wraps any job specific timeouts
#
JOB_TIMEOUT = 30
# where do we keep pid file
#
PIDFILE = '/tmp/satrap.pid'
# script's file change time - used to check if file has changed
#
CTIME = File.ctime(__FILE__).ctime
class << self
include Kantor::Logger
# This is main method that boots and runs Satrap - called at the very bottom
# of class
#
def boot
kill if running?
exit if stopped?
run
end
# Main execution loop.
# Runs forever, picking and running jobs one by one.
#
def run
fork do
write_pid
loop do
jobs.each do |job|
touch_pidfile
Wmid.connection.reconnect!
restart if changed?
retries = TIMEOUT_RETRIES
begin
Timeout::timeout(JOB_TIMEOUT) {send job}
rescue Timeout::Error, Gateway::WebmoneyProxyError
if retries > 0
puts "#{job} failed... (retries left #{retries})"
retries -= 1
retry
else
puts "#{job} failed #{TIMEOUT_RETRIES} times... moving on"
end
rescue
puts "#{job} failed: #{$!.inspect}"
exit
end
sleep SLEEP
end
end
end
end
# Saves this process's PID in pidfile
#
def write_pid
File.open(PIDFILE, 'w') {|f| f.write($$) }
end
# All jobs that need to be continuously executed.
# Simple reflection.
#
def jobs
methods.grep(/_job$/)
end
# Has it been stopped by being called with 'stop' argument?
#
def stopped?
$ARGV[0] == 'stop'
end
# Restarts itself.
#
def restart
exec __FILE__
exit
end
# Has this script changed?
# It needs to restart if so.
#
def changed?
File.ctime(__FILE__).ctime > CTIME
end
# Whether there is another instance (presumably) running.
#
def running?
File.exists? PIDFILE
end
# Kills currently running instance. Either to stop or to restart.
#
def kill
Process.kill("HUP", pid) rescue false
FileUtils.rm PIDFILE
end
# PID of currently running instance, not necessarily THIS instance.
# It can be previous instance which might need to be killed first.
#
def pid
@pid ||= File.readlines(PIDFILE).join.to_i
end
# Touches pid file to make monit happy
#
def touch_pidfile
FileUtils.touch PIDFILE
end
# Picks a purse without wmid and pulls its wmid
#
def populate_purses_wmid_job
purse = WmPurse.first :conditions => 'wmid_id is null'
return unless purse
wmid = Importer.webmoney.wmid_by_purse(purse)
if wmid
purse.wmid = Wmid.find_by_wmid(wmid)
unless purse.wmid
purse.create_wmid(
:wmid => wmid,
:updated_at => 1.year.ago)
end
purse.save if purse.changed?
else
purse.delete
end
end
# Imports both Wmid and WmCertificate since this is one remote call
# It also updates wmid trust level (separate call), since we have only one
# updated_at field so when first call updates object it'll change its
# updated_at field moving it to the very end of update queue.
#
def import_wmid_trust_job
wmid = Wmid.first :order => 'trust_updated_at'
populate_cert(wmid)
wmid.import_trust
end
# Picks WmCertificate that does not have Customer and create Customer object
# for it
#
def populate_wm_certificates_customer_job
cert = WmCertificate.first :conditions => 'customer_id is null'
return unless cert
cert.create_customer
cert.save
end
def import_certificate_job
return # x11 banned by ip by wm - disabled for now
cert = empty_cert ||
not_credit_worthy_stale_cert ||
credit_worthy_stale_cert
if cert.wmids.empty?
cert.destroy
return
end
cert.import if cert
end
# Imports WmBl
#
def import_wm_bl_job
wmid = wmid_without_bl ||
not_credit_worthy_wmid_with_stale_bl ||
credit_worthy_wmid_with_stale_bl
wmid.import_bl if wmid
end
# Imports WmPurseBalance
#
def import_purse_job
wmid = Wmid.first :order => 'purses_updated_at'
wmid.import_purses
end
# Imports WmComments
#
def import_comments_job
wmid = wmid_without_comments ||
not_credit_worthy_wmid_with_stale_comments ||
credit_worthy_wmid_with_stale_comments
wmid.import_comments if wmid
end
def import_kantor_transactions_job
return if run_within_last :import_kantor_transactions, 10.minutes
# only important purses
purses = WmPurse.all(
:conditions => 'name in ("Z849446636219", "D481271580244")')
purses.each do |purse|
purse.import_invoices 1.day.ago
purse.import_payments 1.day.ago
end
end
def import_exchange_rates_job
return if run_within_last :import_exchange_rates, 1.day
Importer.import_rates
end
def populate_cert(wmid)
unless wmid.certificate
# updated_at=null
wmid.create_certificate(:updated_at => nil)
wmid.save
end
end
def wmid_without_bl
Wmid.first(:conditions => 'bl_updated_at is null')
end
def not_credit_worthy_wmid_with_stale_bl
Wmid.not_credit_worthy.first(
:order => 'bl_updated_at',
:conditions => ['bl_updated_at < ?', 1.month.ago]
)
end
def credit_worthy_wmid_with_stale_bl
Wmid.credit_worthy.first(
:order => 'bl_updated_at',
:conditions => ['bl_updated_at < ?', 1.week.ago]
)
end
def wmid_without_comments
Wmid.first :conditions => ['comments_updated_at is null']
end
def not_credit_worthy_wmid_with_stale_comments
Wmid.not_credit_worthy.first(
:order => 'comments_updated_at',
:conditions => ['comments_updated_at < ?', 1.month.ago]
)
end
def credit_worthy_wmid_with_stale_comments
Wmid.credit_worthy.first(
:order => 'comments_updated_at',
:conditions => ['comments_updated_at < ?', 1.week.ago]
)
end
attr :last_run
def run_within_last(method, interval)
@last_run ||= {}
if @last_run[method] and @last_run[method] + interval > Time.now
return false
else
@last_run[method] = Time.now
return true
end
end
def empty_cert
WmCertificate.first :conditions => 'fullaccess is null'
end
def not_credit_worthy_stale_cert
WmCertificate.first(
:conditions => ['level < ? and update_at < ?',
WmCertificate::PERSONAL, 1.week.ago],
:order => 'updated_at'
)
end
def not_credit_worthy_stale_cert
WmCertificate.first(
:conditions => ['level < ? and update_at < ?',
WmCertificate::PERSONAL, 1.month.ago],
:order => 'updated_at'
)
end
end
boot
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment