dalli 1.1.5 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of dalli might be problematic. Click here for more details.

data/Gemfile CHANGED
@@ -3,8 +3,6 @@ source :rubygems
3
3
  gemspec
4
4
 
5
5
  gem 'rake'
6
- gem 'em-synchrony', :platforms => :mri_19
7
- gem 'em-spec', :platforms => :mri_19
8
6
 
9
7
  group :test do
10
8
  gem 'simplecov'
data/History.md CHANGED
@@ -1,11 +1,29 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
- WARNING: This is the last 1.1.x release. The next release will be 2.0
5
- and not compatible with 1.x; lock dalli in your Gemfile so it does not
6
- pick up 2.x releases:
4
+ 2.0.0
5
+ =======
6
+
7
+ - Reimplemented the Rails' dalli\_store to remove use of
8
+ ActiveSupport::Cache::Entry which added 109 bytes overhead to every
9
+ value stored, was a performance bottleneck and duplicated a lot of
10
+ functionality already in Dalli. One benchmark went from 4.0 sec to 3.0
11
+ sec with the new dalli\_store. [#173]
12
+ - Added reset\_stats operation [#155]
13
+ - Added support for configuring keepalive on TCP connections to memcached servers (@bianster, #180)
14
+
15
+ Notes:
16
+
17
+ * data stored with dalli\_store 2.x is NOT backwards compatible with 1.x.
18
+ Upgraders are advised to namespace their keys and roll out the 2.x
19
+ upgrade slowly so keys do not clash and caches are warmed.
20
+ `config.cache_store = :dalli_store, :expires_in => 24.hours.to_i, :namespace => 'myapp2'`
21
+ * data stored with plain Dalli::Client API is unchanged.
22
+ * removed support for dalli\_store's race\_condition\_ttl option.
23
+ * removed support for em-synchrony and unix socket connection options.
24
+ * removed support for Ruby 1.8.6
25
+ * removed memcache-client compability layer and upgrade documentation.
7
26
 
8
- gem 'dalli', '~> 1.1'
9
27
 
10
28
  1.1.5
11
29
  =======
@@ -84,7 +102,7 @@ v1.1.0 was a bad release. Yanked.
84
102
  - Allow browser session cookies (blindsey)
85
103
  - Compatibility fixes (mwynholds)
86
104
  - Add backwards compatibility module for memcache-client, require 'dalli/memcache-client'. It makes
87
- Dalli more compatible with memcache-client and prints out a warning any time you do something that
105
+ Dalli more compatible with memcache-client and prints out a warning any time you do something that
88
106
  is no longer supported so you can fix your code.
89
107
 
90
108
  1.0.1
data/Performance.md CHANGED
@@ -2,84 +2,41 @@ Performance
2
2
  ====================
3
3
 
4
4
  Caching is all about performance, so I carefully track Dalli performance to ensure no regressions.
5
- Times are from a Unibody MBP 2.4Ghz Core i5 running Snow Leopard.
5
+ You can optionally use kgio to give Dalli a 10-20% performance boost: `gem install kgio`.
6
6
 
7
- You can optionally use kgio to give Dalli a small, 10-20% performance boost: gem install kgio.
7
+ Note I've added some benchmarks over time to Dalli that the other libraries don't necessarily have.
8
8
 
9
9
  memcache-client
10
10
  ---------------
11
11
 
12
- Testing 1.8.5 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
12
+ Testing 1.8.5 with ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.2.0]
13
13
 
14
- user system total real
15
- set:plain:memcache 2.070000 0.340000 2.410000 ( 2.611709)
16
- set:ruby:memcache 1.990000 0.330000 2.320000 ( 2.869653)
17
- get:plain:memcache 2.290000 0.360000 2.650000 ( 2.926425)
18
- get:ruby:memcache 2.360000 0.350000 2.710000 ( 2.951604)
19
- multiget:ruby:memcache 1.050000 0.120000 1.170000 ( 1.285787)
20
- missing:ruby:memcache 1.990000 0.330000 2.320000 ( 2.567641)
21
- mixed:ruby:memcache 4.390000 0.670000 5.060000 ( 5.721000)
22
- incr:ruby:memcache 0.700000 0.120000 0.820000 ( 0.842896)
14
+ user system total real
15
+ set:plain:memcache-client 1.860000 0.310000 2.170000 ( 2.188030)
16
+ set:ruby:memcache-client 1.830000 0.290000 2.120000 ( 2.130212)
17
+ get:plain:memcache-client 1.830000 0.340000 2.170000 ( 2.176156)
18
+ get:ruby:memcache-client 1.900000 0.330000 2.230000 ( 2.235045)
19
+ multiget:ruby:memcache-client 0.860000 0.120000 0.980000 ( 0.987348)
20
+ missing:ruby:memcache-client 1.630000 0.320000 1.950000 ( 1.954867)
21
+ mixed:ruby:memcache-client 3.690000 0.670000 4.360000 ( 4.364469)
23
22
 
24
- libmemcached
25
- ------------
26
-
27
- Testing 1.1.2 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
28
-
29
- user system total real
30
- set:plain:libm 0.120000 0.220000 0.340000 ( 0.847521)
31
- setq:plain:libm 0.030000 0.000000 0.030000 ( 0.126944)
32
- set:ruby:libm 0.220000 0.250000 0.470000 ( 1.102789)
33
- get:plain:libm 0.140000 0.230000 0.370000 ( 0.813998)
34
- get:ruby:libm 0.210000 0.240000 0.450000 ( 1.025994)
35
- multiget:ruby:libm 0.100000 0.080000 0.180000 ( 0.322217)
36
- missing:ruby:libm 0.250000 0.240000 0.490000 ( 1.049972)
37
- mixed:ruby:libm 0.400000 0.410000 0.810000 ( 2.172349)
38
- mixedq:ruby:libm 0.410000 0.360000 0.770000 ( 1.516718)
39
- incr:ruby:libm 0.080000 0.340000 0.420000 ( 1.685931)
40
23
 
41
24
  dalli
42
25
  -----
43
26
 
27
+ Testing with Rails 3.2.1
44
28
  Using kgio socket IO
45
- Testing 1.0.2 with ruby 1.9.2p136 (2010-12-25 revision 30365) [x86_64-darwin10.6.0]
46
-
47
- user system total real
48
- set:plain:dalli 0.850000 0.320000 1.170000 ( 1.691393)
49
- setq:plain:dalli 0.500000 0.130000 0.630000 ( 0.651227)
50
- set:ruby:dalli 0.900000 0.330000 1.230000 ( 1.865228)
51
- get:plain:dalli 0.990000 0.390000 1.380000 ( 1.929994)
52
- get:ruby:dalli 0.950000 0.370000 1.320000 ( 1.844251)
53
- multiget:ruby:dalli 0.790000 0.300000 1.090000 ( 1.227073)
54
- missing:ruby:dalli 0.810000 0.370000 1.180000 ( 1.627039)
55
- mixed:ruby:dalli 1.850000 0.710000 2.560000 ( 3.555032)
56
- mixedq:ruby:dalli 1.840000 0.610000 2.450000 ( 2.945982)
57
- incr:ruby:dalli 0.310000 0.120000 0.430000 ( 0.616465)
58
-
59
- Testing 1.0.2 with rubinius 1.3.0dev (1.8.7 382e813f xxxx-xx-xx JI) [x86_64-apple-darwin10.6.0]
60
-
61
- user system total real
62
- set:plain:dalli 2.800581 0.329360 3.129941 ( 5.186546)
63
- setq:plain:dalli 1.064253 0.138044 1.202297 ( 1.280355)
64
- set:ruby:dalli 2.220885 0.262619 2.483504 ( 2.778118)
65
- get:plain:dalli 2.291344 0.280490 2.571834 ( 2.948004)
66
- get:ruby:dalli 2.148900 0.274477 2.423377 ( 2.425808)
67
- multiget:ruby:dalli 1.724193 0.249145 1.973338 ( 2.158673)
68
- missing:ruby:dalli 1.881502 0.272610 2.154112 ( 2.208384)
69
- mixed:ruby:dalli 4.292620 0.533768 4.826388 ( 4.830238)
70
- mixedq:ruby:dalli 4.076032 0.501442 4.577474 ( 4.583800)
71
- incr:ruby:dalli 0.691467 0.091475 0.782942 ( 0.931674)
72
-
73
- Testing 1.0.2 with rubinius 1.2.0 (1.8.7 release 2010-12-21 JI) [x86_64-apple-darwin10.6.0]
74
-
75
- user system total real
76
- set:plain:dalli 6.586927 0.331545 6.918472 ( 4.628652)
77
- setq:plain:dalli 0.930905 0.129008 1.059913 ( 1.016105)
78
- set:ruby:dalli 2.702486 0.283004 2.985490 ( 2.690442)
79
- get:plain:dalli 2.740202 0.291353 3.031555 ( 2.722746)
80
- get:ruby:dalli 1.979379 0.282986 2.262365 ( 2.264118)
81
- multiget:ruby:dalli 1.887086 0.249799 2.136885 ( 1.803230)
82
- missing:ruby:dalli 1.882662 0.278019 2.160681 ( 2.113429)
83
- mixed:ruby:dalli 3.969242 0.553361 4.522603 ( 4.524504)
84
- mixedq:ruby:dalli 3.520755 0.475669 3.996424 ( 3.997405)
85
- incr:ruby:dalli 0.849998 0.094012 0.944010 ( 0.884001)
29
+ Testing 2.0.0 with ruby 1.9.3p125 (2012-02-16 revision 34643) [x86_64-darwin11.3.0]
30
+
31
+ user system total real
32
+ mixed:rails:dalli 1.580000 0.570000 2.150000 ( 3.008839)
33
+ set:plain:dalli 0.730000 0.300000 1.030000 ( 1.567098)
34
+ setq:plain:dalli 0.520000 0.120000 0.640000 ( 0.634402)
35
+ set:ruby:dalli 0.800000 0.300000 1.100000 ( 1.640348)
36
+ get:plain:dalli 0.840000 0.330000 1.170000 ( 1.668425)
37
+ get:ruby:dalli 0.850000 0.330000 1.180000 ( 1.665716)
38
+ multiget:ruby:dalli 0.700000 0.260000 0.960000 ( 0.965423)
39
+ missing:ruby:dalli 0.720000 0.320000 1.040000 ( 1.511720)
40
+ mixed:ruby:dalli 1.660000 0.640000 2.300000 ( 3.320743)
41
+ mixedq:ruby:dalli 1.630000 0.510000 2.140000 ( 2.629734)
42
+ incr:ruby:dalli 0.270000 0.100000 0.370000 ( 0.547618)
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Dalli
2
2
  =========
3
3
 
4
- Dalli is a high performance pure Ruby client for accessing memcached servers. It works with memcached 1.4+ only as it uses the newer binary protocol. It should be considered a replacement for the memcache-client gem. The API tries to be mostly compatible with memcache-client with the goal being to make it a drop-in replacement for Rails.
4
+ Dalli is a high performance pure Ruby client for accessing memcached servers. It works with memcached 1.4+ only as it uses the newer binary protocol. It should be considered a replacement for the memcache-client gem.
5
5
 
6
6
  The name is a variant of Salvador Dali for his famous painting [The Persistence of Memory](http://en.wikipedia.org/wiki/The_Persistence_of_Memory).
7
7
 
@@ -25,10 +25,8 @@ So a few notes. Dalli:
25
25
  0. uses the exact same algorithm to choose a server so existing memcached clusters with TBs of data will work identically to memcache-client.
26
26
  1. is approximately 20% faster than memcache-client (which itself was heavily optimized) in Ruby 1.9.2.
27
27
  2. contains explicit "chokepoint" methods which handle all requests; these can be hooked into by monitoring tools (NewRelic, Rack::Bug, etc) to track memcached usage.
28
- 3. comes with hooks to replace memcache-client in Rails.
29
- 4. supports SASL for use in managed environments, e.g. Heroku.
30
- 5. provides proper failover with recovery and adjustable timeouts
31
- 6. has a backwards-compatibility mode for people migrating from memcache-client (see Upgrade.md).
28
+ 3. supports SASL for use in managed environments, e.g. Heroku.
29
+ 4. provides proper failover with recovery and adjustable timeouts
32
30
 
33
31
 
34
32
  Supported Ruby versions and implementations
@@ -47,8 +45,7 @@ If you have problems, please enter an issue.
47
45
  Installation and Usage
48
46
  ------------------------
49
47
 
50
- Remember, Dalli **requires** memcached 1.4+. You can check the version with `memcached -h`. Please note that memcached that Mac OS X Snow Leopard ships with is 1.2.8 and
51
- won't work. Install 1.4.x using Homebrew with
48
+ Remember, Dalli **requires** memcached 1.4+. You can check the version with `memcached -h`. Please note that memcached that Mac OS X Snow Leopard ships with is 1.2.8 and won't work. Install 1.4.x using Homebrew with
52
49
 
53
50
  brew install memcached
54
51
 
@@ -65,7 +62,7 @@ You can verify your installation using this piece of code:
65
62
  The test suite requires memcached 1.4.3+ with SASL enabled (brew install memcached --enable-sasl ; mv /usr/bin/memcached /usr/bin/memcached.old). Currently only supports the PLAIN mechanism.
66
63
 
67
64
  Dalli has no runtime dependencies and never will. You can optionally install the 'kgio' gem to
68
- give Dalli a 10-20% performance boost.
65
+ give Dalli a 20-30% performance boost.
69
66
 
70
67
 
71
68
  Usage with Rails 3.x
@@ -79,21 +76,17 @@ In `config/environments/production.rb`:
79
76
 
80
77
  config.cache_store = :dalli_store
81
78
 
82
- A more comprehensive example (note that we are setting a reasonable default for maximum cache entry lifetime (one day), enabling compression for large values, and namespacing all entries for this rails app. Remove the namespace if you have multiple apps which share cached values):
79
+ Here's a more comprehensive example that sets a reasonable default for maximum cache entry lifetime (one day), enables compression for large values and namespaces all entries for this rails app. Remove the namespace if you have multiple apps which share cached values.
83
80
 
84
81
  config.cache_store = :dalli_store, 'cache-1.example.com', 'cache-2.example.com',
85
- { :namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compression => true }
82
+ { :namespace => NAME_OF_RAILS_APP, :expires_in => 1.day, :compress => true }
86
83
 
87
84
  To use Dalli for Rails session storage, in `config/initializers/session_store.rb`:
88
85
 
89
86
  require 'action_dispatch/middleware/session/dalli_store'
90
87
  Rails.application.config.session_store :dalli_store, :memcache_server => ['host1', 'host2'], :namespace => 'sessions', :key => '_foundation_session', :expire_after => 30.minutes
91
88
 
92
-
93
- Usage with Rails 2.3.x
94
- ----------------------------
95
-
96
- Dalli v1.1+ does not support Rails 2.3. Please use an earlier version: gem install dalli -v "~> 1.0.4"
89
+ Dalli does not support Rails 2.x any longer.
97
90
 
98
91
 
99
92
  Usage with Passenger
@@ -122,7 +115,7 @@ Dalli::Client accepts the following options. All times are in seconds.
122
115
 
123
116
  **failover**: Boolean, if true Dalli will failover to another server if the main server for a key is down.
124
117
 
125
- **compression**: Boolean, if true Dalli will gzip-compress values larger than 1K.
118
+ **compress**: Boolean, if true Dalli will gzip-compress values larger than 1K.
126
119
 
127
120
  **socket_timeout**: Timeout for all socket operations (connect, read, write). Default is 0.5.
128
121
 
@@ -138,17 +131,13 @@ Dalli::Client accepts the following options. All times are in seconds.
138
131
 
139
132
  **password**: The password to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
140
133
 
141
- **async**: Boolean, if true Dalli will assume its running inside the EventMachine reactor and use EM through the em-synchrony gem. Currently disables the socket_timeout option. Default is false.
134
+ **keepalive**: Boolean, if true Dalli will enable keep-alives on the socket so inactivity
142
135
 
143
136
  Features and Changes
144
137
  ------------------------
145
138
 
146
- Dalli is **NOT** 100% API compatible with memcache-client. If you have code which uses the MemCache API directly, it will likely need small tweaks. Method parameters and return values changed slightly. See Upgrade.md for more detail.
147
-
148
139
  By default, Dalli is thread-safe. Disable thread-safety at your own peril.
149
140
 
150
- Multi-threaded use will fail if used with EventMachine.
151
-
152
141
  Note that Dalli does not require ActiveSupport or Rails. You can safely use it in your own Ruby projects.
153
142
 
154
143
 
@@ -171,7 +160,9 @@ Brian Mitchell - for his remix-stash project which was helpful when implementing
171
160
  Author
172
161
  ----------
173
162
 
174
- Mike Perham, mperham@gmail.com, [mikeperham.com](http://mikeperham.com), [@mperham](http://twitter.com/mperham) If you like and use this project, please give me a recommendation at [WWR](http://workingwithrails.com/person/10797-mike-perham). Happy caching!
163
+ Mike Perham, mperham@gmail.com, [mikeperham.com](http://mikeperham.com), [@mperham](http://twitter.com/mperham) If you like and use this project, please give me a recommendation at [WWR](http://workingwithrails.com/person/10797-mike-perham) or send a few bucks my way via my Pledgie page below. Happy caching!
164
+
165
+ <a href='http://www.pledgie.com/campaigns/16623'><img alt='Click here to lend your support to Open Source and make a donation at www.pledgie.com !' src='http://www.pledgie.com/campaigns/16623.png?skin_name=chrome' border='0' /></a>
175
166
 
176
167
 
177
168
  Copyright
data/dalli.gemspec CHANGED
@@ -5,7 +5,6 @@ Gem::Specification.new do |s|
5
5
  s.version = Dalli::VERSION
6
6
 
7
7
  s.authors = ["Mike Perham"]
8
- s.date = Time.now.utc.strftime("%Y-%m-%d")
9
8
  s.description = %q{High performance memcached client for Ruby}
10
9
  s.email = %q{mperham@gmail.com}
11
10
  s.files = Dir.glob("lib/**/*") + [
@@ -16,7 +15,6 @@ Gem::Specification.new do |s|
16
15
  "Gemfile",
17
16
  "dalli.gemspec",
18
17
  "Performance.md",
19
- "Upgrade.md",
20
18
  ]
21
19
  s.homepage = %q{http://github.com/mperham/dalli}
22
20
  s.rdoc_options = ["--charset=UTF-8"]
@@ -25,7 +23,6 @@ Gem::Specification.new do |s|
25
23
  s.test_files = Dir.glob("test/**/*")
26
24
  s.add_development_dependency(%q<mini_shoulda>, [">= 0"])
27
25
  s.add_development_dependency(%q<mocha>, [">= 0"])
28
- s.add_development_dependency(%q<rails>, [">= 3.0"])
29
- s.add_development_dependency(%q<memcache-client>, [">= 1.8.5"])
26
+ s.add_development_dependency(%q<rails>, ["~> 3"])
30
27
  end
31
28
 
@@ -1,24 +1,12 @@
1
1
  # encoding: ascii
2
- begin
3
- require 'dalli'
4
- rescue LoadError => e
5
- $stderr.puts "You don't have dalli installed in your application. Please add it to your Gemfile and run bundle install"
6
- raise e
7
- end
2
+ require 'dalli'
8
3
  require 'digest/md5'
9
- require 'active_support/cache'
10
4
 
11
5
  module ActiveSupport
12
6
  module Cache
13
- # A cache store implementation which stores data in Memcached:
14
- # http://www.memcached.org
15
- #
16
- # DalliStore implements the Strategy::LocalCache strategy which implements
17
- # an in memory cache inside of a block.
18
- class DalliStore < Store
7
+ class DalliStore
19
8
 
20
9
  ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
21
- RAW = { :raw => true }
22
10
 
23
11
  # Creates a new DalliStore object, with the given memcached server
24
12
  # addresses. Each address is either a host name, or a host-with-port string
@@ -32,33 +20,73 @@ module ActiveSupport
32
20
  def initialize(*addresses)
33
21
  addresses = addresses.flatten
34
22
  options = addresses.extract_options!
35
- super(options)
36
-
23
+ options[:compression] = options.delete(:compress) || options[:compression]
37
24
  addresses << 'localhost:11211' if addresses.empty?
38
- options = options.dup
39
- options.delete(:namespace)
40
- # Extend expiry by stale TTL or else memcached will never return stale data.
41
- # See ActiveSupport::Cache#fetch.
42
- options[:expires_in] += options[:race_condition_ttl] if options[:expires_in] && options[:race_condition_ttl]
43
25
  @data = Dalli::Client.new(addresses, options)
26
+ end
27
+
28
+ def fetch(name, options={})
29
+ if block_given?
30
+ unless options[:force]
31
+ entry = instrument(:read, name, options) do |payload|
32
+ payload[:super_operation] = :fetch if payload
33
+ read_entry(name, options)
34
+ end
35
+ end
36
+
37
+ if entry
38
+ instrument(:fetch_hit, name, options) { |payload| }
39
+ entry
40
+ else
41
+ result = instrument(:generate, name, options) do |payload|
42
+ yield
43
+ end
44
+ write(name, result, options)
45
+ result
46
+ end
47
+ else
48
+ read(name, options)
49
+ end
50
+ end
44
51
 
45
- extend Strategy::LocalCache
46
- extend LocalCacheWithRaw
52
+ def read(name, options={})
53
+ instrument(:read, name, options) do |payload|
54
+ entry = read_entry(name, options)
55
+ payload[:hit] = !!entry if payload
56
+ entry
57
+ end
58
+ end
59
+
60
+ def write(name, value, options={})
61
+ instrument(:write, name, options) do |payload|
62
+ write_entry(name, value, options)
63
+ end
64
+ end
65
+
66
+ def exist?(name, options={})
67
+ !!read_entry(name, options)
68
+ end
69
+
70
+ def delete(name, options={})
71
+ @data.delete(name)
47
72
  end
48
73
 
49
74
  # Reads multiple keys from the cache using a single call to the
50
- # servers for all keys. Options can be passed in the last argument.
75
+ # servers for all keys. Keys must be Strings.
51
76
  def read_multi(*names)
52
- options = names.extract_options!
53
- options = merged_options(options)
54
- keys_to_names = names.flatten.inject({}){|map, name| map[escape_key(namespaced_key(name, options))] = name; map}
55
- raw_values = @data.get_multi(keys_to_names.keys, RAW)
56
- values = {}
57
- raw_values.each do |key, value|
58
- entry = deserialize_entry(value)
59
- values[keys_to_names[key]] = entry.value unless entry.expired?
77
+ names.extract_options!
78
+ names = names.flatten
79
+
80
+ mapping = names.inject({}) { |memo, name| memo[escape(name)] = name; memo }
81
+ instrument(:read_multi, names) do
82
+ results = @data.get_multi(mapping.keys)
83
+ results.inject({}) do |memo, (inner, value)|
84
+ entry = results[inner]
85
+ # NB Backwards data compatibility, to be removed at some point
86
+ memo[mapping[inner]] = (entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry)
87
+ memo
88
+ end
60
89
  end
61
- values
62
90
  end
63
91
 
64
92
  # Increment a cached value. This method uses the memcached incr atomic
@@ -66,12 +94,11 @@ module ActiveSupport
66
94
  # Calling it on a value not stored with :raw will fail.
67
95
  # :initial defaults to the amount passed in, as if the counter was initially zero.
68
96
  # memcached counters cannot hold negative values.
69
- def increment(name, amount = 1, options = nil) # :nodoc:
70
- options = merged_options(options)
97
+ def increment(name, amount = 1, options={}) # :nodoc:
71
98
  initial = options[:initial] || amount
72
- expires_in = options[:expires_in].to_i
73
- response = instrument(:increment, name, :amount => amount) do
74
- @data.incr(escape_key(namespaced_key(name, options)), amount, expires_in, initial)
99
+ expires_in = options[:expires_in]
100
+ instrument(:increment, name, :amount => amount) do
101
+ @data.incr(name, amount, expires_in, initial)
75
102
  end
76
103
  rescue Dalli::DalliError => e
77
104
  logger.error("DalliError: #{e.message}") if logger
@@ -83,12 +110,11 @@ module ActiveSupport
83
110
  # Calling it on a value not stored with :raw will fail.
84
111
  # :initial defaults to zero, as if the counter was initially zero.
85
112
  # memcached counters cannot hold negative values.
86
- def decrement(name, amount = 1, options = nil) # :nodoc:
87
- options = merged_options(options)
113
+ def decrement(name, amount = 1, options={}) # :nodoc:
88
114
  initial = options[:initial] || 0
89
- expires_in = options[:expires_in].to_i
90
- response = instrument(:decrement, name, :amount => amount) do
91
- @data.decr(escape_key(namespaced_key(name, options)), amount, expires_in, initial)
115
+ expires_in = options[:expires_in]
116
+ instrument(:decrement, name, :amount => amount) do
117
+ @data.decr(name, amount, expires_in, initial)
92
118
  end
93
119
  rescue Dalli::DalliError => e
94
120
  logger.error("DalliError: #{e.message}") if logger
@@ -97,7 +123,7 @@ module ActiveSupport
97
123
 
98
124
  # Clear the entire cache on all memcached servers. This method should
99
125
  # be used with care when using a shared cache.
100
- def clear(options = nil)
126
+ def clear(options=nil)
101
127
  @data.flush_all
102
128
  end
103
129
 
@@ -112,74 +138,65 @@ module ActiveSupport
112
138
 
113
139
  protected
114
140
 
115
- # This CacheStore impl controls value marshalling so we take special
116
- # care to always pass :raw => true to the Dalli API so it does not
117
- # double marshal.
118
-
119
- # Read an entry from the cache.
120
- def read_entry(key, options) # :nodoc:
121
- deserialize_entry(@data.get(escape_key(key), RAW))
122
- rescue Dalli::DalliError => e
123
- logger.error("DalliError: #{e.message}") if logger
124
- nil
125
- end
141
+ # Read an entry from the cache.
142
+ def read_entry(key, options) # :nodoc:
143
+ entry = @data.get(escape(key), options)
144
+ # NB Backwards data compatibility, to be removed at some point
145
+ entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
146
+ rescue Dalli::DalliError => e
147
+ logger.error("DalliError: #{e.message}") if logger
148
+ nil
149
+ end
126
150
 
127
- # Write an entry to the cache.
128
- def write_entry(key, entry, options) # :nodoc:
129
- method = options[:unless_exist] ? :add : :set
130
- value = options[:raw] ? entry.value.to_s : entry
131
- expires_in = options[:expires_in].to_i
132
- if expires_in > 0 && !options[:raw]
133
- # Set the memcache expire a few minutes in the future to support race condition ttls on read
134
- expires_in += 5.minutes
135
- end
136
- @data.send(method, escape_key(key), value, expires_in, options)
137
- rescue Dalli::DalliError => e
138
- logger.error("DalliError: #{e.message}") if logger
139
- false
140
- end
151
+ # Write an entry to the cache.
152
+ def write_entry(key, value, options) # :nodoc:
153
+ method = options[:unless_exist] ? :add : :set
154
+ expires_in = options[:expires_in]
155
+ @data.send(method, escape(key), value, expires_in, options)
156
+ rescue Dalli::DalliError => e
157
+ logger.error("DalliError: #{e.message}") if logger
158
+ false
159
+ end
141
160
 
142
- # Delete an entry from the cache.
143
- def delete_entry(key, options) # :nodoc:
144
- @data.delete(escape_key(key))
145
- rescue Dalli::DalliError => e
146
- logger.error("DalliError: #{e.message}") if logger
147
- false
148
- end
161
+ # Delete an entry from the cache.
162
+ def delete_entry(key, options) # :nodoc:
163
+ @data.delete(escape(key))
164
+ rescue Dalli::DalliError => e
165
+ logger.error("DalliError: #{e.message}") if logger
166
+ false
167
+ end
149
168
 
150
169
  private
151
- def escape_key(key)
152
- key = key.to_s.dup
153
- key = key.force_encoding('BINARY') if key.respond_to? :force_encoding
154
- key = key.gsub(ESCAPE_KEY_CHARS){|match| "%#{match.getbyte(0).to_s(16).upcase}"}
155
- key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
156
- key
157
- end
158
170
 
159
- def deserialize_entry(raw_value)
160
- if raw_value
161
- # FIXME: This is a terrible implementation for performance reasons:
162
- # throwing an exception is much slower than some if logic.
163
- entry = Marshal.load(raw_value) rescue raw_value
164
- entry.is_a?(Entry) ? entry : Entry.new(entry)
165
- else
166
- nil
167
- end
171
+ def escape(key)
172
+ key = key.to_s.dup
173
+ key = key.force_encoding("BINARY") if key.encoding_aware?
174
+ key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
175
+ key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
176
+ key
177
+ end
178
+
179
+ def instrument(operation, key, options = nil)
180
+ log(operation, key, options)
181
+
182
+ if ActiveSupport::Cache::Store.instrument
183
+ payload = { :key => key }
184
+ payload.merge!(options) if options.is_a?(Hash)
185
+ ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
186
+ else
187
+ yield(nil)
168
188
  end
189
+ end
169
190
 
170
- # Provide support for raw values in the local cache strategy.
171
- module LocalCacheWithRaw # :nodoc:
172
- protected
173
- def write_entry(key, entry, options) # :nodoc:
174
- retval = super
175
- if options[:raw] && local_cache && retval
176
- raw_entry = Entry.new(entry.value.to_s)
177
- raw_entry.expires_at = entry.expires_at
178
- local_cache.write_entry(key, raw_entry, options)
179
- end
180
- retval
181
- end
191
+ def log(operation, key, options = nil)
192
+ return unless logger && logger.debug?
193
+ logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
182
194
  end
195
+
196
+ def logger
197
+ Dalli.logger
198
+ end
199
+
183
200
  end
184
201
  end
185
202
  end