diff --git a/lib/puma/const.rb b/lib/puma/const.rb index 667c3088..465555ce 100644 --- a/lib/puma/const.rb +++ b/lib/puma/const.rb @@ -118,6 +118,13 @@ module Puma # sending data back WRITE_TIMEOUT = 10 + # How many requests to attempt inline before sending a client back to + # the reactor to be subject to normal ordering. The idea here is that + # we amortize the cost of going back to the reactor for a well behaved + # but very "greedy" client across 10 requests. This prevents a not + # well behaved client from monopolizing the thread forever. + MAX_FAST_INLINE = 10 + # The original URI requested by the client. REQUEST_URI= 'REQUEST_URI'.freeze REQUEST_PATH = 'REQUEST_PATH'.freeze diff --git a/lib/puma/server.rb b/lib/puma/server.rb index 9f56ccd1..5b5bc335 100644 --- a/lib/puma/server.rb +++ b/lib/puma/server.rb @@ -466,6 +466,8 @@ module Puma clean_thread_locals = @options[:clean_thread_locals] close_socket = true + requests = 0 + while true case handle_request(client, buffer) when false @@ -479,7 +481,19 @@ module Puma ThreadPool.clean_thread_locals if clean_thread_locals - unless client.reset(@status == :run) + requests += 1 + + check_for_more_data = @status == :run + + if requests >= MAX_FAST_INLINE + # This will mean that reset will only try to use the data it already + # has buffered and won't try to read more data. What this means is that + # every client, independent of their request speed, gets treated like a slow + # one once every MAX_FAST_INLINE requests. + check_for_more_data = false + end + + unless client.reset(check_for_more_data) close_socket = false client.set_timeout @persistent_timeout @reactor.add client