redis-helper 1.1.0 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9158b2e2f6026d24b0861d6a257f3edb475c1d03
4
- data.tar.gz: 2646c37f2ef7d789b4b9714127298d132b78809f
3
+ metadata.gz: c2ca31e77c5ef65597d7c6a3d85645d01b82bf27
4
+ data.tar.gz: 24d3c3c8890f4838dacfdbb0136b1e4e4a22fafc
5
5
  SHA512:
6
- metadata.gz: b8ba219bd493173ff38f407ac4fff7c41b865dbcf5cf3b5b33eaca06b428da05aa68909ecd605245ca1f8a2e4a98270af9a5c9777c8a5041cf2d5f9ab414e098
7
- data.tar.gz: 5e8f455b382c4c332c26f64dfa1c7cceafc8b17fdc4d3e5bb26ad4341d2983b06a7e29d90794cd73d610e8a7c2519d42877700766f10aa937ea271e61174ff57
6
+ metadata.gz: 0af9c99539da831f765713ab402f0c5b7a21623a7cec03703c681f4ae28362f0e9e6ac691aa335918e37f4e816312bdd28a4cf772f0dbb221cc43443af86c1cd
7
+ data.tar.gz: 0a6a4a5de54c157ab64c9a1aca82ab8b5de5e8dbaa1474d94983b24723f0b6ae8951356848b8a157880459f03cda260fca2243a00df342fb4c2cbb57c16f3946
data/.travis.yml CHANGED
@@ -1,5 +1,6 @@
1
- sudo: false
2
1
  language: ruby
3
2
  rvm:
4
3
  - 2.3.1
5
- before_install: gem install bundler -v 1.12.5
4
+ services:
5
+ - redis-server
6
+ cache: bundler
data/README.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Redis::Helper
2
2
 
3
+ [![Gem](https://img.shields.io/gem/v/redis-helper.svg)](https://rubygems.org/gems/redis-helper)
4
+ [![Gem](https://img.shields.io/gem/dtv/redis-helper.svg)](https://rubygems.org/gems/redis-helper)
5
+ [![Gemnasium](https://gemnasium.com/Narazaka/redis-helper.svg)](https://gemnasium.com/Narazaka/redis-helper)
6
+ [![Inch CI](http://inch-ci.org/github/Narazaka/redis-helper.svg)](http://inch-ci.org/github/Narazaka/redis-helper)
7
+ [![Travis Build Status](https://travis-ci.org/Narazaka/redis-helper.svg)](https://travis-ci.org/Narazaka/redis-helper)
8
+ [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/Narazaka/redis-helper?svg=true)](https://ci.appveyor.com/project/Narazaka/redis-helper)
9
+
3
10
  Redisを扱うクラスで利用するモジュール
4
11
 
5
12
  Redis::Objectsがmulti使えないとかアレっていう [@i2bskn](https://github.com/i2bskn) さん等の想いのカケラ。
data/appveyor.yml ADDED
@@ -0,0 +1,31 @@
1
+ version: "{build}"
2
+ branches:
3
+ except:
4
+ - gh-pages
5
+ install:
6
+ # redis
7
+ - nuget install redis-64 -excludeversion
8
+ - redis-64\tools\redis-server.exe --service-install
9
+ - redis-64\tools\redis-server.exe --service-start
10
+ # ruby
11
+ - set RUBY_HOME=C:\Ruby%ruby_version%
12
+ - set PATH=%RUBY_HOME%\bin;%PATH%
13
+ - |
14
+ %RUBY_HOME%\DevKit\devkitvars.bat
15
+ - ruby --version
16
+ - gem --version
17
+ - gem update --system
18
+ - gem install bundler --no-document
19
+ - bundle --version
20
+ - bundle config --local path vendor/bundle
21
+ - bundle install
22
+ test_script:
23
+ - bundle exec rake spec
24
+ build: off
25
+ deploy: off
26
+ environment:
27
+ matrix:
28
+ - ruby_version: 23
29
+ - ruby_version: 23-x64
30
+ cache:
31
+ - vendor/bundle
data/bin/console CHANGED
@@ -2,13 +2,6 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "redis/helper"
5
+ require "pry"
5
6
 
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start
7
+ Pry.start
data/lib/redis/helper.rb CHANGED
@@ -2,6 +2,7 @@ require "redis"
2
2
  require "active_support"
3
3
  require "active_support/core_ext/object/blank"
4
4
  require "active_support/core_ext/numeric/time"
5
+ require "redis/helper/lock"
5
6
  require "redis/helper/version"
6
7
 
7
8
  # Redisを扱うクラスで利用するモジュール
@@ -30,11 +31,14 @@ class Redis
30
31
  module Helper
31
32
  # 正しくない固有キー(固有キー値が空?)
32
33
  class UnknownUniqueValue < StandardError; end
34
+ class LockTimeout < StandardError; end
33
35
 
34
36
  # デフォルトの固有キー名
35
37
  DEFAULT_UNIQUE_ATTR_NAME = :id
36
38
  # redisキーの区切り文字
37
39
  REDIS_KEY_DELIMITER = ":".freeze
40
+ # ロックを取得に利用する接尾辞
41
+ LOCK_POSTFIX = "lock".freeze
38
42
 
39
43
  def self.included(klass)
40
44
  klass.extend ClassMethods
@@ -57,6 +61,14 @@ class Redis
57
61
  end
58
62
  end
59
63
  end
64
+
65
+ # 特定のkeyをbaseにしたロックをかけてブロック内の処理を実行
66
+ # @param [String] base_key ロックを取得するリソースのkey
67
+ # @yield ロック中に実行する処理のブロック
68
+ def lock(base_key, &block)
69
+ lock_key = [base_key, LOCK_POSTFIX].compact.join(REDIS_KEY_DELIMITER)
70
+ ::Redis::Helper::Lock.new(redis, lock_key).lock(&block)
71
+ end
60
72
  end
61
73
 
62
74
  # instance固有のkeyとattr_nameからkeyを生成する
@@ -100,5 +112,16 @@ class Redis
100
112
  def redis
101
113
  self.class.redis
102
114
  end
115
+
116
+ # 特定のkeyをbaseにしたロックをかけてブロック内の処理を実行
117
+ # @example
118
+ # lock(attr_key(:foo)) {
119
+ # # some processing
120
+ # }
121
+ # @param [String] base_key ロックを取得するリソースのkey
122
+ # @yield ロック中に実行する処理のブロック
123
+ def lock(base_key, &block)
124
+ self.class.lock(base_key, &block)
125
+ end
103
126
  end
104
127
  end
@@ -0,0 +1,80 @@
1
+ class Redis
2
+ module Helper
3
+ class Lock
4
+ # ロック取得のタイムアウト(sec)
5
+ DEFAULT_TIMEOUT = 5
6
+
7
+ def initialize(redis, lock_key, options = {})
8
+ @redis = redis
9
+ @lock_key = lock_key
10
+ @options = options
11
+ @locked_by_self = false
12
+ end
13
+
14
+ # ロックをかけてブロック内の処理を実行
15
+ # @yield ロック中に実行する処理のブロック
16
+ def lock
17
+ raise ArgumentError unless block_given?
18
+ if Thread.current[@lock_key]
19
+ yield
20
+ else
21
+ begin
22
+ Thread.current[@lock_key] = true
23
+ try_lock!(Time.now.to_f)
24
+ yield
25
+ ensure
26
+ unlock
27
+ Thread.current[@lock_key] = nil
28
+ end
29
+ end
30
+ end
31
+
32
+ # ロックを開放
33
+ # (自身でかけたロックの場合のみ開放する)
34
+ def unlock
35
+ if @locked_by_self
36
+ @redis.del(@lock_key)
37
+ @locked_by_self = false
38
+ end
39
+ end
40
+
41
+ private
42
+ # ロックを取得
43
+ # @param [Float] start ロック取得開始時間のUNIXタイムスタンプ
44
+ # @yield ロック中に実行する処理のブロック
45
+ # @raise [Redis::Helper::LockTimeout] ロックの取得に失敗した
46
+ def try_lock!(start)
47
+ loop do
48
+ if @redis.setnx(@lock_key, expiration)
49
+ @locked_by_self = true
50
+ break
51
+ end
52
+
53
+ current = @redis.get(@lock_key).to_f
54
+ if current < Time.now.to_f
55
+ old = @redis.getset(@lock_key, expiration).to_f
56
+ if old < Time.now.to_f
57
+ @locked_by_self = true
58
+ break
59
+ end
60
+ end
61
+
62
+ Kernel.sleep(0.1)
63
+ raise ::Redis::Helper::LockTimeout if (Time.now.to_f - start) > timeout
64
+ end
65
+ end
66
+
67
+ # ロックの有効期限
68
+ # @return [Float] 有効期限(UNIXタイムスタンプ)
69
+ def expiration
70
+ (Time.now + timeout).to_f
71
+ end
72
+
73
+ # ロック取得と取得したロックがタイムアウトするまでの時間
74
+ # @return [Float] タイムアウト時間(sec)
75
+ def timeout
76
+ @timeout ||= (@options[:timeout] || DEFAULT_TIMEOUT).to_f
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,6 +1,6 @@
1
1
  class Redis
2
2
  module Helper
3
3
  # バージョン
4
- VERSION = "1.1.0".freeze
4
+ VERSION = "1.2.0".freeze
5
5
  end
6
6
  end
data/redis-helper.gemspec CHANGED
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "yard"
31
31
  spec.add_development_dependency "simplecov"
32
32
  spec.add_development_dependency "onkcop"
33
+ spec.add_development_dependency "pry"
33
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Narazaka
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-17 00:00:00.000000000 Z
11
+ date: 2017-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  description:
126
140
  email:
127
141
  - info@narazaka.net
@@ -137,9 +151,11 @@ files:
137
151
  - LICENSE.txt
138
152
  - README.md
139
153
  - Rakefile
154
+ - appveyor.yml
140
155
  - bin/console
141
156
  - bin/setup
142
157
  - lib/redis/helper.rb
158
+ - lib/redis/helper/lock.rb
143
159
  - lib/redis/helper/version.rb
144
160
  - redis-helper.gemspec
145
161
  homepage: https://github.com/Narazaka/redis-helper