atomic_mem_cache_store 0.0.3 → 0.1.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: 77ba4d9c1bc102bcbde344a6d09967c1e39ffbb1
4
- data.tar.gz: 01b0150dca2282c8552ed32e5fc61871e2b075d0
3
+ metadata.gz: 3d0b40d56a1ff47b39cc4c91ecb5e0f20191b3b0
4
+ data.tar.gz: 8468c58414e2629320a7e9be9d92baf085fa1ec0
5
5
  SHA512:
6
- metadata.gz: ed9fa3ba3fdb4ffb7f43f23178043ce7d632213f3b456c8c670095c9b43d30911dbf6e57a7a03151e04363b1d83ae88ee6b13252177548f985d04cfe50ec0597
7
- data.tar.gz: 0424e91427c574aec7d0e75a439ac195ad990f8d1d4b80dcdd4b39174f5995f5245df2aeb7804c0e02c46f553a6482a5ebc73586c738e104ac1ff60a7e686cb4
6
+ metadata.gz: b2fd8b0d3d5ef67121c1291b68e936c9a99693483337b215b7fbe2c5b09f37bf422823d28350f1e1d11a48643a00e7bd02466e32256dc95f99aece25612eb207
7
+ data.tar.gz: 0c1f63889d399a11b9272bcb95a0c384d38b48a62d82e3f0931f70597386bc63fa56c9653fdaf1215deb572863089e04f8ab0f3df3b52251cb083fad7171e560
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 1.9.3
5
+ - 1.9.2
6
+ - 1.8.7
7
+ - ree
8
+ services: memcached
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # AtomicMemCacheStore
1
+ # AtomicMemCacheStore [![Build Status](https://travis-ci.org/nel/atomic_mem_cache_store.png?branch=master)](https://travis-ci.org/nel/atomic_mem_cache_store)
2
2
 
3
3
  ## Why ?
4
4
 
5
5
  Anyone caching slow content on a website with moderate to heavy traffic will
6
- sooner of later be a victim of the [thundering herb issue](http://en.wikipedia.org/wiki/Thundering_herd_problem) also called Dog-pile Effect.
6
+ sooner of later be a victim of the [thundering herd issue](http://en.wikipedia.org/wiki/Thundering_herd_problem) also called Dog-pile Effect.
7
7
 
8
8
  Basically cache invalidation of a high traffic page will trigger several concurrent cache recalculation that could lead to pikes of load and transient slow down of your architecture.
9
9
 
@@ -23,17 +23,27 @@ Install the gem
23
23
 
24
24
  or add it to your Gemfile
25
25
 
26
- gem 'atomic_mem_cache_store'
26
+ gem 'atomic_mem_cache_store'
27
27
 
28
28
  Then use it directly
29
+
30
+ cache = AtomicMemCacheStore.new
31
+ cache.write('key', 'value', :expires_in => 10)
32
+ cache.read('key')
29
33
 
30
- cache = AtomicMemCacheStore.new
31
- cache.write('key', 'value', :expires_in => 10)
32
- cache.read('key')
34
+ Or for Rails add it in your config/environments/<env>.rb
35
+
36
+ config.cache_store = :atomic_mem_cache_store, %w( 127.0.0.1 ), { :namespace => "cache:#{Rails.env}" }
37
+
38
+ If you want to use Dalli instead, do the following:
39
+
40
+ cache = AtomicDalliStore.new
41
+ cache.write('key', 'value', :expires_in => 10)
42
+ cache.read('key')
33
43
 
34
44
  Or for Rails add it in your config/environments/<env>.rb
35
45
 
36
- config.cache_store = :atomic_mem_cache_store, %w( 127.0.0.1 ), { :namespace => "cache:#{Rails.env}" }
46
+ config.cache_store = :atomic_dalli_store, %w( 127.0.0.1 ), { :namespace => "cache:#{Rails.env}" }
37
47
 
38
48
  It supports the same parameters as [ActiveSupport::Cache::MemCacheStore](http://apidock.com/rails/ActiveSupport/Cache/MemCacheStore)
39
49
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.1.0
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.email = ["nel@w3fu.com"]
8
8
  s.homepage = "https://github.com/nel/atomic_mem_cache_store"
9
9
  s.summary = %q{Rails memcached store with atomic expiration}
10
- s.description = %q{Rails memcached store optimized for the thundering herb issue. This limit cache recalculation to a single process while as long as key is not swept by LRU. Drop-in replacement of Rails memcached store.}
10
+ s.description = %q{Rails memcached store optimized for the thundering herd issue. This limit cache recalculation to a single process while as long as key is not swept by LRU. Drop-in replacement of Rails memcached store.}
11
11
 
12
12
  s.rubyforge_project = "atomic_mem_cache_store"
13
13
 
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency "rspec"
20
20
  s.add_development_dependency "rake"
21
21
  s.add_development_dependency "iconv"
22
- s.add_runtime_dependency "activesupport", "~>2.1"
23
- s.add_runtime_dependency "memcache-client"
22
+ s.add_development_dependency "dalli", "~> 1.0.4"
23
+ s.add_development_dependency "memcache-client"
24
+ s.add_runtime_dependency "activesupport", "~> 2.1"
24
25
  end
@@ -0,0 +1,10 @@
1
+ require 'dalli'
2
+ require 'dalli/memcache-client'
3
+ require 'active_support/cache/dalli_store23'
4
+ require 'atomic_store'
5
+
6
+ class AtomicDalliStore < ActiveSupport::Cache::DalliStore
7
+ RAW_ARG = { :raw => true }
8
+
9
+ include AtomicStore
10
+ end
@@ -1,49 +1,8 @@
1
1
  require 'active_support'
2
+ require 'atomic_store'
2
3
 
3
4
  class AtomicMemCacheStore < ActiveSupport::Cache::CompressedMemCacheStore
4
- VERSION = File.read(File.join(File.dirname(__FILE__),'..','VERSION') ).strip
5
- NEWLY_STORED = "STORED\r\n"
5
+ RAW_ARG = true
6
6
 
7
- class << self; attr_accessor :grace_period; end
8
- @grace_period = 90
9
-
10
- def read(key, options = nil)
11
- result = super
12
-
13
- if result.present?
14
- timer_key = timer_key(key)
15
- #check whether the cache is expired
16
- if @data.get(timer_key, true).nil?
17
- #optimistic lock to avoid concurrent recalculation
18
- if @data.add(timer_key, '', self.class.grace_period, true) == NEWLY_STORED
19
- #trigger cache recalculation
20
- return handle_expired_read(key,result)
21
- end
22
- #already recalculated or expirated in another process/thread
23
- end
24
- #key not expired
25
- end
26
- result
27
- end
28
-
29
- def write(key, value, options = nil)
30
- expiry = (options && options[:expires_in]) || 0
31
- #extend write expiration period and reset expiration timer
32
- options[:expires_in] = expiry + 2*self.class.grace_period unless expiry.zero?
33
- @data.set(timer_key(key), '', expiry, true)
34
- super
35
- end
36
-
37
- protected
38
-
39
- #to be overidden for something else than synchronous cache recalculation
40
- def handle_expired_read(key,result)
41
- nil
42
- end
43
-
44
- private
45
-
46
- def timer_key(key)
47
- "tk:#{key}"
48
- end
7
+ include AtomicStore
49
8
  end
@@ -0,0 +1,55 @@
1
+ module AtomicStore
2
+ VERSION = File.read(File.join(File.dirname(__FILE__),'..','VERSION') ).strip
3
+ NEWLY_STORED = "STORED\r\n"
4
+
5
+ module ClassMethods
6
+ attr_accessor :grace_period
7
+ end
8
+
9
+ @grace_period = 90
10
+
11
+ def self.included(base)
12
+ @raw_arg = base::RAW_ARG
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ def read(key, options = nil)
17
+ result = super
18
+
19
+ if result.present?
20
+ timer_key = timer_key(key)
21
+ #check whether the cache is expired
22
+ if @data.get(timer_key, true).nil?
23
+ #optimistic lock to avoid concurrent recalculation
24
+ if @data.add(timer_key, '', self.class.grace_period, @raw_arg) == NEWLY_STORED
25
+ #trigger cache recalculation
26
+ return handle_expired_read(key,result)
27
+ end
28
+ #already recalculated or expirated in another process/thread
29
+ end
30
+ #key not expired
31
+ end
32
+ result
33
+ end
34
+
35
+ def write(key, value, options = nil)
36
+ expiry = (options && options[:expires_in]) || 0
37
+ #extend write expiration period and reset expiration timer
38
+ options[:expires_in] = expiry + 2*self.class.grace_period unless expiry.zero?
39
+ @data.set(timer_key(key), '', expiry, @raw_arg)
40
+ super
41
+ end
42
+
43
+ protected
44
+
45
+ #to be overidden for something else than synchronous cache recalculation
46
+ def handle_expired_read(key,result)
47
+ nil
48
+ end
49
+
50
+ private
51
+
52
+ def timer_key(key)
53
+ "tk:#{key}"
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe AtomicDalliStore do
4
+ it_behaves_like 'an atomic store'
5
+ end
@@ -1,83 +1,5 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe AtomicMemCacheStore do
4
- before(:all) do
5
- begin
6
- @store = AtomicMemCacheStore.new('127.0.0.1', :namespace => "spec-atomic")
7
- @store.read('test')
8
- rescue MemCache::MemCacheError
9
- puts "You need a real memcache server to execute the specs, you can run those test on production server, this won't flush your memcache"
10
- end
11
- end
12
-
13
- before(:each) do
14
- @seed = "#{Time.now.to_i}#{rand(1000000000000000000000000)}"
15
- AtomicMemCacheStore.grace_period = 90
16
- end
17
-
18
- def prefix_key(value)
19
- "#{@seed}#{value}"
20
- end
21
-
22
- describe "with expiry" do
23
- it "returns nil when key has not been set" do
24
- @store.read(prefix_key('unknown')).should be_nil
25
- end
26
-
27
- it "returns value when key has been set and is not expired" do
28
- key = prefix_key('not-expired')
29
- @store.write(key, true, :expires_in => 10)
30
- @store.read(key).should be
31
- end
32
-
33
- it "returns new value when key is rewriten" do
34
- key = prefix_key('not-expired')
35
- @store.write(key, 1, :expires_in => 10)
36
- @store.write(key, 2, :expires_in => 10)
37
- @store.read(key).should be 2
38
- end
39
-
40
- it "returns nil once when key is expired, and then the old value" do
41
- key = prefix_key('expired')
42
-
43
- @store.write(key, 1, :expires_in => 1)
44
- sleep 2
45
- @store.read(key).should be_nil
46
- @store.read(key).should be 1
47
- @store.read(key).should be 1
48
- end
49
-
50
- it "returns nil when 2 times the grace period is passed" do
51
- AtomicMemCacheStore.grace_period = 1
52
- key = prefix_key('expired')
53
-
54
- @store.write(key, 1, :expires_in => 1)
55
- sleep 2
56
- @store.read(key).should be_nil
57
- @store.read(key).should be 1
58
- sleep 2
59
- @store.read(key).should be_nil
60
- end
61
- end
62
-
63
- describe "without expiry" do
64
- it "returns nil when key has not been set" do
65
- @store.read(prefix_key('unknown')).should be_nil
66
- end
67
-
68
- it "returns value when key is set" do
69
- key = prefix_key('key-without-expiry')
70
-
71
- @store.write(key, 1)
72
- @store.read(key).should be 1
73
- end
74
-
75
- it "returns new value when key is rewriten" do
76
- key = prefix_key('key-without-expiry')
77
-
78
- @store.write(key, 1)
79
- @store.write(key, 2)
80
- @store.read(key).should be 2
81
- end
82
- end
4
+ it_behaves_like 'an atomic store'
83
5
  end
@@ -6,3 +6,5 @@ rescue LoadError
6
6
  require 'rspec'
7
7
  end
8
8
  require File.expand_path(File.dirname(__FILE__) + '/../lib/atomic_mem_cache_store')
9
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/atomic_dalli_store')
10
+ Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
@@ -0,0 +1,83 @@
1
+ shared_examples "an atomic store" do
2
+
3
+ before(:all) do
4
+ begin
5
+ @store = described_class.new('127.0.0.1', :namespace => "spec-atomic")
6
+ @store.read('test')
7
+ rescue MemCache::MemCacheError
8
+ puts "You need a real memcache server to execute the specs, you can run those test on production server, this won't flush your memcache"
9
+ end
10
+ end
11
+
12
+ before(:each) do
13
+ @seed = "#{Time.now.to_i}#{rand(1000000000000000000000000)}"
14
+ described_class.grace_period = 90
15
+ end
16
+
17
+ def prefix_key(value)
18
+ "#{@seed}#{value}"
19
+ end
20
+
21
+ describe "with expiry" do
22
+ it "returns nil when key has not been set" do
23
+ @store.read(prefix_key('unknown')).should be_nil
24
+ end
25
+
26
+ it "returns value when key has been set and is not expired" do
27
+ key = prefix_key('not-expired')
28
+ @store.write(key, true, :expires_in => 10)
29
+ @store.read(key).should be
30
+ end
31
+
32
+ it "returns new value when key is rewriten" do
33
+ key = prefix_key('not-expired')
34
+ @store.write(key, 1, :expires_in => 10)
35
+ @store.write(key, 2, :expires_in => 10)
36
+ @store.read(key).should be 2
37
+ end
38
+
39
+ it "returns nil once when key is expired, and then the old value" do
40
+ key = prefix_key('expired')
41
+
42
+ @store.write(key, 1, :expires_in => 1)
43
+ sleep 2
44
+ @store.read(key).should be_nil
45
+ @store.read(key).should be 1
46
+ @store.read(key).should be 1
47
+ end
48
+
49
+ it "returns nil when 2 times the grace period is passed" do
50
+ described_class.grace_period = 1
51
+ key = prefix_key('expired')
52
+
53
+ @store.write(key, 1, :expires_in => 1)
54
+ sleep 2
55
+ @store.read(key).should be_nil
56
+ @store.read(key).should be 1
57
+ sleep 2
58
+ @store.read(key).should be_nil
59
+ end
60
+ end
61
+
62
+ describe "without expiry" do
63
+ it "returns nil when key has not been set" do
64
+ @store.read(prefix_key('unknown')).should be_nil
65
+ end
66
+
67
+ it "returns value when key is set" do
68
+ key = prefix_key('key-without-expiry')
69
+
70
+ @store.write(key, 1)
71
+ @store.read(key).should be 1
72
+ end
73
+
74
+ it "returns new value when key is rewriten" do
75
+ key = prefix_key('key-without-expiry')
76
+
77
+ @store.write(key, 1)
78
+ @store.write(key, 2)
79
+ @store.read(key).should be 2
80
+ end
81
+ end
82
+
83
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: atomic_mem_cache_store
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Renaud (Nel) Morvan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-29 00:00:00.000000000 Z
11
+ date: 2013-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: activesupport
56
+ name: dalli
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: '2.1'
62
- type: :runtime
61
+ version: 1.0.4
62
+ type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ~>
67
67
  - !ruby/object:Gem::Version
68
- version: '2.1'
68
+ version: 1.0.4
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: memcache-client
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -73,14 +73,28 @@ dependencies:
73
73
  - - '>='
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
- type: :runtime
76
+ type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- description: Rails memcached store optimized for the thundering herb issue. This limit
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '2.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '2.1'
97
+ description: Rails memcached store optimized for the thundering herd issue. This limit
84
98
  cache recalculation to a single process while as long as key is not swept by LRU.
85
99
  Drop-in replacement of Rails memcached store.
86
100
  email:
@@ -90,14 +104,19 @@ extensions: []
90
104
  extra_rdoc_files: []
91
105
  files:
92
106
  - .gitignore
107
+ - .travis.yml
93
108
  - Gemfile
94
109
  - README.md
95
110
  - Rakefile
96
111
  - VERSION
97
112
  - atomic_mem_cache_store.gemspec
113
+ - lib/atomic_dalli_store.rb
98
114
  - lib/atomic_mem_cache_store.rb
115
+ - lib/atomic_store.rb
116
+ - spec/lib/atomic_dalli_store_spec.rb
99
117
  - spec/lib/atomic_mem_cache_store_spec.rb
100
118
  - spec/spec_helper.rb
119
+ - spec/support/shared_atomic_store_spec.rb
101
120
  homepage: https://github.com/nel/atomic_mem_cache_store
102
121
  licenses: []
103
122
  metadata: {}
@@ -117,10 +136,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
136
  version: '0'
118
137
  requirements: []
119
138
  rubyforge_project: atomic_mem_cache_store
120
- rubygems_version: 2.0.3
139
+ rubygems_version: 2.1.2
121
140
  signing_key:
122
141
  specification_version: 4
123
142
  summary: Rails memcached store with atomic expiration
124
143
  test_files:
144
+ - spec/lib/atomic_dalli_store_spec.rb
125
145
  - spec/lib/atomic_mem_cache_store_spec.rb
126
146
  - spec/spec_helper.rb
147
+ - spec/support/shared_atomic_store_spec.rb