uncomplicated_mutex 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/uncomplicated_mutex.rb +72 -0
- 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: []
|