uncomplicated_mutex 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/uncomplicated_mutex.rb +72 -0
  3. metadata +72 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3dd807b45351f0f39f3a4b62ddb90f348e4cc71a
4
+ data.tar.gz: dd1221b4fa3d1a0414513dc06e7a91b1166b3a90
5
+ SHA512:
6
+ metadata.gz: cfd9eb1d7d2828c673ce55f9534d96e1f6319b86f128b715f890c9fbda64a9a8fd7694c860c060ac0e9012c8fec8b4dc09032edcefa94762099b1545caed0cb4
7
+ data.tar.gz: e70f88dd37b4c0d1dca6616fbfd803953deff0cc10759980f89e5f8a5834eea5bbad9fc278c2b154ce16f8e42c87285dd66c62eb6c30f3a0f215d8270ba6273c
@@ -0,0 +1,72 @@
1
+ require 'digest/md5'
2
+ require 'redis'
3
+
4
+ class UncomplicatedMutex
5
+ attr_reader :lock_name
6
+
7
+ MutexTimeout = Class.new(StandardError)
8
+
9
+ LUA_ACQUIRE = "return redis.call('SET', KEYS[1], ARGV[2], 'NX', 'EX', ARGV[1]) and redis.call('expire', KEYS[1], ARGV[1]) and 1 or 0"
10
+ LUA_RELEASE = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
11
+
12
+ def initialize(obj, opts = {})
13
+ @verbose = opts[:verbose]
14
+ @timeout = opts[:timeout] || 300
15
+ @fail_on_timeout = opts[:fail_on_timeout]
16
+ @ticks = opts[:ticks] || 100
17
+ @wait_tick = @timeout.to_f / @ticks.to_f
18
+ @redis = opts[:redis] || Redis.new
19
+ @lock_name = "lock:#{obj.class.name}:#{obj.id}".squeeze(":")
20
+ @token = Digest::MD5.new.hexdigest("#{@lock_name}_#{Time.now.to_f}")
21
+ end
22
+
23
+ def acquire_mutex
24
+ puts("Running transaction to acquire the lock #{@lock_name}") if @verbose
25
+ @redis.eval(LUA_ACQUIRE, [ @lock_name ], [ @timeout, @token ]) == 1
26
+ end
27
+
28
+ def destroy_mutex
29
+ puts("Destroying the lock #{@lock_name}") if @verbose
30
+ @redis.del(@lock_name)
31
+ end
32
+
33
+ def lock(&block)
34
+ begin
35
+ wait_for_mutex
36
+ yield block
37
+ ensure
38
+ release_mutex
39
+ end
40
+ end
41
+
42
+ def overwrite_mutex
43
+ puts("Replacing the lock #{@lock_name} with #{@token}") if @verbose
44
+ @redis.set(@lock_name, @token)
45
+ end
46
+
47
+ def recurse_until_ready(depth = 1)
48
+ return false if depth == @ticks
49
+ wait_a_tick if depth > 1
50
+ acquire_mutex || recurse_until_ready(depth + 1)
51
+ end
52
+
53
+ def release_mutex
54
+ puts("Releasing the lock #{@lock_name} if it still holds the value '#{@token}'") if @verbose
55
+ @redis.eval(LUA_RELEASE, [ @lock_name ], [ @token ])
56
+ end
57
+
58
+ def wait_a_tick
59
+ puts("Sleeping #{@wait_tick} for the lock #{@lock_name} to become available") if @verbose
60
+ sleep(@wait_tick)
61
+ end
62
+
63
+ def wait_for_mutex
64
+ if recurse_until_ready
65
+ puts("Acquired lock #{@lock_name}") if @verbose
66
+ else
67
+ puts("Failed to acquire the lock") if @verbose
68
+ raise MutexTimeout.new("Failed to acquire the lock") if @fail_on_timeout
69
+ overwrite_mutex
70
+ end
71
+ end
72
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uncomplicated_mutex
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Coleman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ description: A mutex that uses Redis that is also not complicated.
42
+ email: penguincoder@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/uncomplicated_mutex.rb
48
+ homepage: https://github.com/penguincoder/uncomplicated_mutex
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.2.2
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: Redis. Lua. Mutex.
72
+ test_files: []