memcached_snappy_store 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in memcached_snappy_store.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Camilo Lopez
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ # MemcachedSnappyStore
2
+
3
+ ActiveSupport cache store that adds snappy compression at the cost of making the ```incr, decr, add``` operations unavailable.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'memcached_snappy_store'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install memcached_snappy_store
18
+
19
+ ## Usage
20
+
21
+ In your environment file:
22
+
23
+ ```ruby
24
+
25
+ config.cache_store = :memcached_snappy_store,
26
+ Memcached::Rails.new(:servers => ['memcached1.foo.com', 'memcached2.foo.com'])
27
+
28
+ ```
29
+
30
+ ## Contributing
31
+
32
+ 1. Fork it
33
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
34
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
35
+ 4. Push to the branch (`git push origin my-new-feature`)
36
+ 5. Create new Pull Request
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.libs << "lib/**/*"
8
+ t.test_files = FileList['test/test*.rb']
9
+ t.verbose = true
10
+ end
11
+
12
+ task :default => [:test]
@@ -0,0 +1,48 @@
1
+ module ActiveSupport
2
+ module Cache
3
+ class MemcachedSnappyStore < Cache::MemCacheStore
4
+ class UnsupportedOperation < StandardError; end
5
+
6
+ def increment(*args)
7
+ raise UnsupportedOperation.new("increment is not supported by: #{self.class.name}")
8
+ end
9
+
10
+ def decrement(*args)
11
+ raise UnsupportedOperation.new("decrement is not supported by: #{self.class.name}")
12
+ end
13
+
14
+ protected
15
+
16
+ def write_entry(key, entry, options)
17
+ # normally unless_exist would make this method use add, add will not make sense on compressed entries
18
+ raise UnsupportedOperation.new("unless_exist would try to use the unsupported add method") if options && options[:unless_exist]
19
+
20
+ value = options[:raw] ? entry.value.to_s : entry
21
+ expires_in = options[:expires_in].to_i
22
+ if expires_in > 0 && !options[:raw]
23
+ # Set the memcache expire a few minutes in the future to support race condition ttls on read
24
+ expires_in += 5.minutes
25
+ end
26
+
27
+ serialized_value = Marshal.dump(value)
28
+ serialized_compressed_value = Snappy.deflate(serialized_value)
29
+
30
+ response = @data.set(escape_key(key), serialized_compressed_value, expires_in, true)
31
+ response == Response::STORED
32
+ rescue MemCache::MemCacheError => e
33
+ logger.error("MemCacheError (#{e}): #{e.message}") if logger
34
+ false
35
+ end
36
+
37
+
38
+ def deserialize_entry_with_snappy(*args)
39
+ compressed_value = args.first
40
+ decompressed_value = compressed_value.nil? ? compressed_value : Snappy.inflate(compressed_value)
41
+ args[0] = decompressed_value
42
+ deserialize_entry_without_snappy(*args)
43
+ end
44
+
45
+ alias_method_chain :deserialize_entry, :snappy
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,2 @@
1
+ require "active_support/cache"
2
+
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.authors = ["Camilo Lopez"]
5
+ gem.email = ["camilo@camilolopez.com"]
6
+ gem.description = %q{Memcached store that will compress all entries using snappy}
7
+ gem.summary = %q{Memcached store that will compress all entries using snappy}
8
+ gem.homepage = ""
9
+
10
+ gem.files = `git ls-files`.split($\)
11
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "memcached_snappy_store"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = '0.0.1'
16
+ gem.add_runtime_dependency "activesupport", "~>3.2.12"
17
+ gem.add_runtime_dependency "snappy", "0.0.4"
18
+ gem.add_development_dependency "i18n"
19
+ gem.add_development_dependency "rake"
20
+ gem.add_development_dependency "minitest"
21
+ gem.add_development_dependency "mocha"
22
+ gem.add_development_dependency "timecop"
23
+ gem.add_development_dependency "memcache"
24
+ gem.add_development_dependency "memcache-client"
25
+ end
@@ -0,0 +1,94 @@
1
+ require 'snappy'
2
+ require 'memcache'
3
+ require 'minitest/autorun'
4
+ require 'active_support/cache'
5
+ require 'active_support'
6
+ require 'mocha/setup'
7
+ require 'timecop'
8
+
9
+
10
+ class TestMemcachedSnappyStore < ActiveSupport::TestCase
11
+
12
+ setup do
13
+ @cache = ActiveSupport::Cache.lookup_store(:memcached_snappy_store)
14
+ @cache.clear
15
+ end
16
+
17
+ test "test should not allow increment" do
18
+ assert_raise(ActiveSupport::Cache::MemcachedSnappyStore::UnsupportedOperation) do
19
+ @cache.increment('foo')
20
+ end
21
+ end
22
+
23
+ test "should not allow decrement" do
24
+ assert_raise(ActiveSupport::Cache::MemcachedSnappyStore::UnsupportedOperation) do
25
+ @cache.decrement('foo')
26
+ end
27
+ end
28
+
29
+ test "write should not allow the implicit add operation when unless_exist is passed to write" do
30
+ assert_raise(ActiveSupport::Cache::MemcachedSnappyStore::UnsupportedOperation) do
31
+ @cache.write('foo', 'bar', :unless_exist => true)
32
+ end
33
+ end
34
+
35
+ test "should use snappy to write cache entries" do
36
+ # Freezing time so created_at is the same in entry and the entry created
37
+ # internally and assert_equal between the raw data in the cache and the
38
+ # compressed explicitly makes sense
39
+ Timecop.freeze do
40
+ entry_value = { :omg => 'data' }
41
+ entry = ActiveSupport::Cache::Entry.new(entry_value)
42
+ key = 'moarponies'
43
+ assert @cache.write(key, entry_value)
44
+
45
+ serialized_entry = Marshal.dump(entry)
46
+ serialized_compressed_entry = Snappy.deflate(serialized_entry)
47
+ actual_cache_value = @cache.instance_eval{ @data.get(key, true ) }
48
+
49
+ assert_equal serialized_compressed_entry, actual_cache_value
50
+ end
51
+ end
52
+
53
+ test "should use snappy to read cache entries" do
54
+ entry_value = { :omg => 'data' }
55
+ key = 'ponies'
56
+
57
+ @cache.write(key, entry_value)
58
+ cache_entry = ActiveSupport::Cache::Entry.new(entry_value)
59
+ serialized_cached_entry = Marshal.dump(cache_entry)
60
+
61
+ Snappy.expects(:inflate).returns(serialized_cached_entry)
62
+ assert_equal entry_value, @cache.read(key)
63
+ end
64
+
65
+ test "should skip snappy to reading not found" do
66
+ key = 'ponies2'
67
+ Snappy.expects(:inflate).never
68
+ assert_nil @cache.read(key)
69
+ end
70
+
71
+ test "should use snappy to multi read cache entries but not on missing entries" do
72
+ keys = %w{ one tow three }
73
+ values = keys.map{ |k| k * 10 }
74
+ entries = values.map{ |v| ActiveSupport::Cache::Entry.new(v) }
75
+
76
+ keys.each_with_index{ |k, i| @cache.write(k, values[i]) }
77
+
78
+ keys_and_missing = keys << 'missing'
79
+
80
+ Snappy.expects(:inflate).times(3).returns(*entries)
81
+ assert_equal values, @cache.read_multi(*keys_and_missing).values
82
+ end
83
+
84
+ test "should use snappy to multi read cache entries" do
85
+ keys = %w{ one tow three }
86
+ values = keys.map{ |k| k * 10 }
87
+ entries = values.map{ |v| ActiveSupport::Cache::Entry.new(v) }
88
+
89
+ keys.each_with_index{ |k, i| @cache.write(k, values[i]) }
90
+
91
+ Snappy.expects(:inflate).times(3).returns(*entries)
92
+ assert_equal values, @cache.read_multi(*keys).values
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,205 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: memcached_snappy_store
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Camilo Lopez
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ type: :runtime
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.12
22
+ name: activesupport
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.12
30
+ - !ruby/object:Gem::Dependency
31
+ type: :runtime
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.0.4
38
+ name: snappy
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.0.4
46
+ - !ruby/object:Gem::Dependency
47
+ type: :development
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ name: i18n
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ type: :development
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ name: rake
71
+ prerelease: false
72
+ requirement: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ type: :development
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ name: minitest
87
+ prerelease: false
88
+ requirement: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ type: :development
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ name: mocha
103
+ prerelease: false
104
+ requirement: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ type: :development
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ name: timecop
119
+ prerelease: false
120
+ requirement: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ type: :development
128
+ version_requirements: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ name: memcache
135
+ prerelease: false
136
+ requirement: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ type: :development
144
+ version_requirements: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ name: memcache-client
151
+ prerelease: false
152
+ requirement: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description: Memcached store that will compress all entries using snappy
159
+ email:
160
+ - camilo@camilolopez.com
161
+ executables: []
162
+ extensions: []
163
+ extra_rdoc_files: []
164
+ files:
165
+ - .gitignore
166
+ - Gemfile
167
+ - LICENSE
168
+ - README.md
169
+ - Rakefile
170
+ - lib/active_support/cache/memcached_snappy_store.rb
171
+ - lib/memcached_snappy_store.rb
172
+ - memcached_snappy_store.gemspec
173
+ - test/test_memcached_snappy_store.rb
174
+ homepage: ''
175
+ licenses: []
176
+ post_install_message:
177
+ rdoc_options: []
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ! '>='
184
+ - !ruby/object:Gem::Version
185
+ segments:
186
+ - 0
187
+ hash: 4438705946747853744
188
+ version: '0'
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ none: false
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ segments:
195
+ - 0
196
+ hash: 4438705946747853744
197
+ version: '0'
198
+ requirements: []
199
+ rubyforge_project:
200
+ rubygems_version: 1.8.23
201
+ signing_key:
202
+ specification_version: 3
203
+ summary: Memcached store that will compress all entries using snappy
204
+ test_files:
205
+ - test/test_memcached_snappy_store.rb