redlocker 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/.github/workflows/test.yml +30 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +22 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +87 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/docker-compose.yml +6 -0
- data/lib/redlocker/client.rb +59 -0
- data/lib/redlocker/lock.rb +124 -0
- data/lib/redlocker/version.rb +5 -0
- data/lib/redlocker.rb +11 -0
- data/redlocker.gemspec +33 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e6b158d009f6f41ce941ec241da5e4249fb5d76fc88719065eb91bbaebba4346
|
4
|
+
data.tar.gz: 3c8a024c05fab2a0d7d02409ecd2579da2c6491b3a0d1dc32de4980b6358d10c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f834ec0a9d74c682c37806678da7b4e38555b564c9af61b54cbfb3ab63bc538c0c19c5d59d18cb8d09e679086a6f51cfdaa9d1312f55f2529f819c0e2ce447be
|
7
|
+
data.tar.gz: b3e712bac4dd7f199505a64ba5dc0b7367ab847864e5ed10fb52d2ae25a70915c0ff5a78d87e9f48bb114e356ee5ed098998e3a5e7f8e0191335848b4d373c2e
|
@@ -0,0 +1,30 @@
|
|
1
|
+
on: push
|
2
|
+
name: test
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
strategy:
|
7
|
+
fail-fast: false
|
8
|
+
matrix:
|
9
|
+
redis:
|
10
|
+
- redis:5.0
|
11
|
+
- redis:6.0
|
12
|
+
ruby:
|
13
|
+
- 2.6
|
14
|
+
- 2.7
|
15
|
+
- 3.0
|
16
|
+
services:
|
17
|
+
elasticsearch:
|
18
|
+
image: ${{ matrix.redis }}
|
19
|
+
ports:
|
20
|
+
- 6379:6379
|
21
|
+
steps:
|
22
|
+
- uses: actions/checkout@v1
|
23
|
+
- uses: actions/setup-ruby@v1
|
24
|
+
with:
|
25
|
+
ruby-version: ${{ matrix.ruby }}
|
26
|
+
- run: gem install bundler
|
27
|
+
- run: bundle
|
28
|
+
- run: sleep 3
|
29
|
+
- run: bundle exec rspec
|
30
|
+
- run: bundle exec rubocop
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
AllCops:
|
2
|
+
NewCops: enable
|
3
|
+
TargetRubyVersion: 2.5
|
4
|
+
SuggestExtensions: false
|
5
|
+
|
6
|
+
Metrics/MethodLength:
|
7
|
+
Enabled: false
|
8
|
+
|
9
|
+
Metrics/BlockLength:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Style/Documentation:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Style/NumericPredicate:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Gemspec/RequireMFA:
|
22
|
+
Enabled: false
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Benjamin Vetter
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# Redlocker
|
2
|
+
|
3
|
+
[](https://github.com/mrkamel/redlocker/actions?query=workflow%3Atest+branch%3Amaster)
|
4
|
+
[](http://badge.fury.io/rb/redlocker)
|
5
|
+
|
6
|
+
**Acquire and keep distributed locks alive using redis**
|
7
|
+
|
8
|
+
There are already quite some ruby libraries available which use redis for the
|
9
|
+
purpose of distributed locking, but they require you to specify the time
|
10
|
+
time-to-live of your locks. Contrary, Redlocker allows you to easily acquire
|
11
|
+
and keep distributed locks alive using redis. An acquired lock gets
|
12
|
+
automatically renewed every second from a thread, i.e. its 5 second expiry
|
13
|
+
value gets renewed in redis every second, and it gets released as soon as the
|
14
|
+
given block finishes.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'redlocker'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
$ bundle install
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
$ gem install redlocker
|
31
|
+
|
32
|
+
## Usage
|
33
|
+
|
34
|
+
Using Redlocker could not be easier:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
RedlockerClient = Redlocker::Client.new(redis: Redis.new)
|
38
|
+
|
39
|
+
RedlockerClient.with_lock('some_lock', timeout: 5) do
|
40
|
+
# lock acquired
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
If the lock can not be acquired within the specified `timeout`, a
|
45
|
+
`Redlocker::TimeoutError` is raised.
|
46
|
+
|
47
|
+
When the block finishes or raises, the acquired lock gets freed.
|
48
|
+
|
49
|
+
You can optionally pass a `delay` when acquiring a lock, which specifies the
|
50
|
+
time to wait between subsequent calls which check in redis whether or not the
|
51
|
+
lock is free. Default is 0.25 seconds:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
RedlockerClient.with_lock("some lock", timeout: 5, delay: 1) do
|
55
|
+
# lock acquired
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
If you are using a shared redis, you can pass a namespace, which will be used for
|
60
|
+
prefixing redis keys in addition to the default `redlocker:` namespace.
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
RedlockerClient = Redlocker::Client.new(redis: Redis.new, namespace: "my-namespace")
|
64
|
+
```
|
65
|
+
|
66
|
+
That's it.
|
67
|
+
|
68
|
+
## Reference docs
|
69
|
+
|
70
|
+
Please find the reference docs at
|
71
|
+
[http://www.rubydoc.info/github/mrkamel/redlocker](http://www.rubydoc.info/github/mrkamel/redlocker)
|
72
|
+
|
73
|
+
## Development
|
74
|
+
|
75
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
76
|
+
`bundle exec rspec` to run the tests. You can also run `bin/console` for an
|
77
|
+
interactive prompt that will allow you to experiment.
|
78
|
+
|
79
|
+
## Contributing
|
80
|
+
|
81
|
+
Bug reports and pull requests are welcome on GitHub at
|
82
|
+
https://github.com/mrkamel/redlocker.
|
83
|
+
|
84
|
+
## License
|
85
|
+
|
86
|
+
The gem is available as open source under the terms of the [MIT
|
87
|
+
License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'redlocker'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'irb'
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/docker-compose.yml
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Redlocker
|
4
|
+
# The `Redlocker::Client` class allows to easily acquire and keep distributed
|
5
|
+
# locks using redis. The acquired lock gets automatically renewed every
|
6
|
+
# second from a thread, i.e. its 5 second expiry value gets renewed in redis
|
7
|
+
# every second, and it gets released when the given block finishes.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# RedlockerClient = Redlocker::Client.new(redis: Redis.new)
|
11
|
+
#
|
12
|
+
# RedlockerClient.with_lock("some_lock", timeout: 5, delay: 1) do
|
13
|
+
# # lock acquired
|
14
|
+
# end
|
15
|
+
|
16
|
+
class Client
|
17
|
+
attr_reader :redis, :namespace
|
18
|
+
|
19
|
+
# Creates a new `Redlocker::Client` instance.
|
20
|
+
#
|
21
|
+
# @param redis [Redis] The redis connection
|
22
|
+
# @param namespace [String] An optional namespace to use for redis keys in
|
23
|
+
# addition to the default `redlocker:` namespace.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# RedlockerClient = Redlocker::Client.new(redis: Redis.new)
|
27
|
+
|
28
|
+
def initialize(redis:, namespace: nil)
|
29
|
+
@redis = redis
|
30
|
+
@namespace = namespace
|
31
|
+
end
|
32
|
+
|
33
|
+
# Acquires the specified lock or raises a `Redlocker::TimeoutError` when
|
34
|
+
# the lock can not be acquired within the specified `timeout`. You can pass
|
35
|
+
# a `delay`, which specifies how long to wait between subsequent checks of
|
36
|
+
# whether or not the lock is free. When the lock has been successfully
|
37
|
+
# acquired, it gets refreshed every second, i.e. its expiry value of 5
|
38
|
+
# seconds is refreshed within redis every second.
|
39
|
+
#
|
40
|
+
# @param name [String] The name of the lock. Will be used as the redis key
|
41
|
+
# for the lock.
|
42
|
+
# @param timeout [Integer, Float] How long to wait for the lock. If the lock
|
43
|
+
# can not be acquired within that time, a `Redlocker::TimeoutError` will be
|
44
|
+
# raised.
|
45
|
+
# @param delay [Integer, Float] How long to wait between subsequent checks
|
46
|
+
# of whether or not the lock is free. Default is 0.25 seconds.
|
47
|
+
# @param block [Proc] The block which should be executed when the lock is
|
48
|
+
# acquired.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# RedlockerClient.with_lock("some_lock", timeout: 5, delay: 1) do
|
52
|
+
# # lock acquired
|
53
|
+
# end
|
54
|
+
|
55
|
+
def with_lock(name, timeout:, delay: 0.25, &block)
|
56
|
+
Lock.new(client: self, name: name, timeout: timeout, delay: delay).acquire(&block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Redlocker
|
4
|
+
# The `Redlocker::Lock` class allows to easily acquire and keep distributed
|
5
|
+
# locks using redis. The acquired lock gets automatically renewed every
|
6
|
+
# second, i.e. its 5 second expiry value gets renewed in redis every second,
|
7
|
+
# and it gets released when the given block finishes.
|
8
|
+
|
9
|
+
class Lock
|
10
|
+
attr_reader :client, :name, :timeout, :delay, :token
|
11
|
+
|
12
|
+
# Creates a new `Redlocker::Lock` instance.
|
13
|
+
#
|
14
|
+
# @param client [Redlocker::Client] The client
|
15
|
+
# @param name [String] The name of the lock
|
16
|
+
# @param timeout [Integer, Float] How long to wait for the lock. If the lock
|
17
|
+
# can not be acquired within that time, a `Redlocker::TimeoutError` will be
|
18
|
+
# raised.
|
19
|
+
# @param delay [Integer, Float] How long to wait between subsequent checks
|
20
|
+
# of whether or not the lock is free. Default is 0.25 seconds.
|
21
|
+
|
22
|
+
def initialize(client:, name:, timeout:, delay:)
|
23
|
+
@client = client
|
24
|
+
@name = name
|
25
|
+
@timeout = timeout
|
26
|
+
@delay = delay
|
27
|
+
@token = SecureRandom.hex
|
28
|
+
end
|
29
|
+
|
30
|
+
# Acquires the specified lock or raises a `Redlocker::TimeoutError` when
|
31
|
+
# the lock can not be acquired within the specified `timeout`. Uses the
|
32
|
+
# specified `delay` to poll redis and check whether or not the lock is
|
33
|
+
# free. When the lock has been successfully acquired, it gets refreshed
|
34
|
+
# every second, i.e. its expiry value of 5 seconds is refreshed within
|
35
|
+
# redis every second.
|
36
|
+
#
|
37
|
+
# @param block [Proc] The block which should be executed when the lock is
|
38
|
+
# acquired.
|
39
|
+
|
40
|
+
def acquire(&block)
|
41
|
+
raise(TimeoutError, "Did not get lock within #{timeout} seconds") unless acquire_lock
|
42
|
+
|
43
|
+
begin
|
44
|
+
keep_lock(&block)
|
45
|
+
ensure
|
46
|
+
release_lock
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def redis
|
53
|
+
client.redis
|
54
|
+
end
|
55
|
+
|
56
|
+
def namespace
|
57
|
+
client.namespace
|
58
|
+
end
|
59
|
+
|
60
|
+
def release_lock
|
61
|
+
redis.del(redis_key_name) if redis.get(redis_key_name) == token
|
62
|
+
end
|
63
|
+
|
64
|
+
def keep_lock(&block)
|
65
|
+
stop = false
|
66
|
+
mutex = Mutex.new
|
67
|
+
|
68
|
+
Thread.new do
|
69
|
+
until mutex.synchronize { stop }
|
70
|
+
begin
|
71
|
+
sleep 1
|
72
|
+
|
73
|
+
redis.expire(redis_key_name, 5)
|
74
|
+
rescue StandardError
|
75
|
+
# nothing
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
block.call
|
81
|
+
ensure
|
82
|
+
mutex.synchronize do
|
83
|
+
stop = true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def acquire_lock
|
88
|
+
start = Time.now.to_f
|
89
|
+
|
90
|
+
loop do
|
91
|
+
return true if try_acquire_lock
|
92
|
+
return false if Time.now.to_f - start > timeout
|
93
|
+
|
94
|
+
sleep delay
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def try_acquire_lock
|
99
|
+
get_lock_script = <<~GET_LOCK_SCRIPT
|
100
|
+
local lock_key_name, id, expire_value = ARGV[1], ARGV[2]
|
101
|
+
|
102
|
+
local cur = redis.call('get', lock_key_name)
|
103
|
+
|
104
|
+
if not cur then
|
105
|
+
redis.call('setex', lock_key_name, 5, id)
|
106
|
+
|
107
|
+
return true
|
108
|
+
elseif cur == id then
|
109
|
+
redis.call('expire', lock_key_name, 5)
|
110
|
+
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
|
114
|
+
return false
|
115
|
+
GET_LOCK_SCRIPT
|
116
|
+
|
117
|
+
redis.eval(get_lock_script, argv: [redis_key_name, token])
|
118
|
+
end
|
119
|
+
|
120
|
+
def redis_key_name
|
121
|
+
@redis_key_name ||= [namespace, 'redlocker', name].compact.join(':')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/redlocker.rb
ADDED
data/redlocker.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/redlocker/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'redlocker'
|
7
|
+
spec.version = Redlocker::VERSION
|
8
|
+
spec.authors = ['Benjamin Vetter']
|
9
|
+
spec.email = ['benjamin.vetter@wlw.de']
|
10
|
+
|
11
|
+
spec.summary = 'Acquire and keep distributed locks alive using redis'
|
12
|
+
spec.description = 'Acquire and keep distributed locks alive using redis'
|
13
|
+
spec.homepage = 'https://github.com/mrkamel/redlocker'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = 'https://github.com/mrkamel/redlocker'
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/mrkamel/redlocker/blob/master/CHANGELOG.md'
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.bindir = 'exe'
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ['lib']
|
29
|
+
|
30
|
+
spec.add_development_dependency 'rspec'
|
31
|
+
spec.add_development_dependency 'rubocop'
|
32
|
+
spec.add_dependency 'redis'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redlocker
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Vetter
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-11-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: redis
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Acquire and keep distributed locks alive using redis
|
56
|
+
email:
|
57
|
+
- benjamin.vetter@wlw.de
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".github/workflows/test.yml"
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- ".rubocop.yml"
|
66
|
+
- CHANGELOG.md
|
67
|
+
- Gemfile
|
68
|
+
- LICENSE.txt
|
69
|
+
- README.md
|
70
|
+
- Rakefile
|
71
|
+
- bin/console
|
72
|
+
- bin/setup
|
73
|
+
- docker-compose.yml
|
74
|
+
- lib/redlocker.rb
|
75
|
+
- lib/redlocker/client.rb
|
76
|
+
- lib/redlocker/lock.rb
|
77
|
+
- lib/redlocker/version.rb
|
78
|
+
- redlocker.gemspec
|
79
|
+
homepage: https://github.com/mrkamel/redlocker
|
80
|
+
licenses:
|
81
|
+
- MIT
|
82
|
+
metadata:
|
83
|
+
homepage_uri: https://github.com/mrkamel/redlocker
|
84
|
+
source_code_uri: https://github.com/mrkamel/redlocker
|
85
|
+
changelog_uri: https://github.com/mrkamel/redlocker/blob/master/CHANGELOG.md
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 2.5.0
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubygems_version: 3.0.3
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Acquire and keep distributed locks alive using redis
|
105
|
+
test_files: []
|