Created
March 30, 2011 16:26
-
-
Save smazhara/894730 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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