From d8c9bbc58b7d591d11ff8cef5433b71932051c8e Mon Sep 17 00:00:00 2001 From: schneems Date: Tue, 1 May 2018 15:42:05 -0500 Subject: [PATCH 1/4] Doc Puma::Client --- lib/puma/client.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/puma/client.rb b/lib/puma/client.rb index f0dc872efe..c395555cd4 100644 --- a/lib/puma/client.rb +++ b/lib/puma/client.rb @@ -21,6 +21,17 @@ module Puma class ConnectionError < RuntimeError; end + # An instance of this class represents a unique request from a client. + # For example a web request from a browser or from CURL. This + # + # An instance of `Puma::Client` can be used as if it were an IO object + # for example it is passed into `IO.select` inside of the `Puma::Reactor`. + # This is accomplished by the `to_io` method which gets called on any + # non-IO objects being used with the IO api such as `IO.select. + # + # Instances of this class are responsible for knowing if + # the header and body are fully buffered via the `try_to_finish` method. + # They can be used to "time out" a response via the `timeout_at` reader. class Client include Puma::Const extend Puma::Delegation From 41caf3d666180e77b00a22bc76930a518dea9d1a Mon Sep 17 00:00:00 2001 From: schneems Date: Tue, 1 May 2018 15:42:12 -0500 Subject: [PATCH 2/4] Doc Puma::Cluster --- lib/puma/cluster.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/puma/cluster.rb b/lib/puma/cluster.rb index 8be4fd74ad..4179d5523c 100644 --- a/lib/puma/cluster.rb +++ b/lib/puma/cluster.rb @@ -5,6 +5,17 @@ require 'time' module Puma + # This class is instantiated by the `Puma::Launcher` and used + # to boot and serve a Ruby application when puma "workers" are needed + # i.e. when using multi-processes. For example `$ puma -w 5` + # + # At the core of this class is running an instance of `Puma::Server` which + # gets created via the `start_server` method from the `Puma::Runner` class + # that this inherits from. + # + # An instance of this class will spawn the number of processes passed in + # via the `spawn_workers` method call. Each worker will have it's own + # instance of a `Puma::Server`. class Cluster < Runner WORKER_CHECK_INTERVAL = 5 From c26ebe6101a6ed5f4b61a0ce6c037fde76adc512 Mon Sep 17 00:00:00 2001 From: schneems Date: Tue, 1 May 2018 15:44:08 -0500 Subject: [PATCH 3/4] Doc Puma::Runner, Puma::Single, and Puma::Cluster --- lib/puma/reactor.rb | 3 +++ lib/puma/runner.rb | 3 +++ lib/puma/server.rb | 9 +++++++++ lib/puma/single.rb | 7 +++++++ 4 files changed, 22 insertions(+) diff --git a/lib/puma/reactor.rb b/lib/puma/reactor.rb index d6fb326ad0..7d0c139de6 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -9,6 +9,9 @@ module Puma # If read buffering is not done, and no other read buffering is performed (such as by an application server # such as nginx) then the application would be subject to a slow client attack. # + # Each Puma "worker" process has its own Reactor. For example if you start puma with `$ puma -w 5` then + # it will have 5 workers and each worker will have it's own reactor. + # # For a graphical representation of how the reactor works see [architecture.md](https://github.com/puma/puma/blob/master/docs/architecture.md#connection-pipeline). # # ## Reactor Flow diff --git a/lib/puma/runner.rb b/lib/puma/runner.rb index 8f0fd5ac2f..546db5aee5 100644 --- a/lib/puma/runner.rb +++ b/lib/puma/runner.rb @@ -2,6 +2,9 @@ require 'puma/const' module Puma + # Generic class that is used by `Puma::Cluster` and `Puma::Single` to + # serve requests. This class spawns a new instance of `Puma::Server` via + # a call to `start_server`. class Runner def initialize(cli, events) @launcher = cli diff --git a/lib/puma/server.rb b/lib/puma/server.rb index fe5eb070ed..cead5fbc44 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -23,6 +23,15 @@ module Puma # The HTTP Server itself. Serves out a single Rack app. + # + # This class is used by the `Puma::Single` and `Puma::Cluster` classes + # to generate one or more `Puma::Server` instances capable of handling requests. + # Each Puma process will contain one `Puma::Server` instacne. + # + # The `Puma::Server` instance pulls requests from the socket, adds them to a + # `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`. + # + # Each `Puma::Server` will have one reactor and one thread pool. class Server include Puma::Const diff --git a/lib/puma/single.rb b/lib/puma/single.rb index 1f55605518..1d6c12091a 100644 --- a/lib/puma/single.rb +++ b/lib/puma/single.rb @@ -3,6 +3,13 @@ require 'puma/plugin' module Puma + # This class is instantiated by the `Puma::Launcher` and used + # to boot and serve a Ruby application when no puma "workers" are needed + # i.e. only using "threaded" mode. For example `$ puma -t 1:5` + # + # At the core of this class is running an instance of `Puma::Server` which + # gets created via the `start_server` method from the `Puma::Runner` class + # that this inherits from. class Single < Runner def stats b = @server.backlog || 0 From 2eb627a02a23518a12a82deb752f7dd0e45f0a3c Mon Sep 17 00:00:00 2001 From: schneems Date: Tue, 1 May 2018 15:46:53 -0500 Subject: [PATCH 4/4] Doc Puma::ThreadPool --- lib/puma/thread_pool.rb | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/puma/thread_pool.rb b/lib/puma/thread_pool.rb index 15147cd44a..55cfd0dbe7 100644 --- a/lib/puma/thread_pool.rb +++ b/lib/puma/thread_pool.rb @@ -1,8 +1,17 @@ require 'thread' module Puma - # A simple thread pool management object. + # Internal Docs for A simple thread pool management object. # + # Each Puma "worker" has a thread pool to process requests. + # + # First a connection to a client is made in `Puma::Server`. It is wrapped in a + # `Puma::Client` instance and then passed to the `Puma::Reactor` to ensure + # the whole request is buffered into memory. Once the request is ready, it is passed into + # a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array. + # + # Each thread in the pool has an internal loop where it pulls a request from the `@todo` array + # and proceses it. class ThreadPool class ForceShutdown < RuntimeError end @@ -153,6 +162,32 @@ def <<(work) end end + # This method is used by `Puma::Server` to let the server know when + # the thread pool can pull more requests from the socket and + # pass to the reactor. + # + # The general idea is that the thread pool can only work on a fixed + # number of requests at the same time. If it is already processing that + # number of requests then it is at capacity. If another Puma process has + # spare capacity, then the request can be left on the socket so the other + # worker can pick it up and process it. + # + # For example: if there are 5 threads, but only 4 working on + # requests, this method will not wait and the `Puma::Server` + # can pull a request right away. + # + # If there are 5 threads and all 5 of them are busy, then it will + # pause here, and wait until the `not_full` condition variable is + # signaled, usually this indicates that a request has been processed. + # + # It's important to note that even though the server might accept another + # request, it might not be added to the `@todo` array right away. + # For example if a slow client has only sent a header, but not a body + # then the `@todo` array would stay the same size as the reactor works + # to try to buffer the request. In tha scenario the next call to this + # method would not block and another request would be added into the reactor + # by the server. This would continue until a fully bufferend request + # makes it through the reactor and can then be processed by the thread pool. def wait_until_not_full @mutex.synchronize do while true