rubysinatrapumasequelsequel-gem

Sequel + ADO + Puma is not threading queries


We have a website running in Windows Server 2008 + SQLServer 2008 + Ruby + Sinatra + Sequel/Puma

We've developed an API for our website. When the access points are requested by many clients, at the same time, the clients start getting RequestTimeout exceptions.

I investigated a bit, and I noted that Puma is managing multi threading fine. But Sequel (or any layer below Sequel) is processing one query at time, even if they came from different clients.

In fact, the RequestTimeout exceptions don't occur if I launch many web servers, each one listening one different port, and I assign one different port to each client.

I don't know yet if the problem is Sequel, ADO, ODBC, Windows, SQLServer or what. The truth is that I cannot switch to any other technology (like TinyTDS)

Bellow is a little piece of code with screenshots that you can use to replicate the bug:

require 'sinatra'
require 'sequel'
CONNECTION_STRING =
 "Driver={SQL Server};Server=.\\SQLEXPRESS;" +
 "Trusted_Connection=no;" +
 "Database=pulqui;Uid=;Pwd=;"

DB = Sequel.ado(:conn_string=>CONNECTION_STRING)
enable :sessions
configure { set :server, :puma }
set :public_folder, './public/'
set :bind, '0.0.0.0'
get '/delaybyquery.json' do
 tid = params[:tid].to_s
 begin
 puts "(track-id=#{tid}).starting access point"
 q = "select p1.* from liprofile p1, liprofile p2, liprofile p3, liprofile p4, liprofile p5"
 DB[q].each { |row| # this query should takes a lot of time
 puts row[:id]
 }
 puts "(track-id=#{tid}).done!"
 rescue=>e
 puts "(track-id=#{tid}).error:#{e.to_s}"
 end
end
get '/delaybycode.json' do
 tid = params[:tid].to_s
 begin
 puts "(track-id=#{tid}).starting access point"
 sleep(30)
 puts "(track-id=#{tid}).done!"
 rescue=>e
 puts "(track-id=#{tid}).error:#{e.to_s}"
 end
end 

There are 2 access points in the code above:

  1. delaybyquery.json, that generates a delay by joining the same table 5 times. Note that the table must be about 1000 rows in order to get the query working really slow; and

  2. delaybycode.json, that generates a delay by just calling the ruby sleep function.

Both access points receives a tid (tracking-id) parameter, and both write the outout in the CMD, so you can follow the activity of both process in the same window and check which access point is blocking incoming requests from other browsers.

For testing I'm opening 2 tabs in the same chrome browser. Below are the 2 testings that I'm performing.

Step #1: Run the webserver

c:\source\pulqui>ruby example.app.rb -p 81 I get the output below

Step #2: Testing Delay by Code

I called to this URL: 127.0.0.1:81/delaybycode.json?tid=123 and 5 seconds later I called this other URL 127.0.0.1:81/delaybycode.json?tid=456 Below is the output, where you can see that both calls are working in parallel

click here to see the screenshot

Step #3: Testing Delay by Query

I called to this URL: 127.0.0.1:81/delaybyquery.json?tid=123 and 5 seconds later I called this other URL 127.0.0.1:81/delaybyquery.json?tid=456 Below is the output, where you can see that calls are working 1 at time. Each call to an access point is finishing with a query timeout exception.

click here to see the screenshot


Solution

  • This is almost assuredly due to win32ole (the driver that Sequel's ado adapter uses). It probably doesn't release the GVL during queries, which would cause the issues you are seeing.

    If you cannot switch to TinyTDS or switch to JRuby, then your only option if you want concurrent queries is to run separate webserver processes, and have a reverse proxy server dispatch requests to them.