libmemcached_store 0.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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ .DS_Store
2
+ pkg/
3
+ *.gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 37signals
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,47 @@
1
+ = LibmemcachedStore
2
+
3
+ An ActiveSupport cache store that uses the C-based libmemcached client through
4
+ Evan Weaver's Ruby/SWIG wrapper, memcached. libmemcached is fast, lightweight,
5
+ and supports consistent hashing, non-blocking IO, and graceful server failover.
6
+
7
+ == Prerequisites
8
+
9
+ You'll need both the libmemcached client and the memcached gem:
10
+
11
+ * http://tangent.org/552/libmemcached.html
12
+ * http://blog.evanweaver.com/files/doc/fauna/memcached
13
+
14
+ Make sure you install libmemcached first, before you try installing the gem. If
15
+ you're using OS X, the easiest way to install libmemcached is through MacPorts:
16
+
17
+ sudo port install libmemcached
18
+
19
+ For other platforms, download and extract the libmemcached tarball and install
20
+ manually:
21
+
22
+ ./configure
23
+ make && sudo make install
24
+
25
+ Once libmemcached is installed, install the memcached gem:
26
+
27
+ gem install memcached --no-rdoc --no-ri
28
+
29
+ == Usage
30
+
31
+ This is a drop-in replacement for the memcache store that ships with Rails. To
32
+ enable, set the <tt>config.cache_store</tt> option to <tt>:libmemcached_store</tt>
33
+ in the config for your environment
34
+
35
+ config.cache_store = :libmemcached_store
36
+
37
+ If no servers are specified, localhost is assumed. You can specify a list of
38
+ server addresses, either as hostnames or IP addresses, with or without a port
39
+ designation. If no port is given, 11211 is assumed:
40
+
41
+ config.cache_store = :libmemcached_store, %w(cache-01 cache-02 127.0.0.1:11212)
42
+
43
+ == Props
44
+
45
+ Thanks to Brian Aker (http://tangent.org) for creating libmemcached, and Evan
46
+ Weaver (http://blog.evanweaver.com) for the Ruby wrapper.
47
+
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "libmemcached_store"
9
+ gemspec.version = '0.2.0'
10
+ gemspec.summary = "ActiveSupport::Cache wrapper for libmemcached "
11
+ gemspec.description = "An ActiveSupport cache store that uses the C-based libmemcached client through
12
+ Evan Weaver's Ruby/SWIG wrapper, memcached. libmemcached is fast, lightweight,
13
+ and supports consistent hashing, non-blocking IO, and graceful server failover."
14
+ gemspec.email = "packagethief@gmail.com"
15
+ gemspec.homepage = "http://github.com/37signals/libmemcached_store"
16
+ gemspec.authors = ["Jeffrey Hardy"]
17
+ gemspec.add_runtime_dependency 'memcached'
18
+ end
19
+ rescue LoadError
20
+ puts "Jeweler not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ desc 'Default: run unit tests.'
24
+ task :default => :test
25
+
26
+ desc 'Test the libmemcached_store plugin.'
27
+ Rake::TestTask.new(:test) do |t|
28
+ t.libs << 'lib'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = true
31
+ end
32
+
33
+ desc 'Generate documentation for the libmemcached_store plugin.'
34
+ Rake::RDocTask.new(:rdoc) do |rdoc|
35
+ rdoc.rdoc_dir = 'rdoc'
36
+ rdoc.title = 'LibmemcachedStore'
37
+ rdoc.options << '--line-numbers' << '--inline-source'
38
+ rdoc.rdoc_files.include('README')
39
+ rdoc.rdoc_files.include('lib/**/*.rb')
40
+ end
@@ -0,0 +1,15 @@
1
+ module ActiveSupport
2
+ module Cache
3
+ class CompressedLibmemcachedStore < LibmemcachedStore
4
+ def read(name, options = {})
5
+ if value = super(name, (options || {}).merge(:raw => true))
6
+ Marshal.load(ActiveSupport::Gzip.decompress(value))
7
+ end
8
+ end
9
+
10
+ def write(name, value, options = {})
11
+ super(name, ActiveSupport::Gzip.compress(Marshal.dump(value)), (options || {}).merge(:raw => true))
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,127 @@
1
+ require 'memcached'
2
+
3
+ class Memcached
4
+ # The latest version of memcached (0.11) doesn't support hostnames with dashes
5
+ # in their names, so we overwrite it here to be more lenient.
6
+ def set_servers(servers)
7
+ [*servers].each_with_index do |server, index|
8
+ host, port = server.split(":")
9
+ Lib.memcached_server_add(@struct, host, port.to_i)
10
+ end
11
+ end
12
+ end
13
+
14
+ module ActiveSupport
15
+ module Cache
16
+ class LibmemcachedStore < Store
17
+ attr_reader :addresses
18
+
19
+ DEFAULT_OPTIONS = {
20
+ :distribution => :consistent,
21
+ :no_block => true,
22
+ :failover => true
23
+ }
24
+
25
+ def initialize(*addresses)
26
+ addresses.flatten!
27
+ options = addresses.extract_options!
28
+ addresses = %w(localhost) if addresses.empty?
29
+
30
+ @addresses = addresses
31
+ @cache = Memcached.new(@addresses, options.reverse_merge(DEFAULT_OPTIONS))
32
+ extend ActiveSupport::Cache::Strategy::LocalCache
33
+ end
34
+
35
+ def read(key, options = nil)
36
+ super
37
+ @cache.get(key, marshal?(options))
38
+ rescue Memcached::NotFound
39
+ nil
40
+ rescue Memcached::Error => e
41
+ log_error(e)
42
+ nil
43
+ end
44
+
45
+ def read_multi(*keys)
46
+ read(keys) || {}
47
+ end
48
+
49
+ # Set the key to the given value. Pass :unless_exist => true if you want to
50
+ # skip setting a key that already exists.
51
+ def write(key, value, options = nil)
52
+ super
53
+ method = (options && options[:unless_exist]) ? :add : :set
54
+ @cache.send(method, key, value, expires_in(options), marshal?(options))
55
+ true
56
+ rescue Memcached::Error => e
57
+ log_error(e)
58
+ false
59
+ end
60
+
61
+ def delete(key, options = nil)
62
+ super
63
+ @cache.delete(key)
64
+ true
65
+ rescue Memcached::NotFound
66
+ nil
67
+ rescue Memcached::Error => e
68
+ log_error(e)
69
+ false
70
+ end
71
+
72
+ def exist?(key, options = nil)
73
+ !read(key, options).nil?
74
+ end
75
+
76
+ def increment(key, amount=1)
77
+ log 'incrementing', key, amount
78
+ @cache.incr(key, amount)
79
+ rescue Memcached::Error
80
+ nil
81
+ end
82
+
83
+ def decrement(key, amount=1)
84
+ log 'decrementing', key, amount
85
+ @cache.decr(key, amount)
86
+ rescue Memcached::Error
87
+ nil
88
+ end
89
+
90
+ def delete_matched(matcher, options = nil)
91
+ super
92
+ raise NotImplementedError
93
+ end
94
+
95
+ # Flushes all data in memory
96
+ def clear
97
+ @cache.flush
98
+ end
99
+
100
+ def stats
101
+ @cache.stats
102
+ end
103
+
104
+ # Resets server connections, forcing a reconnect. This is required in
105
+ # cases where processes fork, but continue sharing the same memcached
106
+ # connection. You want to call this after the fork to make sure the
107
+ # new process has its own connection.
108
+ def reset
109
+ @cache.reset
110
+ end
111
+
112
+ private
113
+
114
+ def expires_in(options)
115
+ (options || {})[:expires_in] || 0
116
+ end
117
+
118
+ def marshal?(options)
119
+ !(options || {})[:raw]
120
+ end
121
+
122
+ def log_error(exception)
123
+ logger.error "MemcachedError (#{exception.inspect}): #{exception.message}" if logger && !@logger_off
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,76 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'active_support'
4
+ require 'memcached'
5
+
6
+ require File.dirname(__FILE__) + '/../lib/active_support/cache/libmemcached_store'
7
+
8
+ # Make it easier to get at the underlying cache options during testing.
9
+ class ActiveSupport::Cache::LibmemcachedStore
10
+ delegate :options, :to => '@cache'
11
+ end
12
+
13
+ class LibmemcachedStoreTest < Test::Unit::TestCase
14
+ def setup
15
+ @store = ActiveSupport::Cache.lookup_store :libmemcached_store
16
+ end
17
+
18
+ def test_should_identify_cache_store
19
+ assert_kind_of ActiveSupport::Cache::LibmemcachedStore, @store
20
+ end
21
+
22
+ def test_should_set_server_addresses_to_localhost_if_none_are_given
23
+ assert_equal %w(localhost), @store.addresses
24
+ end
25
+
26
+ def test_should_set_custom_server_addresses
27
+ store = ActiveSupport::Cache.lookup_store :libmemcached_store, 'localhost', '192.168.1.1'
28
+ assert_equal %w(localhost 192.168.1.1), store.addresses
29
+ end
30
+
31
+ def test_should_enable_consistent_hashing_by_default
32
+ assert_equal :consistent, @store.options[:distribution]
33
+ end
34
+
35
+ def test_should_enable_non_blocking_io_by_default
36
+ assert_equal true, @store.options[:no_block]
37
+ end
38
+
39
+ def test_should_enable_server_failover_by_default
40
+ assert_equal true, @store.options[:auto_eject_hosts]
41
+ end
42
+
43
+ def test_should_allow_configuration_of_custom_options
44
+ options = {
45
+ :prefix_key => 'test',
46
+ :distribution => :modula,
47
+ :no_block => false,
48
+ :auto_eject_hosts => false
49
+ }
50
+
51
+ store = ActiveSupport::Cache.lookup_store :libmemcached_store, 'localhost', options
52
+
53
+ assert_equal 'test', store.instance_variable_get(:@cache).prefix_key
54
+ assert_equal :modula, store.options[:distribution]
55
+ assert_equal false, store.options[:no_block]
56
+ assert_equal false, store.options[:auto_eject_hosts]
57
+ end
58
+
59
+ def test_should_use_local_cache
60
+ @store.with_local_cache do
61
+ @store.write('key', 'value')
62
+ assert_equal 'value', @store.send(:local_cache).read('key')
63
+ end
64
+
65
+ assert_equal 'value', @store.read('key')
66
+ end
67
+
68
+ def test_should_read_multiple_keys
69
+ @store.write('a', 1)
70
+ @store.write('b', 2)
71
+
72
+ assert_equal({ 'a' => 1, 'b' => 2 }, @store.read_multi('a', 'b', 'c'))
73
+ assert_equal({}, @store.read_multi())
74
+ end
75
+
76
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: libmemcached_store
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
+ platform: ruby
12
+ authors:
13
+ - Jeffrey Hardy
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-24 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: memcached
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ description: |-
36
+ An ActiveSupport cache store that uses the C-based libmemcached client through
37
+ Evan Weaver's Ruby/SWIG wrapper, memcached. libmemcached is fast, lightweight,
38
+ and supports consistent hashing, non-blocking IO, and graceful server failover.
39
+ email: packagethief@gmail.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - README
46
+ files:
47
+ - .gitignore
48
+ - MIT-LICENSE
49
+ - README
50
+ - Rakefile
51
+ - lib/active_support/cache/compressed_libmemcached_store.rb
52
+ - lib/active_support/cache/libmemcached_store.rb
53
+ - test/libmemcached_store_test.rb
54
+ has_rdoc: true
55
+ homepage: http://github.com/37signals/libmemcached_store
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --charset=UTF-8
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 3
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: ActiveSupport::Cache wrapper for libmemcached
88
+ test_files:
89
+ - test/libmemcached_store_test.rb