From 0728a5ad34495b3dbc95a07ccad267e7f887d3ac Mon Sep 17 00:00:00 2001 From: Petrik Date: Sun, 3 May 2026 12:58:37 +0200 Subject: [PATCH 1/2] [rails] Remove thruster for static assets It's a bit difficult to get thruster to work with running both http and https. This reverts commit 6b5fa67c6c8cdfb27235f9aa332075acb23f018c. --- frameworks/rails/Dockerfile | 5 +---- frameworks/rails/Gemfile | 1 - frameworks/rails/Gemfile.lock | 5 ----- frameworks/rails/bin/thrust | 5 ----- 4 files changed, 1 insertion(+), 15 deletions(-) delete mode 100755 frameworks/rails/bin/thrust diff --git a/frameworks/rails/Dockerfile b/frameworks/rails/Dockerfile index 85e624293..450d8a8bc 100644 --- a/frameworks/rails/Dockerfile +++ b/frameworks/rails/Dockerfile @@ -23,7 +23,4 @@ COPY . . EXPOSE 8080 -ENV THRUSTER_TARGET_PORT=8080 -ENV THRUSTER_LOG_REQUESTS=false - -CMD ["bin/thrust", "bin/rails", "s"] +CMD ["bin/rails", "server"] diff --git a/frameworks/rails/Gemfile b/frameworks/rails/Gemfile index cc909ec39..6fcea7fe5 100644 --- a/frameworks/rails/Gemfile +++ b/frameworks/rails/Gemfile @@ -5,4 +5,3 @@ gem 'puma', '~> 7.2' gem 'pg', '~> 1.5' gem 'bootsnap', require: false gem 'connection_pool' -gem 'thruster', require: false diff --git a/frameworks/rails/Gemfile.lock b/frameworks/rails/Gemfile.lock index 8f3e6a90d..fd29f8b6f 100644 --- a/frameworks/rails/Gemfile.lock +++ b/frameworks/rails/Gemfile.lock @@ -188,8 +188,6 @@ GEM securerandom (0.4.1) stringio (3.2.0) thor (1.5.0) - thruster (0.1.20-arm64-darwin) - thruster (0.1.20-x86_64-linux) timeout (0.6.1) tsort (0.2.0) tzinfo (2.0.6) @@ -212,7 +210,6 @@ DEPENDENCIES pg (~> 1.5) puma (~> 7.2) rails (~> 8.0) - thruster CHECKSUMS action_text-trix (2.1.18) sha256=3fdb83f8bff4145d098be283cdd47ac41caf5110bfa6df4695ed7127d7fb3642 @@ -279,8 +276,6 @@ CHECKSUMS securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 - thruster (0.1.20-arm64-darwin) sha256=630cf8c273f562063b92ea5ccd7a721d7ba6130cc422c823727f4744f6d0770e - thruster (0.1.20-x86_64-linux) sha256=d579f252bf67aee6ba6d957e48f566b72e019d7657ba2f267a5db1e4d91d2479 timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b diff --git a/frameworks/rails/bin/thrust b/frameworks/rails/bin/thrust deleted file mode 100755 index 36bde2d83..000000000 --- a/frameworks/rails/bin/thrust +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env ruby -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("thruster", "thrust") From 2c893f76559a0fbdcd9f15d07198c2184f34bb0e Mon Sep 17 00:00:00 2001 From: Petrik Date: Sun, 3 May 2026 10:33:58 +0200 Subject: [PATCH 2/2] [rails] Upgrade Puma to 8 Use Puma 8's new 'mark_as_io_bound' option allow more threads on the IO heavy upload call. It seems only the baseline call benefits from this. --- frameworks/rails/Dockerfile | 1 + frameworks/rails/Gemfile | 2 +- frameworks/rails/Gemfile.lock | 6 +-- frameworks/rails/config/application.rb | 68 ++++++++++++++++---------- frameworks/rails/config/puma.rb | 1 + 5 files changed, 48 insertions(+), 30 deletions(-) diff --git a/frameworks/rails/Dockerfile b/frameworks/rails/Dockerfile index 450d8a8bc..2e7e9e6c0 100644 --- a/frameworks/rails/Dockerfile +++ b/frameworks/rails/Dockerfile @@ -12,6 +12,7 @@ ENV RUBY_MN_THREADS=1 ENV RACK_ENV=production ENV WEB_CONCURRENCY=auto ENV RAILS_MAX_THREADS=3 +ENV MAX_IO_THREADS=5 WORKDIR /app diff --git a/frameworks/rails/Gemfile b/frameworks/rails/Gemfile index 6fcea7fe5..28f8346dc 100644 --- a/frameworks/rails/Gemfile +++ b/frameworks/rails/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gem 'rails', '~> 8.0' -gem 'puma', '~> 7.2' +gem 'puma', '~> 8.0' gem 'pg', '~> 1.5' gem 'bootsnap', require: false gem 'connection_pool' diff --git a/frameworks/rails/Gemfile.lock b/frameworks/rails/Gemfile.lock index fd29f8b6f..71a85dc2c 100644 --- a/frameworks/rails/Gemfile.lock +++ b/frameworks/rails/Gemfile.lock @@ -137,7 +137,7 @@ GEM psych (5.3.1) date stringio - puma (7.2.0) + puma (8.0.1) nio4r (~> 2.0) racc (1.8.1) rack (3.2.6) @@ -208,7 +208,7 @@ DEPENDENCIES bootsnap connection_pool pg (~> 1.5) - puma (~> 7.2) + puma (~> 8.0) rails (~> 8.0) CHECKSUMS @@ -260,7 +260,7 @@ CHECKSUMS prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 - puma (7.2.0) sha256=bf8ef4ab514a4e6d4554cb4326b2004eba5036ae05cf765cfe51aba9706a72a8 + puma (8.0.1) sha256=7b94e50c07655718c1fb8ae41a11fc06c7d61293208b3aa608ff71a46d3ad37c racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f rack (3.2.6) sha256=5ed78e1f73b2e25679bec7d45ee2d4483cc4146eb1be0264fc4d94cb5ef212c2 rack-session (2.1.2) sha256=595434f8c0c3473ae7d7ac56ecda6cc6dfd9d37c0b2b5255330aa1576967ffe8 diff --git a/frameworks/rails/config/application.rb b/frameworks/rails/config/application.rb index 56375b953..f3b1e58d5 100644 --- a/frameworks/rails/config/application.rb +++ b/frameworks/rails/config/application.rb @@ -3,6 +3,46 @@ Bundler.require(*Rails.groups) +# Catch unknown HTTP methods, routing errors, and mark /upload as binary +class MethodGuard + VALID_METHODS = %w[GET HEAD POST PUT DELETE PATCH OPTIONS TRACE].to_set.freeze + + def initialize(app) + @app = app + end + + def call(env) + unless VALID_METHODS.include?(env['REQUEST_METHOD']) + return [405, { 'content-type' => 'text/plain' }, ['Method Not Allowed']] + end + # Mark /upload as binary so Rack skips form parameter parsing + if env['PATH_INFO'] == '/upload' + env['CONTENT_TYPE'] = 'application/octet-stream' + end + @app.call(env) + rescue => e + if e.class.name.include?('UnknownHttpMethod') || e.class.name.include?('RoutingError') + [400, { 'content-type' => 'text/plain' }, ['Bad Request']] + else + raise + end + end +end + +# Threads marked as IO bound are allowed to go over Puma's max thread limit. +class MarkAsIOBoundThreads + def initialize(app) + @app = app + end + + def call(env) + if env['PATH_INFO'].start_with? '/baseline' && env['REQUEST_METHOD'] == 'POST' + env["puma.mark_as_io_bound"].call + end + @app.call(env) + end +end + class BenchmarkApp < Rails::Application config.load_defaults Rails::VERSION::STRING.to_f config.eager_load = true @@ -25,32 +65,8 @@ class BenchmarkApp < Rails::Application # Add gzip support config.middleware.insert 0, Rack::Deflater - - # Catch unknown HTTP methods, routing errors, and mark /upload as binary - config.middleware.insert 0, Class.new { - VALID_METHODS = %w[GET HEAD POST PUT DELETE PATCH OPTIONS TRACE].to_set.freeze - - def initialize(app) - @app = app - end - - def call(env) - unless VALID_METHODS.include?(env['REQUEST_METHOD']) - return [405, { 'content-type' => 'text/plain' }, ['Method Not Allowed']] - end - # Mark /upload as binary so Rack skips form parameter parsing - if env['PATH_INFO'] == '/upload' - env['CONTENT_TYPE'] = 'application/octet-stream' - end - @app.call(env) - rescue => e - if e.class.name.include?('UnknownHttpMethod') || e.class.name.include?('RoutingError') - [400, { 'content-type' => 'text/plain' }, ['Bad Request']] - else - raise - end - end - } + config.middleware.insert 0, MethodGuard + config.middleware.insert 0, MarkAsIOBoundThreads # Silence logging config.logger = nil diff --git a/frameworks/rails/config/puma.rb b/frameworks/rails/config/puma.rb index 65ad56d1c..a1eafb400 100644 --- a/frameworks/rails/config/puma.rb +++ b/frameworks/rails/config/puma.rb @@ -1,5 +1,6 @@ thread_count = ENV.fetch('RAILS_MAX_THREADS', 4).to_i threads thread_count, thread_count +max_io_threads ENV.fetch("MAX_IO_THREADS", 10).to_i port 8080