ActiveSupport::Cache::MemCacheStore unsupported
owentran opened this issue · comments
Hi.
We recently upgraded from the dalli gem to mem_cache_store. This silently broke the rack-throttle gem since it expects the cache to support :has_key?
, :get
, and :set
.
To fix it, we applied this monkey patch...
class RackThrottleMemCacheStore < ActiveSupport::Cache::MemCacheStore
alias_method :has_key?, :exist?
alias_method :get, :read
alias_method :set, :write
end
# Limits :max requests per minute by session id
cache_servers = Rails.application.config.cache_store[1...-1]
cache_options = Rails.application.config.cache_store.last
memcache_store = RackThrottleMemCacheStore.new(cache_servers, cache_options)
Rails.application.config.middleware.use Rack::Throttle::Minute,
max: 60,
key_prefix: :throttle,
cache: memcache_store
I'm not sure if there's a better way but sharing this tidbit for others who may be running this with dalli and then switched to mem_cache_store.
Thanks for this gem!
Owen
Hi Owen! Most people are using rack-throttle for legacy versions of ruby, but if you are able to: https://github.com/kickstarter/rack-attack is much more feature full & maintained!
Thanks FreekingDean. I'll work on migrating with our next sprint and add any gotchas/comments to this issue.
Would love to know if there are large sweeping differences, but I believe it was originally based on this repo.
The switch took about an hour.
safelist
took care of the IP whitelisting- Created a Rack::Attack::Request like the example config to whitelist localhost IP and a few paths
- Able to create three different throttles based on different paths
- Plugs right into Rails.cache.store without any issues
The hardest part was understanding how to log if something was throttled. Here was my solution...
ActiveSupport::Notifications.subscribe("throttle.rack_attack") do |_name, _start, _finish, _request_id, payload|
request = payload[:request]
Rails.logger.error("[rack-attack] throttle=#{request.env["rack.attack.matched"]}, path=#{request.path}, ip=#{request.ip}")
end
Will throw it into production in two weeks and see how it performs.
Works great. Only issue was that the IP method needs to be overwritten if you deal with HTTP_X_FORWARDED_FOR otherwise you'll block all traffic from your load balancers.
def ip
if addr = env['HTTP_X_FORWARDED_FOR']
(addr.split(',').first || env['REMOTE_ADDR']).to_s.strip
else
env['REMOTE_ADDR']
end
end