Class: EY::Snaplock

Inherits:
Object
  • Object
show all
Defined in:
lib/ey_snaplock.rb,
lib/ey_snaplock/timer.rb,
lib/ey_snaplock/version.rb,
lib/ey_snaplock/database.rb,
lib/ey_snaplock/database/mysql.rb,
lib/ey_snaplock/database/postgresql9.rb

Defined Under Namespace

Classes: Database, Timer

Constant Summary collapse

REQUEST_TIMEOUT =
15
PER_DATABASE_TIMEOUT =
5
VERSION =
'0.1.0'

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(callback_uri = nil, *database_uris) ⇒ Snaplock

Returns a new instance of Snaplock.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/ey_snaplock.rb', line 16

def initialize(callback_uri = nil, *database_uris)
  if callback_uri.nil?
    $stderr.puts "Usage: ey-snaplock CALLBACK_URI [DATABASE_URI] [DATABASE_URI] ..."
    abort "Error: CALLBACK_URI is required."
  end

  callback = request(callback_uri)
  callback = timeout(&callback)
  callback = sync_filesystem(&callback)

  database_uris.each do |database_uri| # it'd be nice to parallelize these database locks when we have more than one
    callback = database_lock(database_uri, PER_DATABASE_TIMEOUT, &callback)
  end
  @callback = callback
end

Class Method Details

.call(*argv) ⇒ Object



11
12
13
14
# File 'lib/ey_snaplock.rb', line 11

def self.call(*argv)
  new(*argv).call
  exit 0
end

Instance Method Details

#callObject



32
33
34
35
36
37
38
39
# File 'lib/ey_snaplock.rb', line 32

def call
  success, code, body = @callback.call
  if success
    exit
  else
    abort "Error: Received unsuccessful response #{code} from callback:\n#{body}"
  end
end

#database_lock(database_uri, timeout, &block) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/ey_snaplock.rb', line 70

def database_lock(database_uri, timeout, &block)
  database = Database.for(database_uri)
  with_locked_db = block
  lambda do
    lock_filename = database.lock_filename
    file = File.open(lock_filename, File::RDWR|File::EXCL|File::CREAT) rescue nil
    if !file && File.mtime(lock_filename) < (Time.now - (20 * 60 * 60))
      $stdout.puts "Cleared stale database lock: #{lock_filename}"
      file = File.open(lock_filename, File::RDWR|File::EXCL)
    elsif !file
      $stderr.puts "Failed to acquire database lock: #{lock_filename}"
      exit 1
    end
    file.write(Process.pid.to_s)
    file.close
    begin
      database.with_lock(timeout, &with_locked_db)
    ensure
      File.delete(lock_filename) if File.exists?(lock_filename)
    end
  end
end

#request(uri) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/ey_snaplock.rb', line 41

def request(uri)
  uri = URI.parse(uri)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true if uri.scheme == "https"

  lambda do
    response = http.request_post(post_path_for_uri(uri), "", "Content-Type" => "application/x-www-form-urlencoded")
    [(200...300).include?(response.code.to_i), response.code.to_i, response.body]
  end
end

#sync_filesystem(&block) ⇒ Object



63
64
65
66
67
68
# File 'lib/ey_snaplock.rb', line 63

def sync_filesystem(&block)
  lambda do
    `sync && sync && sync`
    yield
  end
end

#timeoutObject



52
53
54
55
56
57
58
59
60
61
# File 'lib/ey_snaplock.rb', line 52

def timeout
  lambda do
    begin
      EY::Snaplock::Timer.timeout(REQUEST_TIMEOUT) { yield }
    rescue Timeout::Error
      $stderr.puts "Timeout Exceeded: Callback request took longer than #{REQUEST_TIMEOUT} seconds."
      raise
    end
  end
end