dalli 1.0.0 → 1.0.1

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/History.md CHANGED
@@ -1,6 +1,17 @@
1
1
  Dalli Changelog
2
2
  =====================
3
3
 
4
+ 1.0.1
5
+ =======
6
+
7
+ - Explicitly handle application marshalling bugs, GH-56
8
+ - Add support for username/password as options, to allow multiple bucket access
9
+ from the same Ruby process, GH-52
10
+ - Add support for >1MB values with :value_max_bytes option, GH-54 (r-stu31)
11
+ - Add support for default TTL, :expires_in, in Rails 2.3. (Steven Novotny)
12
+ config.cache_store = :dalli_store, 'localhost:11211', {:expires_in => 4.hours}
13
+
14
+
4
15
  1.0.0
5
16
  =======
6
17
 
data/README.md CHANGED
@@ -128,8 +128,13 @@ Dalli::Client accepts the following options. All times are in seconds.
128
128
 
129
129
  **socket_failure_delay**: Before retrying a socket operation, the process sleeps for this amount of time. Default is 0.01. Set to nil for no delay.
130
130
 
131
- **down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value to low, otherwise each request which tries the failed server might hang for the maximum timeout (see below). Default is 1 second.
131
+ **down_retry_delay**: When a server has been marked down due to many failures, the server will be checked again for being alive only after this amount of time. Don't set this value to low, otherwise each request which tries the failed server might hang for the maximum **socket_timeout**. Default is 1 second.
132
132
 
133
+ **value_max_bytes**: The maximum size of a value in memcached. Defaults to 1MB, this can be increased with memcached's -I parameter. You must also configure Dalli to allow the larger size here.
134
+
135
+ **username**: The username to use for authenticating this client instance against a SASL-enabled memcached server. Heroku users should not need to use this normally.
136
+
137
+ **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.
133
138
 
134
139
  Features and Changes
135
140
  ------------------------
data/Rakefile CHANGED
@@ -22,4 +22,9 @@ task :default => :test
22
22
  task :test_all do
23
23
  system('rake test RAILS_VERSION="~> 2.3.0"')
24
24
  system('rake test RAILS_VERSION="~> 3.0.0"')
25
+ end
26
+
27
+ require 'rake/rdoctask'
28
+ Rake::RDocTask.new do |rd|
29
+ rd.rdoc_files.include("lib/**/*.rb")
25
30
  end
@@ -37,6 +37,7 @@ module ActiveSupport
37
37
 
38
38
  mem_cache_options = options.dup
39
39
  @namespace = mem_cache_options.delete(:namespace)
40
+ @expires_in = mem_cache_options[:expires_in]
40
41
  @data = self.class.build_mem_cache(*(addresses + [mem_cache_options]))
41
42
 
42
43
  extend Strategy::LocalCache
@@ -154,7 +155,7 @@ module ActiveSupport
154
155
 
155
156
  # Exists in 2.3.8 but not in 2.3.2 so roll our own version
156
157
  def expires_in(options)
157
- expires_in = options && options[:expires_in]
158
+ expires_in = (options && options[:expires_in]) || @expires_in
158
159
 
159
160
  raise ":expires_in must be a number" if expires_in && !expires_in.is_a?(Numeric)
160
161
 
data/lib/dalli/client.rb CHANGED
@@ -19,7 +19,7 @@ module Dalli
19
19
  # - :threadsafe - ensure that only one thread is actively using a socket at a time. Default: true.
20
20
  # - :expires_in - default TTL in seconds if you do not pass TTL as a parameter to an individual operation, defaults to 0 or forever
21
21
  # - :compression - defaults to false, if true Dalli will compress values larger than 100 bytes before
22
- # sending them to memcached.
22
+ # sending them to memcached.
23
23
  #
24
24
  def initialize(servers=nil, options={})
25
25
  @servers = env_servers || servers || 'localhost:11211'
@@ -145,9 +145,7 @@ module Dalli
145
145
  end
146
146
 
147
147
  # deprecated, please use #flush.
148
- def flush_all(delay=0)
149
- flush(delay)
150
- end
148
+ alias_method :flush_all, :flush
151
149
 
152
150
  ##
153
151
  # Incr adds the given amount to the counter on the memcached server.
data/lib/dalli/server.rb CHANGED
@@ -17,7 +17,11 @@ module Dalli
17
17
  # times a socket operation may fail before considering the server dead
18
18
  :socket_max_failures => 2,
19
19
  # amount of time to sleep between retries when a failure occurs
20
- :socket_failure_delay => 0.01
20
+ :socket_failure_delay => 0.01,
21
+ # max size of value in bytes (default is 1 MB, can be overriden with "memcached -I <size>")
22
+ :value_max_bytes => 1024 * 1024,
23
+ :username => nil,
24
+ :password => nil,
21
25
  }
22
26
 
23
27
  def initialize(attribs, options = {})
@@ -41,6 +45,11 @@ module Dalli
41
45
  raise
42
46
  rescue Dalli::DalliError
43
47
  raise
48
+ rescue TypeError => ex
49
+ Dalli.logger.error "Application bug: #{ex.class.name}: #{ex.message}"
50
+ Dalli.logger.error "You are trying to cache a Ruby object which cannot be serialized to memcached."
51
+ Dalli.logger.error ex.backtrace.join("\n\t")
52
+ false
44
53
  rescue Exception => ex
45
54
  Dalli.logger.error "Unexpected exception in Dalli: #{ex.class.name}: #{ex.message}"
46
55
  Dalli.logger.error "This is a bug in Dalli, please enter an issue in Github if it does not already exist."
@@ -126,8 +135,6 @@ module Dalli
126
135
  Thread.current[:dalli_multi]
127
136
  end
128
137
 
129
- ONE_MB = 1024 * 1024
130
-
131
138
  def get(key)
132
139
  req = [REQUEST, OPCODES[:get], key.bytesize, 0, 0, 0, key.bytesize, 0, 0, key].pack(FORMAT[:get])
133
140
  write(req)
@@ -255,7 +262,7 @@ module Dalli
255
262
  value = Zlib::Deflate.deflate(value)
256
263
  compressed = true
257
264
  end
258
- raise Dalli::DalliError, "Value too large, memcached can only store 1MB of data per key" if value.bytesize > ONE_MB
265
+ raise Dalli::DalliError, "Value too large, memcached can only store #{@options[:value_max_bytes]} of data per key" if value.bytesize > @options[:value_max_bytes]
259
266
  flags = 0
260
267
  flags |= FLAG_COMPRESSED if compressed
261
268
  flags |= FLAG_MARSHALLED if marshalled
@@ -364,7 +371,7 @@ module Dalli
364
371
  begin
365
372
  @sock = KSocket.open(hostname, port, :timeout => options[:socket_timeout])
366
373
  @version = version # trigger actual connect
367
- sasl_authentication if Dalli::Server.need_auth?
374
+ sasl_authentication if need_auth?
368
375
  up!
369
376
  rescue Dalli::DalliError # SASL auth failure
370
377
  raise
@@ -450,26 +457,19 @@ module Dalli
450
457
  # SASL authentication support for NorthScale
451
458
  #######
452
459
 
453
- def self.need_auth?
454
- ENV['MEMCACHE_USERNAME']
460
+ def need_auth?
461
+ @options[:username] || ENV['MEMCACHE_USERNAME']
455
462
  end
456
463
 
457
- def init_sasl
458
- require 'dalli/sasl/base'
459
- require 'dalli/sasl/plain'
460
- end
461
-
462
464
  def username
463
- ENV['MEMCACHE_USERNAME']
465
+ @options[:username] || ENV['MEMCACHE_USERNAME']
464
466
  end
465
467
 
466
468
  def password
467
- ENV['MEMCACHE_PASSWORD']
469
+ @options[:password] || ENV['MEMCACHE_PASSWORD']
468
470
  end
469
471
 
470
472
  def sasl_authentication
471
- init_sasl if !defined?(::SASL)
472
-
473
473
  Dalli.logger.info { "Dalli/SASL authenticating as #{username}" }
474
474
 
475
475
  # negotiate
@@ -482,12 +482,11 @@ module Dalli
482
482
  content = read(count)
483
483
  return (Dalli.logger.debug("Authentication not required/supported by server")) if status == 0x81
484
484
  mechanisms = content.split(' ')
485
+ raise NotImplementedError, "Dalli only supports the PLAIN authentication mechanism" if !mechanisms.include?('PLAIN')
485
486
 
486
487
  # request
487
- sasl = ::SASL.new(mechanisms)
488
- msg = sasl.start[1]
489
- mechanism = sasl.name
490
- #p [mechanism, msg]
488
+ mechanism = 'PLAIN'
489
+ msg = "\x0#{username}\x0#{password}"
491
490
  req = [REQUEST, OPCODES[:auth_request], mechanism.bytesize, 0, 0, 0, mechanism.bytesize + msg.bytesize, 0, 0, mechanism, msg].pack(FORMAT[:auth_request])
492
491
  write(req)
493
492
 
data/lib/dalli/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dalli
2
- VERSION = '1.0.0'
2
+ VERSION = '1.0.1'
3
3
  end
data/test/test_dalli.rb CHANGED
@@ -329,6 +329,18 @@ class TestDalli < Test::Unit::TestCase
329
329
  end
330
330
  end
331
331
 
332
+ should "handle application marshalling issues" do
333
+ memcached do |dc|
334
+ old = Dalli.logger
335
+ Dalli.logger = Logger.new(nil)
336
+ begin
337
+ assert_equal false, dc.set('a', Proc.new { true })
338
+ ensure
339
+ Dalli.logger = old
340
+ end
341
+ end
342
+ end
343
+
332
344
  context 'with compression' do
333
345
  should 'allow large values' do
334
346
  memcached do |dc|
data/test/test_sasl.rb CHANGED
@@ -6,7 +6,7 @@ class TestSasl < Test::Unit::TestCase
6
6
 
7
7
  context 'without authentication credentials' do
8
8
  setup do
9
- ENV['MEMCACHE_USERNAME'] = 'testuser'
9
+ ENV['MEMCACHE_USERNAME'] = 'foo'
10
10
  ENV['MEMCACHE_PASSWORD'] = 'wrongpwd'
11
11
  end
12
12
 
@@ -24,6 +24,15 @@ class TestSasl < Test::Unit::TestCase
24
24
  end
25
25
  end
26
26
 
27
+ should 'fail SASL authentication with wrong options' do
28
+ memcached(19124, '-S') do |dc|
29
+ dc = Dalli::Client.new('localhost:19124', :username => 'foo', :password => 'wrongpwd')
30
+ assert_raise Dalli::DalliError, /32/ do
31
+ dc.set('abc', 123)
32
+ end
33
+ end
34
+ end
35
+
27
36
  # OSX: Create a SASL user for the memcached application like so:
28
37
  #
29
38
  # saslpasswd2 -a memcached -c testuser
@@ -40,16 +49,31 @@ class TestSasl < Test::Unit::TestCase
40
49
  ENV['MEMCACHE_PASSWORD'] = nil
41
50
  end
42
51
 
43
- should 'support SASL authentication' do
52
+ should 'pass SASL authentication' do
44
53
  memcached(19124, '-S') do |dc|
45
54
  # I get "Dalli::DalliError: Error authenticating: 32" in OSX
46
55
  # but SASL works on Heroku servers. YMMV.
47
56
  assert_equal true, dc.set('abc', 123)
48
57
  assert_equal 123, dc.get('abc')
49
- assert_equal({"localhost:19121"=>{}}, dc.stats)
58
+ results = dc.stats
59
+ assert_equal 1, results.size
60
+ assert_equal 38, results.values.first.size
50
61
  end
51
62
  end
52
63
  end
53
64
 
65
+ should 'pass SASL authentication with options' do
66
+ memcached(19124, '-S') do |dc|
67
+ dc = Dalli::Client.new('localhost:19124', :username => 'testuser', :password => 'testtest')
68
+ # I get "Dalli::DalliError: Error authenticating: 32" in OSX
69
+ # but SASL works on Heroku servers. YMMV.
70
+ assert_equal true, dc.set('abc', 123)
71
+ assert_equal 123, dc.get('abc')
72
+ results = dc.stats
73
+ assert_equal 1, results.size
74
+ assert_equal 38, results.values.first.size
75
+ end
76
+ end
77
+
54
78
  end
55
79
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 0
8
- - 0
9
- version: 1.0.0
8
+ - 1
9
+ version: 1.0.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Mike Perham
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-11-28 00:00:00 -08:00
17
+ date: 2011-01-05 00:00:00 -08:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -89,9 +89,6 @@ files:
89
89
  - lib/dalli/client.rb
90
90
  - lib/dalli/options.rb
91
91
  - lib/dalli/ring.rb
92
- - lib/dalli/sasl/anonymous.rb
93
- - lib/dalli/sasl/base.rb
94
- - lib/dalli/sasl/plain.rb
95
92
  - lib/dalli/server.rb
96
93
  - lib/dalli/socket.rb
97
94
  - lib/dalli/version.rb
@@ -1,14 +0,0 @@
1
- module SASL
2
- ##
3
- # SASL ANONYMOUS where you only send a username that may not get
4
- # evaluated by the server.
5
- #
6
- # RFC 4505:
7
- # http://tools.ietf.org/html/rfc4505
8
- class Anonymous < Mechanism
9
- def start
10
- @state = nil
11
- ['auth', preferences.username.to_s]
12
- end
13
- end
14
- end
@@ -1,89 +0,0 @@
1
- module SASL
2
-
3
- MECHANISMS = {
4
- }
5
-
6
- class Preferences
7
- def authzid
8
- nil
9
- end
10
-
11
- def realm
12
- raise NotImplementedError
13
- end
14
-
15
- def digest_uri
16
- raise NotImplementedError
17
- end
18
-
19
- def username
20
- ENV['MEMCACHE_USERNAME']
21
- end
22
-
23
- def has_password?
24
- false
25
- end
26
-
27
- def allow_plaintext?
28
- false
29
- end
30
-
31
- def password
32
- ENV['MEMCACHE_PASSWORD']
33
- end
34
-
35
- def want_anonymous?
36
- false
37
- end
38
- end
39
-
40
- def SASL.new(mechanisms)
41
- mechanisms.each do |mech|
42
- if MECHANISMS.has_key?(mech)
43
- x = MECHANISMS[mech]
44
- return x.new(mech, Preferences.new)
45
- end
46
- end
47
-
48
- raise NotImplementedError, "No supported mechanisms in #{mechanisms.join(',')}"
49
- end
50
-
51
- ##
52
- # Common functions for mechanisms
53
- #
54
- # Mechanisms implement handling of methods start and receive. They
55
- # return: [message_name, content] or nil where message_name is
56
- # either 'auth' or 'response' and content is either a string which
57
- # may transmitted encoded as Base64 or nil.
58
- class Mechanism
59
- attr_reader :preferences
60
- attr_reader :name
61
-
62
- def initialize(name, preferences)
63
- @name = name
64
- @preferences = preferences
65
- @state = nil
66
- end
67
-
68
- def success?
69
- @state == :success
70
- end
71
- def failure?
72
- @state == :failure
73
- end
74
-
75
- def start
76
- raise NotImplementedError
77
- end
78
-
79
- def receive(message_name, content)
80
- case message_name
81
- when 'success'
82
- @state = :success
83
- when 'failure'
84
- @state = :failure
85
- end
86
- nil
87
- end
88
- end
89
- end
@@ -1,18 +0,0 @@
1
- module SASL
2
- ##
3
- # RFC 4616:
4
- # http://tools.ietf.org/html/rfc4616
5
- class Plain < Mechanism
6
-
7
- def start
8
- @state = nil
9
- message = [preferences.authzid.to_s,
10
- preferences.username,
11
- preferences.password].join("\000")
12
- ['auth', message]
13
- end
14
- end
15
-
16
- MECHANISMS['PLAIN'] = SASL::Plain
17
-
18
- end