Is Sinatra Multi Threaded

Is Sinatra multi threaded?

tl;dr Sinatra works well with Threads, but you will probably have to use a different web server.

Sinatra itself does not impose any concurrency model, it does not even handle concurrency. This is done by the Rack handler (web server), like Thin, WEBrick or Passenger. Sinatra itself is thread-safe, meaning that if your Rack handler uses multiple threads to server requests, it works just fine. However, since Ruby 1.8 only supports green threads and Ruby 1.9 has a global VM lock, threads are not that widely used for concurrency, since on both versions, Threads will not run truly in parallel. The will, however, on JRuby or the upcoming Rubinius 2.0 (both alternative Ruby implementations).

Most existing Rack handlers that use threads will use a thread pool in order to reuse threads instead of actually creating a thread for each incoming request, since thread creation is not for free, esp. on 1.9 where threads map 1:1 to native threads. Green threads have far less overhead, which is why fibers, which are basically cooperatively scheduled green threads, as used by the above mentioned sinatra-synchrony, became so popular recently. You should be aware that any network communication will have to go through EventMachine, so you cannot use the mysql gem, for instance, to talk to your database.

Fibers scale well for network intense processing, but fail miserably for heavy computations. You are less likely to run into race conditions, a common pitfall with concurrency, if you use fibers, as they only do a context switch at clearly defined points (with synchony, whenever you wait for IO). There is a third common concurrency model: Processes. You can use preforking server or fire up multiple processes yourself. While this seems a bad idea at first glance, it has some advantages: On the normal Ruby implementation, this is the only way to use all your CPUs simultaniously. And you avoid shared state, so no race conditions by definition. Also, multiprocess apps scale easily over multiple machines. Keep in mind that you can combine multiple process with other concurrency models (evented, cooperative, preemptive).

The choice is mainly made by the server and middleware you use:

  • Multi-Process, non-preforking: Mongrel, Thin, WEBrick, Zbatery
  • Multi-Process, preforking: Unicorn, Rainbows, Passenger
  • Evented (suited for sinatra-synchrony): Thin, Rainbows, Zbatery
  • Threaded: Net::HTTP::Server, Threaded Mongrel, Puma, Rainbows, Zbatery, Thin[1], Phusion Passenger Enterprise >= 4

[1] since Sinatra 1.3.0, Thin will be started in threaded mode, if it is started by Sinatra (i.e. with ruby app.rb, but not with the thin command, nor with rackup).

Sinatra with thin, multi thread do not work

Thin can be multi-threaded, but only if you configure it to be so, by default it is single-threaded (evented). From the answer to the question you linked to:

since Sinatra 1.3.0, Thin will be started in threaded mode, if it is started by Sinatra (i.e. with ruby app.rb, but not with the thin command, nor with rackup).

There doesn’t appear to be a way to get rackup to pass the threaded option through to Thin, so you will need to use either thin start --threaded or ruby my_app.rb to get threading on Thin.

Is Sinatra multi threaded?

tl;dr Sinatra works well with Threads, but you will probably have to use a different web server.

Sinatra itself does not impose any concurrency model, it does not even handle concurrency. This is done by the Rack handler (web server), like Thin, WEBrick or Passenger. Sinatra itself is thread-safe, meaning that if your Rack handler uses multiple threads to server requests, it works just fine. However, since Ruby 1.8 only supports green threads and Ruby 1.9 has a global VM lock, threads are not that widely used for concurrency, since on both versions, Threads will not run truly in parallel. The will, however, on JRuby or the upcoming Rubinius 2.0 (both alternative Ruby implementations).

Most existing Rack handlers that use threads will use a thread pool in order to reuse threads instead of actually creating a thread for each incoming request, since thread creation is not for free, esp. on 1.9 where threads map 1:1 to native threads. Green threads have far less overhead, which is why fibers, which are basically cooperatively scheduled green threads, as used by the above mentioned sinatra-synchrony, became so popular recently. You should be aware that any network communication will have to go through EventMachine, so you cannot use the mysql gem, for instance, to talk to your database.

Fibers scale well for network intense processing, but fail miserably for heavy computations. You are less likely to run into race conditions, a common pitfall with concurrency, if you use fibers, as they only do a context switch at clearly defined points (with synchony, whenever you wait for IO). There is a third common concurrency model: Processes. You can use preforking server or fire up multiple processes yourself. While this seems a bad idea at first glance, it has some advantages: On the normal Ruby implementation, this is the only way to use all your CPUs simultaniously. And you avoid shared state, so no race conditions by definition. Also, multiprocess apps scale easily over multiple machines. Keep in mind that you can combine multiple process with other concurrency models (evented, cooperative, preemptive).

The choice is mainly made by the server and middleware you use:

  • Multi-Process, non-preforking: Mongrel, Thin, WEBrick, Zbatery
  • Multi-Process, preforking: Unicorn, Rainbows, Passenger
  • Evented (suited for sinatra-synchrony): Thin, Rainbows, Zbatery
  • Threaded: Net::HTTP::Server, Threaded Mongrel, Puma, Rainbows, Zbatery, Thin[1], Phusion Passenger Enterprise >= 4

[1] since Sinatra 1.3.0, Thin will be started in threaded mode, if it is started by Sinatra (i.e. with ruby app.rb, but not with the thin command, nor with rackup).

Ruby, Sinatra: Threads are not working

The most likely reason for that blocking behaviour is that you are testing from two different tabs in your web browser, which is blocking the second call until the first call is done. You can easily see that happen with Firefox's network monitor, for instance. Try instead from the command line:

curl http://localhost:3000/freeze &

Understand ruby multi-threading in simple Sinatra app

There is a Global Interpreter Lock in MRI that prevents two threads running together. In your example, your long thread is sleeping (doing nothing) thus MRI can suspend it and run the other thread. IF both threads where taking 100% of cpu time, then you would expect one of the threads to wait for another. If you had JRuby, then you would have each thread taking 100% of CPU time from each core (assuming you have multicore processor), thus your threads wouldnt slow down.

Following article should answer your question in depth: http://ablogaboutcode.com/2012/02/06/the-ruby-global-interpreter-lock/

How do I run a function in only one thread in a multi-threaded Unicorn Sinatra server?

Unicorn is a multi-process (not multi-threaded) Rack server. There's no native support for executing a specific code path only in one of the worker processes.

However, you can work around that by saving the worker number after fork into an environment variable and then checking its value in your application code.

In config/unicorn.rb use

after_worker_ready do |server, worker|
ENV["WORKER_NR"] = worker.nr.to_s
end

In your Sinatra app do:

if unicorn_worker_nr == "0"
scheduler.every "30m", :first => :now do
...
end
end

def unicorn_worker_nr
ENV["WORKER_NR"]
end

Ruby threading/forking with API (Sinatra)

I believe that better way to do it - is to use background jobs. While your worker executes some long-running tasks, it is unavailable for new requests. With background jobs - they do the work, while your web-worker can work with new request.

You can have a look at most popular backgroung jobs gems for ruby as a starting point: resque, delayed_jobs, sidekiq

UPD: Implementation depends on chosen gem, but general scheme will be like this:

# Controller
post '/items' do
# Processing data
MyAwesomeJob.enqueue # here you put your job into queue
head :ok # or whatever
end

In MyAwesomejob you implement your long-runnning task

Next, about Mongoid and background jobs. You should never use complex objects as job arguments. I don't know what kind of task you are implementing, but there is general answer - use simple objects.

For example, instead of using your User as argument, use user_id and then find it inside your job. If you will do it like that, you can use any DB without problems.

sinatra service

You might want to look at async-sinatra.

Why Sinatra request takes EM thread?

Ok, so it seems Sinatra starts Thin in threaded mode by default causing the above behavior.
You can add

set :threaded, false

in your Sinatra configure section and this will prevent the Reactor defering requests on a separate thread, and blocking when under load.

Source1

Source2



Related Topics



Leave a reply



Submit