How Rails handle multiple requests, and what are process, thread, semaphore and mutex ?

·

3 min read

Behaviour of Ruby on Rails when receiving multiple requests

When using puma.rb in Ruby on Rails, a master process and worker process (which has multiple threads) are created. When a request comes in, Puma's load balancer sends the request to the worker process and the processing is executed in the thread of that worker process. If a second request comes in while the first request is being processed, it is processed in a different thread. This allows multiple requests to be processed in parallel.

Issues and considerations with parallel processing

When multiple requests for image processing come in, multiple threads may simultaneously execute saving to memory. If saving to memory is done simultaneously from a large number of threads, memory overflow may occur. Mutexes and semaphores can be used to address this issue and race conditions, but caution is needed when implementing them to avoid deadlocks.

Process

An executing program that has memory space and includes a stack, an instruction pointer, and a register state. A single process has multiple threads, and by default, when using Ruby on Rails, five threads are created in the worker process.

Master process

The process that is the origin of the worker process when it is created. The master process does not process requests. Instead, the worker process forks the master process and handles the request processing. After the worker process is created, it monitors them.

Worker process

A process created by copying from the master process. When using puma.rb in Ruby on Rails, one master process and multiple worker processes are created (only one worker process in dev). This worker process executes the application and has multiple threads.

Thread

One unit of program execution. When there are multiple threads in a process, they share memory space with the other threads in the process.

Semaphore

A method of exclusive processing. It limits the number of resources (such as memory) that can be used simultaneously.

When using a semaphore, it behaves as follows:

  1. Define the semaphore and set the upper limit of concurrent use.

  2. Subtract 1 from the semaphore immediately before using the resource.

  3. When the resource is finished being used, add 1 to the semaphore.

  4. When the semaphore is 0 (when the resource should not be used by other programs), other programs wait until the semaphore is incremented to 1 or more. For example, in an image hosting service, the semaphore is set to 4, and no more than four image editing tasks (which consume a lot of memory) can use memory at the same time.

Mutex

A method of exclusive processing. While one task is using a resource, other tasks cannot access that resource. In other words, it behaves the same way as when the semaphore number is only 1 or 0.

Points to note when using Mutex and Semaphore in Ruby on Rails:

While Mutex and Semaphore can be defined within the Ruby on Rails application, when access needs to be controlled from all worker processes, they must be defined before the server is started (in puma.rb, for example).

Reason:

Files such as application.rb, as well as models and controllers, can be used to configure the Rails application instance. However, if a global variable is defined in these files, that global variable will only be accessible within the same worker process. Defining them before puma.rb or earlier allows all worker processes to use the same semaphore.