Sinatra Synchrony with Redis connection pooling - ruby

Is this the correct way of handling Redis connection pooling with Sinatra Synchrony?
My gemfile looks like this:
gem 'sinatra-synchrony'
gem 'hiredis'
gem 'redis'
The sinatra server files use the classic style approach, and generally look like so:
require 'sinatra'
require 'sinatra/synchrony'
require 'redis/connection/hiredis'
require 'redis/connection/synchrony'
require 'redis'
redis = EventMachine::Synchrony::ConnectionPool.new(size: 5) do
Redis.new(path: '/tmp/redis.sock')
end
get / do
# lots of redis reads and writes
end
I then launch multiple instances of the same server application, each under a different port, and use nginx to load balance between them.
Is this the proper solution for connection pooling Redis with Sinatra servers?

Here is a working example, I removed sinatra/sinatra because I don't feel it is needed and I couldn't make it works:
Gemfile:
source :rubygems
gem 'thin'
gem 'rack-fiber_pool'
gem 'hiredis'
gem 'sinatra'
gem 'em-synchrony'
gem 'redis'
config.ru:
require 'rubygems'
require 'bundler/setup'
require 'sinatra/base'
require 'redis/connection/synchrony'
require 'redis'
require 'rack/fiber_pool'
class App < Sinatra::Base
set :template_path, '/tmp'
def initialize
super
#redis = EventMachine::Synchrony::ConnectionPool.new(size: 2) do
Redis.new
end
end
get '/' do
#redis.multi do |r|
r.set('v', "value2")
r.set('v2', '43')
end
#redis.get('v')
end
end
use Rack::FiberPool
use Rack::CommonLogger
run App
And run it with (in the same folder):
bundle
bundle exec thin start
In a real application you would remove the application code from the config.ru file and add a require but at least it gives you a start :)

Related

Sinatra - ActiveRecord::ConnectionNotEstablished: No connection pool for ActiveRecord::Base

I've only been able to find rails answers to this question. Whenever I run rake db:migrate I am getting the aforementioned error. As far as I am aware, I have setup everything correctly so have no idea what's wrong.
config/environment.rb
ENV['SINATRA_ENV'] ||= "development"
require 'bundler/setup'
Bundler.require(:default, ENV['SINATRA_ENV'])
configure :develpoment do
set :database, 'sqlite3:db/database.db'
end
require './app'
Rakefile
require "./config/environment"
require "sinatra/activerecord/rake"

Sinatra Routing - Separate Files

I'm going through a recently released book on Sinatra that demonstrates this way of setting up routes in different files:
# app.rb
require "sinatra"
require "slim"
class Todo < Sinatra::Base
# ...
Dir[File.join(File.dirname(__FILE__), "lib", "*.rb")].each { |lib| require lib }
end
# lib/routes.rb
get "/test" do
"The application is running"
end
# config.ru
require "sinatra"
require "bundler/setup"
Bundler.require
ENV["RACK_ENV"] = "development"
require File.join(File.dirname(__FILE__), "app.rb")
Todo.start!
However, it fails to find the route at http://localhost:4567/test. It would make sense to me that this should work when I run ruby config.ru or bundle exec rackup -p 4567. But coming from Rails development where all this configuration is built-in, I don't have a complete understanding of how everything gets wired together. The server is running on that port and I get the Sinatra doesn't know this ditty 404 page. If I reopen the class as suggested by this SO answer, the /test route is found.
# lib/routes.rb
class Todo < Sinatra::Base
get "/test" do
"The application is running"
end
end
Is there something I'm missing about this suggested way to include routes without reopening the class?
Try ruby app.rb, it should work.
You'll need to restart the webserver to load routes that were added while it was running. Routes are loaded into memory when app.rb is invoked and Sinatra is launched. The route itself looks fine and it appears routes.rb is being imported successfully via Dir[File.join(File.dirname(__FILE__), "lib", "*.rb")].each { |lib| require lib }.
If you're running the server directly through terminal Ctrl+X, Ctrl+C should shut it down, then restart it via rackup config.ru* or ruby app.rb. You may confirm the route is recognized by making a get request through your browser to: http://127.0.0.1:4567/test.
For the rackup config.ru command to work, you can change config.ru to something like:
# config.ru
require './app'
run Sinatra::Application
This is just a deployment convenience.
Edit: #shaun, because Todo extends Sinatra::Base it's fine to use run Todo in your case.
The book suggested Todo.start! to run the application from the config.ru file, but the Sinatra documentation example uses run Sinatra::Application. So I just changed the line from Todo.start! to
run Todo
That seems to work, but I'll have to look into the consequences.

Sidekiq web panel shows forbidden

I have mounted the Sidekiq panel in my Sinatra app like this:
require 'rubygems'
require 'bundler'
require 'sidekiq/web'
env = ENV['RACK_ENV'].to_sym || :development
Bundler.require(:default, :sinatra, env)
disable :run
Encoding.default_external = Encoding::UTF_8
set :environment, env
use Rack::ShowExceptions
use Rack::Session::Pool
use Rack::MethodOverride
Sidekiq::Web.use Rack::Session::Pool
require File.expand_path '../app/my_app.rb', __FILE__
run Rack::URLMap.new("/" => MyApp.new, "/sidekiq" => Sidekiq::Web.new)
This means my app is accessible through / and the sidekiq web panel through /sidekiq.
Now when I try to delete a job, I always get Forbidden. I read here https://github.com/mperham/sidekiq/issues/1289 and here https://github.com/mperham/sidekiq/issues/2487 but wether upgrading to rack-protection > 1.5.1 nor setting a session for Sidekiq::Web has solved the problem so far.
I'm starting my server with rackup using WEBrick, so I think this shouldn't be a server problem.
I'm using sinatra 1.4.2 with sidekiq 3.5.1. Any ideas on how to solve this?
I've found a solution. First I updated from sidekiq 3.4.2 to 4.0.1 and from sinatra 1.4.2 to 1.4.6. No problems so far.
The problem with the Forbidden message was a missing authenticity token for sidekiq's web panel. By adding the following lines, it worked:
require 'rubygems'
require 'bundler'
require 'sidekiq/web'
#####################################
# added a require for rack/protection
require 'rack/protection'
#####################################
env = ENV['RACK_ENV'].to_sym || :development
Bundler.require(:default, :sinatra, env)
disable :run
Encoding.default_external = Encoding::UTF_8
set :environment, env
use Rack::ShowExceptions
use Rack::Session::Pool
use Rack::MethodOverride
#####################################
# tell sinatra to use rack's protection methods
use Rack::Protection
#####################################
require File.expand_path '../app/my_app.rb', __FILE__
run Rack::URLMap.new("/" => MyApp.new, "/sidekiq" => Sidekiq::Web.new)
Also have a look at https://github.com/sinatra/rack-protection where all the protection methods are listed.
You may need to add this to application.rb (or an initializer like config/initializers/sidekiq.rb):
Sidekiq::Web.instance_variable_get(:#middleware).delete_if do |middleware|
middleware.first == Rack::Protection
end
That comes from a recent commit, but it's only applied to production and staging environments.
Detailed explanation about this problem.

No perfomance gains from using em-http-request

I'm trying to understand how to use various non-blocking IO libraries in Ruby and made a simple app for testing using Sinatra,
# proxy.rb
require 'bundler/setup'
require 'sinatra/base'
require 'sinatra/synchrony'
require 'faraday'
class ProxyApp < Sinatra::Base
register Sinatra::Synchrony
get "/proxy" do
conn = Faraday.new("http://mirror.yandex.ru") do |faraday|
faraday.use Faraday::Adapter::EMSynchrony
end
conn.get "/ubuntu-releases/precise/ubuntu-12.04.1-alternate-i386.iso"
"Hello, world"
end
get "/" do
"Hello, world"
end
end
As far as I understand, downloading a file using non-blocking IO should allow other requests to execute, but it doesn't - if I'm using ab to open /proxy path (I'm using Thin as an app server), request to / takes a very long time. Am I doing something wrong?
Sinatra::Synchrony? Why?
config.ru:
require File.join Dir.pwd, 'proxy.rb'
run Proxy
Gemfile:
source 'https://rubygems.org'
gem 'sinatra'
gem 'thin'
gem 'faraday'
gem 'em-synchrony'
gem 'em-http-request'
gem 'rack-fiber_pool'
proxy.rb:
require 'bundler'
Bundler.require
class Proxy < Sinatra::Base
use Rack::FiberPool
get "/proxy" do
conn = Faraday.new("http://mirror.yandex.ru") do |faraday|
faraday.use Faraday::Adapter::EMSynchrony
end
conn.get "/ubuntu-releases/precise/ubuntu-12.04.1-alternate-i386.iso"
"Hello, world"
end
get "/" do
"Hello, world"
end
end
Start:
thin start -d
wget localhost:3000/proxy
In another terminal:
wget localhost:3000/
The reply is immediate for /, no matter how many requests to /proxy you do in parrallel.

issues finding public folder when requiring sinatra/base

I have found that in my Sinatra app, when I require 'sinatra', I can access my public folder as expected, but when I require 'sinatra/base' I can't. Here is my relevant code (which works until I change to /base):
config.ru
root = ::File.dirname(__FILE__)
require ::File.join( root, 'app' )
run MyApp.new
app.rb
require 'sinatra'
require 'sinatra/namespace'
require 'haml'
class MyApp < Sinatra::Application
# ...
end
require_relative 'models/init'
require_relative 'helpers/init'
require_relative 'routes/init'
script.haml
%script(type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js")
%script(type="text/javascript" src="/js/table.js")
%link(rel="stylesheet" type="text/css" href="/css/table.css")
And yes, I have the correct directory structure in place. Like I said, it works using require sinatra. Anyone know why this is occurring and what I can do to fix it?
Requiring Sinatra::Base does not set any of the default configuration settings that requiring Sinatra does. You'll need to set :public_folder ... to a suitable value yourself, e.g:
set :public_folder, 'public'

Resources