redis-lockers 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []