Question

I have a Sinatra app, written in modular style, running on Heroku. It uses Redis and I have a limited number (10) of Redis connections. I found that it would often throw errors complaining that it had run out of Redis connections. So I started using connection_pool in the hope that would fix things; a single pool of Redis connections and the app would choose one of those each time, rather than try to create a new connection on each request.

But I'm still getting the same issue. I can do loads of Redis queries on a single query without complaints. But if I reload a single test page, which just does some Redis queries, several times in fairly quick succession, I get the "Redis::CommandError - ERR max number of clients reached" error again.

So I'm assuming, maybe, it's creating a new instance of connection_pool on each request... I don't know. But it's not "pooling" as I would expect it to.

I have this kind of thing:

# myapp.rb
$LOAD_PATH.unshift(File.dirname(__FILE__))
$stdout.sync = true

require 'thin'
require 'myapp/frontend'

MyApp::Frontend.run!

And the Sinatra app:

# myapp/frontend.rb
require 'sinatra/base'
require 'redis'
require 'connection_pool'
require 'uuid'

module MyApp
  class Frontend < Sinatra::Base

    helpers do
      def redis_pool
        @redis_pool ||= ConnectionPool.new(:size => 8, :timeout => 5) do
          redis_uri = URI.parse(ENV['REDISCLOUD_URL'])
          client = ::Redis.new(:host => redis_uri.host,
                               :port => redis_uri.port,
                               :password => redis_uri.password)
        end
      end
    end

    get '/tester/'
      redis_pool.with do |r|
        id = UUID.generate
        r.hset(:user, id, "Some data")
        r.hget(:user, id)
        r.hdel(:user, id)
      end
      p "DONE"
    end
  end
end

The Procfile looks like:

web: ruby myapp.rb

Any ideas? The current site is pretty low traffic, so this should be possible.

Was it helpful?

Solution

A new instance of @redis_pool is created every time a get request for /tester/ is processed because the helper method redis_pool is called every time.

You can use sinatra's settings helper to initialize a redis connection only once:

config do
  redis_uri = URI.parse(ENV['REDISCLOUD_URL'])
  set :redis, Redis.new(:host => redis_uri.host,
                        :port => redis_uri.port,
                        :password => redis_uri.password)
end

Now the each instance of the app has one redis connection that persists for all requests. Access the setting like so

get '/tester/'
  id = UUID.generate
  settings.redis.hset(:user, id, "some data")
  settings.redis.hget(:user, id)
  settings.redis.hdel(:user, id)
  p "DONE"
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top