Skip to content

Commit

Permalink
flag to enable exception bubbling
Browse files Browse the repository at this point in the history
Add :bang option to both cache and session stores, which propagates exceptions (e.g. if all memcache servers are down) instead of silently hiding errors.
  • Loading branch information
pitr committed Apr 17, 2012
1 parent 37f6a90 commit fb2e793
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -86,6 +86,8 @@ To use Dalli for Rails session storage, in `config/initializers/session_store.rb
require 'action_dispatch/middleware/session/dalli_store'
Rails.application.config.session_store :dalli_store, :memcache_server => ['host1', 'host2'], :namespace => 'sessions', :key => '_foundation_session', :expire_after => 30.minutes

Both cache and session stores support `:bang` parameter, which propagates exceptions (e.g. if all memcache servers are down) instead of silently hiding errors.

Dalli does not support Rails 2.x any longer.


Expand Down
5 changes: 5 additions & 0 deletions lib/action_dispatch/middleware/session/dalli_store.rb
Expand Up @@ -20,6 +20,8 @@ def initialize(app, options = {})
end
@namespace = @default_options[:namespace]

@bang = !!@default_options[:bang]

super
end

Expand Down Expand Up @@ -49,6 +51,7 @@ def set_session(env, sid, session_data, options = nil)
sid
rescue Dalli::DalliError
Rails.logger.warn("Session::DalliStore#set: #{$!.message}")
raise if @bang
false
end

Expand All @@ -57,6 +60,7 @@ def destroy_session(env, session_id, options)
@pool.delete(session_id)
rescue Dalli::DalliError
Rails.logger.warn("Session::DalliStore#destroy_session: #{$!.message}")
raise if @bang
end
return nil if options[:drop]
generate_sid
Expand All @@ -68,6 +72,7 @@ def destroy(env)
end
rescue Dalli::DalliError
Rails.logger.warn("Session::DalliStore#destroy: #{$!.message}")
raise if @bang
false
end

Expand Down
6 changes: 6 additions & 0 deletions lib/active_support/cache/dalli_store.rb
Expand Up @@ -21,6 +21,7 @@ def initialize(*addresses)
addresses = addresses.flatten
options = addresses.extract_options!
options[:compress] ||= options[:compression]
@bang = !!options[:bang]
addresses << 'localhost:11211' if addresses.empty?
@data = Dalli::Client.new(addresses, options)
end
Expand Down Expand Up @@ -107,6 +108,7 @@ def increment(name, amount = 1, options=nil)
end
rescue Dalli::DalliError => e
logger.error("DalliError: #{e.message}") if logger
raise if @bang
nil
end

Expand All @@ -124,6 +126,7 @@ def decrement(name, amount = 1, options=nil)
end
rescue Dalli::DalliError => e
logger.error("DalliError: #{e.message}") if logger
raise if @bang
nil
end

Expand Down Expand Up @@ -151,6 +154,7 @@ def read_entry(key, options) # :nodoc:
entry.is_a?(ActiveSupport::Cache::Entry) ? entry.value : entry
rescue Dalli::DalliError => e
logger.error("DalliError: #{e.message}") if logger
raise if @bang
nil
end

Expand All @@ -161,6 +165,7 @@ def write_entry(key, value, options) # :nodoc:
@data.send(method, escape(key), value, expires_in, options)
rescue Dalli::DalliError => e
logger.error("DalliError: #{e.message}") if logger
raise if @bang
false
end

Expand All @@ -169,6 +174,7 @@ def delete_entry(key, options) # :nodoc:
@data.delete(escape(key))
rescue Dalli::DalliError => e
logger.error("DalliError: #{e.message}") if logger
raise if @bang
false
end

Expand Down
29 changes: 29 additions & 0 deletions test/test_active_support.rb
Expand Up @@ -160,6 +160,35 @@
end
end
end

should 'respect bang option' do
with_activesupport do
memcached(29125) do
@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:29125')
@dalli.write 'foo', 'bar'
assert_equal @dalli.read('foo'), 'bar'

memcached_kill(29125)

assert_equal @dalli.read('foo'), nil

@dalli = ActiveSupport::Cache.lookup_store(:dalli_store, 'localhost:29125', :bang => true)

exception = [Dalli::RingError, :message => "No server available"]

assert_raises(*exception) { @dalli.read 'foo' }
assert_raises(*exception) { @dalli.read 'foo', :raw => true }
assert_raises(*exception) { @dalli.write 'foo', 'bar' }
assert_raises(*exception) { @dalli.exist? 'foo' }
assert_raises(*exception) { @dalli.increment 'foo' }
assert_raises(*exception) { @dalli.decrement 'foo' }
assert_raises(*exception) { @dalli.delete 'foo' }
assert_equal @dalli.read_multi('foo', 'bar'), {}
assert_raises(*exception) { @dalli.delete 'foo' }
assert_raises(*exception) { @dalli.fetch('foo') { 42 } }
end
end
end
end

should 'handle crazy characters from far-away lands' do
Expand Down
38 changes: 38 additions & 0 deletions test/test_session_store.rb
Expand Up @@ -12,6 +12,8 @@ def inspect
end

class TestSessionStore < ActionController::IntegrationTest
include MemcachedMock::Helper

class TestController < ActionController::Base
def no_session_access
head :ok
Expand Down Expand Up @@ -201,6 +203,42 @@ def test_expires_in
assert_equal 'foo: nil', response.body
end
end

def test_without_bang_option
memcached(29125) do
with_test_route_set(:memcache_server => '127.0.0.1:29125') do
get '/set_session_value'
assert_response :success

get '/get_session_value'
assert_response :success
assert_equal 'foo: "bar"', response.body

memcached_kill(29125)

get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
end
end
end

def test_with_bang_option
memcached(29125) do
with_test_route_set(:memcache_server => '127.0.0.1:29125', :bang => true) do
get '/set_session_value'
assert_response :success

memcached_kill(29125)

exception = [Dalli::RingError, :message => "No server available"]

assert_raises(*exception) { get '/get_session_value' }
assert_raises(*exception) { get '/set_session_value' }
assert_raises(*exception) { get '/call_reset_session' }
end
end
end
rescue LoadError, RuntimeError
$stderr.puts "Skipping SessionStore tests. Start memcached and try again: #{$!.message}"
end
Expand Down

0 comments on commit fb2e793

Please sign in to comment.