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.
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: []