redis-lockers 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a851912fde0e448b9ea869edc8e4d987085d97be49e16a9d9c423b0784ccb6ee
4
+ data.tar.gz: ebdc57a08b43f6a958aefc9d4592c8a61c30a9610a5dba6cf51366f19ced3399
5
+ SHA512:
6
+ metadata.gz: 573be720690cc6f04b3e75cfb19b1e4518f1c31aa423e54216e27d6c18ed1e25291c2f44d58761ba1240b6941c0bedbeb2cebe4b7010bc8b6a92932d6c55a3b1
7
+ data.tar.gz: 6ebf339fffae54308e09e01f46b76234ea2bf5ca958ab45d6f8e0ba19d2c8d3991f33ac40154b5b786fb645bd7091a8208514dcb51128b9405de1b988866b3c2
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /Gemfile.lock
2
+
3
+ /.autoenv.zsh
4
+ /.bundle
5
+ /.yardoc
6
+ /_yardoc
7
+ /coverage
8
+ /doc
9
+ /pkg
10
+ /tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,47 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ DisplayCopNames: true
4
+
5
+
6
+ ## Layout ######################################################################
7
+
8
+ Layout/DotPosition:
9
+ EnforcedStyle: trailing
10
+
11
+ Layout/IndentArray:
12
+ EnforcedStyle: consistent
13
+
14
+ Layout/IndentHash:
15
+ EnforcedStyle: consistent
16
+
17
+
18
+ ## Metrics #####################################################################
19
+
20
+ Metrics/BlockLength:
21
+ Exclude:
22
+ - "Guardfile"
23
+ - "spec/**/*"
24
+
25
+
26
+ ## Style #######################################################################
27
+
28
+ Style/BracesAroundHashParameters:
29
+ Enabled: false
30
+
31
+ Style/HashSyntax:
32
+ EnforcedStyle: hash_rockets
33
+
34
+ Style/RegexpLiteral:
35
+ EnforcedStyle: percent_r
36
+
37
+ Style/RescueStandardError:
38
+ EnforcedStyle: implicit
39
+
40
+ Style/SafeNavigation:
41
+ Enabled: false
42
+
43
+ Style/StringLiterals:
44
+ EnforcedStyle: double_quotes
45
+
46
+ Style/YodaCondition:
47
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,32 @@
1
+ language: ruby
2
+ sudo: false
3
+
4
+ services:
5
+ - redis-server
6
+
7
+ cache: bundler
8
+
9
+ env:
10
+ - REDIS_NAMESPACE="wtf"
11
+ - REDIS_NAMESPACE=""
12
+
13
+ rvm:
14
+ - 2.3
15
+ - 2.4
16
+ - 2.5
17
+
18
+ matrix:
19
+ fast_finish: true
20
+ include:
21
+ - rvm: 2.4
22
+ env: TEST_SUITE="rubocop"
23
+
24
+ before_install:
25
+ - gem update --system
26
+ - gem --version
27
+ - gem install bundler --no-rdoc --no-ri
28
+ - bundle --version
29
+
30
+ install: bundle install --without development doc
31
+
32
+ script: bundle exec rake $TEST_SUITE
data/Gemfile ADDED
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+ ruby RUBY_VERSION
5
+
6
+ gem "rake"
7
+ gem "rspec"
8
+ gem "rubocop", "~> 0.52.0", :require => false
9
+
10
+ group :development do
11
+ gem "guard", :require => false
12
+ gem "guard-rspec", :require => false
13
+ gem "guard-rubocop", :require => false
14
+ gem "pry", :require => false
15
+ end
16
+
17
+ group :test do
18
+ gem "codecov", :require => false
19
+ gem "simplecov", :require => false
20
+
21
+ gem "redis-namespace", "~> 1.6", :require => false
22
+ end
23
+
24
+ group :doc do
25
+ gem "redcarpet"
26
+ gem "yard"
27
+ end
28
+
29
+ # Specify your gem's dependencies in redis-prescription.gemspec
30
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ guard :rspec, :cmd => "bundle exec rspec" do
4
+ require "guard/rspec/dsl"
5
+ dsl = Guard::RSpec::Dsl.new(self)
6
+
7
+ # RSpec files
8
+ rspec = dsl.rspec
9
+ watch(rspec.spec_helper) { rspec.spec_dir }
10
+ watch(rspec.spec_support) { rspec.spec_dir }
11
+ watch(rspec.spec_files)
12
+
13
+ # Ruby files
14
+ ruby = dsl.ruby
15
+ dsl.watch_spec_files_for(ruby.lib_files)
16
+ end
17
+
18
+ guard :rubocop do
19
+ watch(%r{.+\.rb$})
20
+ watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
21
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Alexey Zapparov, SensorTower Inc.
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,107 @@
1
+ # Redis::Lockers
2
+
3
+ [![Build Status](https://travis-ci.org/ixti/redis-lockers.svg?branch=master)](https://travis-ci.org/ixti/redis-lockers)
4
+ [![codecov](https://codecov.io/gh/ixti/redis-lockers/branch/master/graph/badge.svg)](https://codecov.io/gh/ixti/redis-lockers)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/5c7c97ef80e7cff43325/maintainability)](https://codeclimate.com/github/ixti/redis-lockers/maintainability)
6
+ [![Inline docs](http://inch-ci.org/github/ixti/redis-lockers.svg?branch=master)](http://inch-ci.org/github/ixti/redis-lockers)
7
+
8
+ Yet another Redis-based lock manager.
9
+
10
+ Simplified implementation of Redlock (distributed lock manger by antirez).
11
+ Right now it works with single Redis node only.
12
+
13
+ ## Why another implementation?
14
+
15
+ I wanted to have simplified implementation that is simple to maintain and work
16
+ with. The most important part I dislike in existing solutions is that they all
17
+ create their own connections, which I find a bit inconvinient.
18
+
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ ```ruby
25
+ gem 'redis-lockers'
26
+ ```
27
+
28
+ And then execute:
29
+
30
+ $ bundle
31
+
32
+ Or install it yourself as:
33
+
34
+ $ gem install redis-lockers
35
+
36
+
37
+ ## Usage
38
+
39
+ ``` ruby
40
+ REDIS = Redis.new
41
+ Redis::Lockers.acquire(REDIS, :resource_name, :ttl => 60_000) do
42
+ # lock was successfully acquired - we're good to run our code
43
+ end
44
+ ```
45
+
46
+
47
+ ## Supported Ruby Versions
48
+
49
+ This library aims to support and is [tested against][travis-ci] the following
50
+ Ruby and Redis client versions:
51
+
52
+ * Ruby
53
+ * 2.3.x
54
+ * 2.4.x
55
+ * 2.5.x
56
+
57
+ * [redis-rb](https://github.com/redis/redis-rb)
58
+ * 4.x
59
+
60
+ * [redis-namespace](https://github.com/resque/redis-namespace)
61
+ * 1.6
62
+
63
+
64
+ If something doesn't work on one of these versions, it's a bug.
65
+
66
+ This library may inadvertently work (or seem to work) on other Ruby versions,
67
+ however support will only be provided for the versions listed above.
68
+
69
+ If you would like this library to support another Ruby version or
70
+ implementation, you may volunteer to be a maintainer. Being a maintainer
71
+ entails making sure all tests run and pass on that implementation. When
72
+ something breaks on your implementation, you will be responsible for providing
73
+ patches in a timely fashion. If critical issues for a particular implementation
74
+ exist at the time of a major release, support for that Ruby version may be
75
+ dropped.
76
+
77
+
78
+ ## Development
79
+
80
+ After checking out the repo, run `bundle install` to install dependencies.
81
+ Then, run `bundle exec rake spec` to run the tests with ruby-rb client.
82
+
83
+ To install this gem onto your local machine, run `bundle exec rake install`.
84
+ To release a new version, update the version number in `version.rb`, and then
85
+ run `bundle exec rake release`, which will create a git tag for the version,
86
+ push git commits and tags, and push the `.gem` file to [rubygems.org][].
87
+
88
+
89
+ ## Contributing
90
+
91
+ * Fork redis-lockers on GitHub
92
+ * Make your changes
93
+ * Ensure all tests pass (`bundle exec rake`)
94
+ * Send a pull request
95
+ * If we like them we'll merge them
96
+ * If we've accepted a patch, feel free to ask for commit access!
97
+
98
+
99
+ ## Copyright
100
+
101
+ Copyright (c) 2018 Alexey Zapparov, SensorTower Inc.<br>
102
+ See [LICENSE.md][] for further details.
103
+
104
+
105
+ [travis.ci]: http://travis-ci.org/ixti/redis-lockers
106
+ [rubygems.org]: https://rubygems.org
107
+ [LICENSE.md]: https://github.com/ixti/redis-lockers/blob/master/LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ require "rspec/core/rake_task"
6
+ RSpec::Core::RakeTask.new
7
+
8
+ require "rubocop/rake_task"
9
+ RuboCop::RakeTask.new
10
+
11
+ if ENV["CI"]
12
+ task :default => :spec
13
+ else
14
+ task :default => %i[rubocop spec]
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redis/lockers/version"
4
+ require "redis/lockers/lock"
5
+
6
+ # @see https://github.com/redis/redis-rb
7
+ class Redis
8
+ # Distributed locks with Redis.
9
+ module Lockers
10
+ # Creates new lock and yields control if it was successfully acquired.
11
+ # Ensures lock is released after execution of the block.
12
+ #
13
+ # @example
14
+ #
15
+ # REDIS = Redis.new
16
+ # Redis::Lockers.acquire(REDIS, :resource_name, :ttl => 60_000) do
17
+ # # lock was successfully acquired - we're good to run our code
18
+ # end
19
+ #
20
+ # @yield [] Executes given block if lock was acquired.
21
+ # @return [Object] result of the last expression in the block
22
+ def self.acquire(redis, *args, **kwargs)
23
+ lock = Lock.new(*args, **kwargs)
24
+ yield if lock.acquire(redis)
25
+ ensure
26
+ lock&.release(redis)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ require "concurrent/utility/monotonic_time"
6
+ require "redis/prescription"
7
+
8
+ class Redis
9
+ module Lockers
10
+ # Single lock instance.
11
+ class Lock
12
+ LOCK_SCRIPT = Redis::Prescription.read("#{__dir__}/scripts/lock.lua")
13
+ private_constant :LOCK_SCRIPT
14
+
15
+ UNLOCK_SCRIPT = Redis::Prescription.read("#{__dir__}/scripts/unlock.lua")
16
+ private_constant :UNLOCK_SCRIPT
17
+
18
+ # Create a new Lock instance.
19
+ # @param key [#to_s] Resource name
20
+ # @param ttl [#to_i] TTL in milliseconds
21
+ def initialize(key, ttl:)
22
+ @key = key.to_s
23
+ @ttl = ttl.to_i
24
+ @drift = @ttl * 0.01 + 2
25
+ @nonce = SecureRandom.uuid
26
+ end
27
+
28
+ # Attempts to acquire lease.
29
+ #
30
+ # @param redis [Redis] Redis client
31
+ # @return [Boolean] whenever lock was acquired or not.
32
+ def acquire(redis)
33
+ deadline = timestamp + @ttl + @drift
34
+ success = LOCK_SCRIPT.eval(redis, {
35
+ :keys => [@key],
36
+ :argv => [@nonce, @ttl]
37
+ })
38
+
39
+ success && timestamp < deadline || false
40
+ end
41
+
42
+ # Release acquired lease.
43
+ #
44
+ # @param redis [Redis] Redis client
45
+ # @return [void]
46
+ def release(redis)
47
+ UNLOCK_SCRIPT.eval(redis, :keys => [@key], :argv => [@nonce])
48
+ end
49
+
50
+ private
51
+
52
+ # Returns monotonic timestamp.
53
+ # @return [Float]
54
+ def timestamp
55
+ Concurrent.monotonic_time
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ local key = KEYS[1]
2
+ local nonce = ARGV[1]
3
+ local ttl = ARGV[2]
4
+
5
+ if redis.call("EXISTS", key) == 0 or redis.call("GET", key) == nonce then
6
+ return redis.call("SET", key, nonce, "PX", ttl)
7
+ end
@@ -0,0 +1,3 @@
1
+ if redis.call("GET", KEYS[1]) == ARGV[1] then
2
+ redis.call("DEL", KEYS[1])
3
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Redis
4
+ module Lockers
5
+ # Gem version.
6
+ VERSION = "1.0.0"
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("../lib", __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require "redis/lockers/version"
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "redis-lockers"
10
+ spec.version = Redis::Lockers::VERSION
11
+ spec.authors = ["Alexey Zapparov"]
12
+ spec.email = ["ixti@member.fsf.org"]
13
+
14
+ spec.summary = "Yet another Redis-based lock manager."
15
+ spec.description = <<~DESCRIPTION
16
+ Simplified implementation of Redlock (distributed lock manger by antirez).
17
+ Right now it works with single Redis node only.
18
+ DESCRIPTION
19
+
20
+ spec.homepage = "https://github.com/ixti/redis-lockers"
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features)/})
24
+ end
25
+
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
29
+ spec.add_runtime_dependency "redis", "~> 4.0"
30
+ spec.add_runtime_dependency "redis-prescription", "~> 1.0"
31
+
32
+ spec.add_development_dependency "bundler", "~> 1.16"
33
+
34
+ spec.required_ruby_version = "~> 2.3"
35
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-lockers
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Alexey Zapparov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-02-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: concurrent-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '4.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '4.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis-prescription
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.16'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.16'
69
+ description: |
70
+ Simplified implementation of Redlock (distributed lock manger by antirez).
71
+ Right now it works with single Redis node only.
72
+ email:
73
+ - ixti@member.fsf.org
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - ".gitignore"
79
+ - ".rspec"
80
+ - ".rubocop.yml"
81
+ - ".travis.yml"
82
+ - Gemfile
83
+ - Guardfile
84
+ - LICENSE.txt
85
+ - README.md
86
+ - Rakefile
87
+ - lib/redis/lockers.rb
88
+ - lib/redis/lockers/lock.rb
89
+ - lib/redis/lockers/scripts/lock.lua
90
+ - lib/redis/lockers/scripts/unlock.lua
91
+ - lib/redis/lockers/version.rb
92
+ - redis-lockers.gemspec
93
+ homepage: https://github.com/ixti/redis-lockers
94
+ licenses: []
95
+ metadata: {}
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '2.3'
105
+ required_rubygems_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project:
112
+ rubygems_version: 2.7.3
113
+ signing_key:
114
+ specification_version: 4
115
+ summary: Yet another Redis-based lock manager.
116
+ test_files: []