public this repo is viewable by everyone
Description: Global named locks (req. mysql) -- Extraction from Shopify
Homepage: http://www.shopify.com
Clone URL: git://github.com/tobi/locking.git
Initial git import
Tobias Lütke (author)
2 months ago
commit  ef1e9941feb2788d6a4580bd7e033aaefad2cd73
tree    e2c02b89c229fdb16ba6d45f9e965ea9a3890504
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
0
@@ -0,0 +1,26 @@
0
+Locking
0
+=======
0
+
0
+Very simple plugin which adds the aquire_lock method to active record base. This is mysql only.
0
+
0
+As long as a lock of a certain name is acquired no other connection on the server is able to aquire the same lock.
0
+This is very useful for background tasks such as billing or search server re-indexing which would cause errors when they
0
+would be ran from multiple machines at the same time but you still want to run them from multiple machines at the same time
0
+for redundancy reasons.
0
+
0
+Example
0
+=======
0
+
0
+Invoice.aquire_lock("Shopify billing") do
0
+
0
+ Invoice.find_all_due.each { |invoice| invoice.collect_payment! }
0
+
0
+end
0
+
0
+
0
+Usecase
0
+=======
0
+
0
+# save to run on several machines at the same time:
0
+
0
+ ./script/runner 'Invoice.aquire_lock("Shopify billing") { Invoice.find_all_due.each { |invoice| invoice.collect_payment! } }'
0
\ No newline at end of file
...
 
 
 
0
...
1
2
3
4
0
@@ -0,0 +1,3 @@
0
+require 'locking'
0
+
0
+ActiveRecord::Base.send(:include, Locking)
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
0
@@ -0,0 +1,39 @@
0
+module Locking
0
+ class LockError < ActiveRecord::ActiveRecordError
0
+ end
0
+
0
+ class LockTimeout < LockError
0
+ end
0
+
0
+ def self.included(base)
0
+ base.send(:extend, ClassMethods)
0
+ end
0
+
0
+ module ClassMethods
0
+
0
+ # Aquires an application level lock in the mysql server.
0
+ def aquire_lock(lock_name = table_name, wait_timeout = 0)
0
+ case c = connection.select_value("SELECT GET_LOCK(#{quote_value(lock_name)}, #{quote_value(wait_timeout)})")
0
+ when '1'
0
+ yield if block_given?
0
+ when '0'
0
+ false
0
+ when 'NULL'
0
+ raise LockError, "Error in locking mechanism"
0
+ else
0
+ raise LockError, "Unknown response from database: #{c.inspect}"
0
+ end
0
+
0
+ ensure
0
+ connection.select_one("SELECT RELEASE_LOCK(#{quote_value(lock_name)})")
0
+ end
0
+
0
+ # Aquires an application level lock in the mysql server. Throws Locking::LockTimeout if the
0
+ # lock cannot be aquired.
0
+ def aquire_lock!(lock_name = table_name, wait_timeout = 0, &block)
0
+ aquire_lock(lock_name, table_name, &block) or raise LockTimeout, 'Timeout waiting for lock'
0
+ end
0
+
0
+ end
0
+
0
+end

Comments

    No one has commented yet.