redis-activesupport-with-cas 0.0.1

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
+ SHA1:
3
+ metadata.gz: 4abb278597b98f98b1c3cd0a48e5ab19483c7e49
4
+ data.tar.gz: 93388de649c88393897dd81f98a8dbfd6ac8616d
5
+ SHA512:
6
+ metadata.gz: a718347768ba3d70de2b1fd1d621aaaee364bc730ab1079bc450437161bba2f2b889d865c7ba49187e3a11ff73c90cc5c4d8300cb41255c7e80a8a81e25ae554
7
+ data.tar.gz: 80291ac50fa93373fe0701fc9f432da8b22776a90f3fee3293fca476da71b3f648379edbb2f5260e1070b708a4ad2c0815cd450c48342241d3e9c0c2dd9f5746
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ Gemfile.lock
2
+ *.gem
3
+ tmp/
4
+ stdout
5
+ .idea/
6
+ .ruby-version
7
+ .ruby-gemset
8
+ .yardoc/
9
+ doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'i18n'
data/MIT-LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 Rajko Albrecht
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 all
13
+ 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 THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # Redis store with CAS for ActiveSupport
2
+
3
+ __`redis-activesupport-with-cas`__ provides a cache for __ActiveSupport__ including 'Compare and Swap' methods.
4
+
5
+ It is based on [redis-store](https://github.com/redis-store/redis-store) and may used with [Identity Cache](https://github.com/Shopify/identity_cache).
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ # Gemfile
11
+ gem 'redis-activesupport-with-cas'
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```ruby
17
+ ActiveSupport::Cache.lookup_store :redis_store_with_cas # { ... optional configuration ... }
18
+ ```
19
+
20
+ ## Running tests
21
+
22
+ ```shell
23
+ gem install bundler
24
+ git clone https://git.alwin-it.de/ruby-redis/redis-activesupport-with-cas.git
25
+ cd redis-activesupport-with-cas
26
+ bundle install
27
+ bundle exec rake
28
+ ```
29
+
30
+ ## Copyright
31
+
32
+ 2017 Rajko Albrecht, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'bundler/setup'
2
+ require 'rake'
3
+ require 'bundler/gem_tasks'
4
+ require 'redis-store/testing/tasks'
data/irb.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "irb"
2
+ require 'active_support/cache/redis_store_with_cas'
3
+
4
+ IRB.start(__FILE__)
@@ -0,0 +1,80 @@
1
+ require 'active_support'
2
+ require 'redis-store-with-cas'
3
+ require 'active_support/cache/redis_store'
4
+
5
+ module ActiveSupport
6
+ module Cache
7
+ module RedisStoreCas
8
+
9
+ attr_accessor :read_only
10
+
11
+ def cas name,options=nil
12
+ options = merged_options(options)
13
+ key = normalize_key(name, options)
14
+ instrument(:cas, name, options) do
15
+ ttl = cas_expiration options
16
+ @data.cas(key,ttl) do |entry|
17
+ value = yield entry.value
18
+ break true if read_only
19
+ options[:raw].present? ? value : Entry.new(value, options)
20
+ end
21
+ end
22
+ end
23
+
24
+ def cas_multi(*names)
25
+ options = names.extract_options!
26
+ return if names.empty?
27
+
28
+ options = merged_options(options)
29
+ keys_to_names = Hash[names.map { |name| [normalize_key(name, options), name] }]
30
+
31
+ instrument(:cas_multi, names, options) do
32
+ @data.cas_multi(*(keys_to_names.keys), {:expires_in => cas_expiration(options)}) do |raw_values|
33
+ values = {}
34
+ raw_values.each do |key, entry|
35
+ values[keys_to_names[key]] = entry.value unless entry.expired?
36
+ end
37
+ values = yield values
38
+ break true if read_only
39
+ mapped_values = values.map do |name,value|
40
+ [normalize_key(name, options),options[:raw].present? ? value : Entry.new(value, options)]
41
+ end
42
+ Hash[mapped_values]
43
+ end
44
+ true
45
+ end
46
+
47
+ end
48
+
49
+ private
50
+
51
+ def cas_expiration(options)
52
+ if options[:expires_in].present? && options[:race_condition_ttl].present? && options[:raw].blank?
53
+ options[:expires_in].to_f + options[:race_condition_ttl].to_f
54
+ else
55
+ nil
56
+ end
57
+ end
58
+
59
+ end
60
+
61
+ class RedisStoreWithCas < RedisStore
62
+
63
+ def initialize(*adresses)
64
+ super adresses
65
+ check_and_extend_cas
66
+ end
67
+
68
+ def candocas?
69
+ @data.is_a?(Redis::Store) && @data.respond_to?(:cas)
70
+ end
71
+
72
+ private
73
+
74
+ def check_and_extend_cas
75
+ extend RedisStoreCas if candocas?
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,3 @@
1
+ require 'redis-store-with-cas'
2
+ require 'active_support'
3
+ require 'active_support/cache/redis_store_with_cas'
@@ -0,0 +1,35 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'redis-activesupport-with-cas'
6
+ s.version = '0.0.1'
7
+ s.authors = ['Rajko Albrecht']
8
+ s.email = ['ral@alwins-world.de']
9
+ s.homepage = 'https://git.alwin-it.de/alwin/redis-activesupport-with-cas'
10
+ s.summary = %q{Extend redis activesupport for Ruby frameworks with cas}
11
+ s.description = %q{Extend the redis activesupport with CAS (compare-and-swap) functionality for better cache integrity support}
12
+
13
+ s.rubyforge_project = 'redis-activesupport-with-cas'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+ s.license = 'MIT'
20
+
21
+ s.add_dependency 'redis', '>= 2.2', '< 4'
22
+ s.add_dependency 'redis-store', '>= 1.1.0', '< 2'
23
+ s.add_dependency 'redis-activesupport'
24
+ s.add_dependency 'redis-store-with-cas'
25
+
26
+ s.add_development_dependency 'rake', '~> 10'
27
+ s.add_development_dependency 'bundler', '~> 1.3'
28
+ s.add_development_dependency 'mocha', '~> 0.14.0'
29
+ s.add_development_dependency 'minitest', '~> 5'
30
+ s.add_development_dependency 'git', '~> 1.2'
31
+ s.add_development_dependency 'pry-nav', '~> 0.2.4'
32
+ s.add_development_dependency 'pry', '~> 0.10.4'
33
+ s.add_development_dependency 'redis-store-testing'
34
+
35
+ end
@@ -0,0 +1,120 @@
1
+ require 'active_support/test_helper'
2
+ require 'ostruct'
3
+
4
+ describe "ActiveSupport::Cache::RedisStoreWithCas" do
5
+
6
+ def setup
7
+ @store = ActiveSupport::Cache::RedisStoreWithCas.new "redis://127.0.0.1:6379/5/cachetest"
8
+ @dstore = ActiveSupport::Cache::RedisStoreWithCas.new "redis://127.0.0.1:6379/5", "redis://127.0.0.1:6379/6"
9
+
10
+ @rabbit = OpenStruct.new :name => "bunny"
11
+ @white_rabbit = OpenStruct.new :color => "white"
12
+ @black_rabbit = OpenStruct.new :color => 'black'
13
+
14
+ end
15
+
16
+ def teardown
17
+ @store.instance_variable_get("@data").flushdb
18
+ @dstore.instance_variable_get("@data").flushdb
19
+ end
20
+
21
+ describe "Including into cache" do
22
+ it "should not done with distributed store" do
23
+ refute @dstore.candocas?
24
+ assert @store.candocas?
25
+ end
26
+ end
27
+
28
+ describe "Single cas " do
29
+ it "should not swap missing key" do
30
+ refute @store.cas('rabbit') { |_value| flunk }
31
+ end
32
+
33
+ it "should correct swap value" do
34
+ @store.write "rabbit", @rabbit
35
+ assert(@store.cas('rabbit') do |value|
36
+ assert_equal @rabbit, value
37
+ @white_rabbit
38
+ end)
39
+ @store.read("rabbit").must_equal(@white_rabbit)
40
+ end
41
+
42
+ it "should not swap if value changes" do
43
+ @store.write('rabbit', @rabbit)
44
+ refute @store.cas('rabbit') { |_value|
45
+ @store.write('rabbit', @black_rabbit)
46
+ @white_rabbit
47
+ }
48
+ @store.read("rabbit").must_equal(@black_rabbit)
49
+ end
50
+
51
+ describe "Test Multi cas" do
52
+ it "should fail with empty names" do
53
+ refute @store.cas_multi { |_hash| flunk }
54
+ end
55
+
56
+ it "should set new values" do
57
+ @store.write('rabbit', @white_rabbit)
58
+ @store.write('hare', @black_rabbit)
59
+ assert_equal true, (@store.cas_multi('rabbit', 'hare') do |hash|
60
+ assert_equal({ "rabbit" => @white_rabbit, 'hare' => @black_rabbit }, hash)
61
+ { "rabbit" => @black_rabbit, 'hare' => @white_rabbit }
62
+ end)
63
+ assert_equal({ "rabbit" => @black_rabbit, 'hare' => @white_rabbit }, @store.read_multi('rabbit', 'hare'))
64
+ end
65
+
66
+ it "should set a ttl " do
67
+ @store.write('rabbit', @white_rabbit)
68
+ @store.write('hare', @black_rabbit)
69
+ @store.cas_multi('rabbit','hare',{:expires_in => 600,:race_condition_ttl => 1}) do |hash|
70
+ { "rabbit" => @black_rabbit, "hare" => @white_rabbit}
71
+ end
72
+ values = @store.read_multi('rabbit', 'hare')
73
+ assert_equal({ "rabbit" => @black_rabbit, 'hare' => @white_rabbit },values)
74
+ re = @store.instance_variable_get('@data')
75
+ assert re.ttl('rabbit') > 0
76
+ assert re.ttl('hare') > 0
77
+
78
+ end
79
+
80
+ it "should not send values for not existing keys" do
81
+ assert(@store.cas_multi('not_exist') do |hash|
82
+ assert hash.empty?
83
+ {}
84
+ end)
85
+ end
86
+
87
+ it "should not write keys not in parameter" do
88
+ @store.write('foo', 'baz')
89
+ assert @store.cas_multi('foo') { |_hash| { 'fu' => 'baz' } }
90
+ assert_nil @store.read('fu')
91
+ assert_equal 'baz', @store.read('foo')
92
+ end
93
+
94
+ def test_cas_multi_with_partial_update
95
+ @store.write('foo', 'bar')
96
+ @store.write('fud', 'biz')
97
+ assert(@store.cas_multi('foo', 'fud') do |hash|
98
+ assert_equal({ "foo" => "bar", "fud" => "biz" }, hash)
99
+
100
+ { "foo" => "baz" }
101
+ end)
102
+ assert_equal({ "foo" => "baz", "fud" => "biz" }, @store.read_multi('foo', 'fud'))
103
+ end
104
+
105
+ def test_cas_multi_with_partial_conflict
106
+ @store.write('foo', 'bar')
107
+ @store.write('fud', 'biz')
108
+ result = @store.cas_multi('foo', 'fud') do |hash|
109
+ assert_equal({ "foo" => "bar", "fud" => "biz" }, hash)
110
+ @store.write('foo', 'bad')
111
+ { "foo" => "baz", "fud" => "buz" }
112
+ end
113
+ assert result
114
+ assert_equal({ "foo" => "bad", "fud" => "buz" }, @store.read_multi('foo', 'fud'))
115
+ end
116
+
117
+ end
118
+ end
119
+
120
+ end
@@ -0,0 +1,7 @@
1
+ require 'bundler/setup'
2
+ require 'minitest/autorun'
3
+ require 'mocha/setup'
4
+ require 'active_support'
5
+ require 'active_support/cache/redis_store_with_cas'
6
+
7
+ puts "Testing against ActiveSupport v.#{ActiveSupport::VERSION::STRING}"
metadata ADDED
@@ -0,0 +1,238 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-activesupport-with-cas
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rajko Albrecht
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-07-26 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: '2.2'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '4'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '2.2'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '4'
33
+ - !ruby/object:Gem::Dependency
34
+ name: redis-store
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.1.0
40
+ - - "<"
41
+ - !ruby/object:Gem::Version
42
+ version: '2'
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.1.0
50
+ - - "<"
51
+ - !ruby/object:Gem::Version
52
+ version: '2'
53
+ - !ruby/object:Gem::Dependency
54
+ name: redis-activesupport
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ type: :runtime
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ - !ruby/object:Gem::Dependency
68
+ name: redis-store-with-cas
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ type: :runtime
75
+ prerelease: false
76
+ version_requirements: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ - !ruby/object:Gem::Dependency
82
+ name: rake
83
+ requirement: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - "~>"
86
+ - !ruby/object:Gem::Version
87
+ version: '10'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - "~>"
93
+ - !ruby/object:Gem::Version
94
+ version: '10'
95
+ - !ruby/object:Gem::Dependency
96
+ name: bundler
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '1.3'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - "~>"
107
+ - !ruby/object:Gem::Version
108
+ version: '1.3'
109
+ - !ruby/object:Gem::Dependency
110
+ name: mocha
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - "~>"
114
+ - !ruby/object:Gem::Version
115
+ version: 0.14.0
116
+ type: :development
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - "~>"
121
+ - !ruby/object:Gem::Version
122
+ version: 0.14.0
123
+ - !ruby/object:Gem::Dependency
124
+ name: minitest
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '5'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '5'
137
+ - !ruby/object:Gem::Dependency
138
+ name: git
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '1.2'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1.2'
151
+ - !ruby/object:Gem::Dependency
152
+ name: pry-nav
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: 0.2.4
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: 0.2.4
165
+ - !ruby/object:Gem::Dependency
166
+ name: pry
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: 0.10.4
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - "~>"
177
+ - !ruby/object:Gem::Version
178
+ version: 0.10.4
179
+ - !ruby/object:Gem::Dependency
180
+ name: redis-store-testing
181
+ requirement: !ruby/object:Gem::Requirement
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ type: :development
187
+ prerelease: false
188
+ version_requirements: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ description: Extend the redis activesupport with CAS (compare-and-swap) functionality
194
+ for better cache integrity support
195
+ email:
196
+ - ral@alwins-world.de
197
+ executables: []
198
+ extensions: []
199
+ extra_rdoc_files: []
200
+ files:
201
+ - ".gitignore"
202
+ - Gemfile
203
+ - MIT-LICENSE
204
+ - README.md
205
+ - Rakefile
206
+ - irb.rb
207
+ - lib/active_support/cache/redis_store_with_cas.rb
208
+ - lib/active_support/redis-activesupport-with-cas.rb
209
+ - redis-activesupport-with-cas.gemspec
210
+ - test/active_support/cache/redis_store_with_cas_test.rb
211
+ - test/active_support/test_helper.rb
212
+ homepage: https://git.alwin-it.de/alwin/redis-activesupport-with-cas
213
+ licenses:
214
+ - MIT
215
+ metadata: {}
216
+ post_install_message:
217
+ rdoc_options: []
218
+ require_paths:
219
+ - lib
220
+ required_ruby_version: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ required_rubygems_version: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '0'
230
+ requirements: []
231
+ rubyforge_project: redis-activesupport-with-cas
232
+ rubygems_version: 2.6.11
233
+ signing_key:
234
+ specification_version: 4
235
+ summary: Extend redis activesupport for Ruby frameworks with cas
236
+ test_files:
237
+ - test/active_support/cache/redis_store_with_cas_test.rb
238
+ - test/active_support/test_helper.rb