diff --git a/Gemfile b/Gemfile index 22ea4bd..d794bff 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ gem "importmap-rails" # JavaScript with ESM import maps gem "turbo-rails" # Hotwire page acceleration (SPA-like) gem "stimulus-rails" # Hotwire JavaScript framework gem "solid_cache" # Database-backed Rails.cache -gem "solid_queue" # Database-backed Active Job +gem "solid_queue", "~> 1.3" # Database-backed Active Job — 1.3+ required for solid_queue_mode :async in config/puma.rb gem "solid_cable" # Database-backed Action Cable gem "bootsnap", require: false # Faster boot times via caching gem "thruster", require: false # HTTP caching/compression for Puma diff --git a/Gemfile.lock b/Gemfile.lock index 34b08c6..67a1852 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -86,7 +86,7 @@ GEM erubi (>= 1.0.0) rack (>= 0.9.0) rouge (>= 1.0.0) - bigdecimal (4.1.0) + bigdecimal (4.1.2) bindex (0.8.1) binding_of_caller (2.0.0) debug_inspector (>= 1.2.0) @@ -131,8 +131,9 @@ GEM devise indefinite_article drb (2.2.3) + erb (6.0.4) erubi (1.13.1) - et-orbi (1.2.11) + et-orbi (1.4.0) tzinfo faraday (2.12.2) faraday-net_http (>= 2.0, < 3.5) @@ -154,10 +155,10 @@ GEM ffi-compiler (1.3.2) ffi (>= 1.15.5) rake - fugit (1.11.1) - et-orbi (~> 1, >= 1.2.11) + fugit (1.12.1) + et-orbi (~> 1.4) raabro (~> 1.4) - globalid (1.2.1) + globalid (1.3.0) activesupport (>= 6.1) grade_runner (0.0.16) activesupport (>= 2.3.5) @@ -188,8 +189,9 @@ GEM indefinite_article (0.2.5) activesupport io-console (0.8.2) - irb (1.15.1) + irb (1.18.0) pp (>= 0.6.0) + prism (>= 1.3.0) rdoc (>= 4.0.0) reline (>= 0.4.2) json (2.19.3) @@ -211,7 +213,7 @@ GEM ffi-compiler (~> 1.0) rake (~> 13.0) logger (1.7.0) - loofah (2.24.0) + loofah (2.25.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -223,8 +225,7 @@ GEM matrix (0.4.2) method_source (1.1.0) mini_mime (1.1.5) - mini_portile2 (2.8.9) - minitest (6.0.2) + minitest (6.0.6) drb (~> 2.0) prism (~> 1.5) msgpack (1.8.0) @@ -240,24 +241,21 @@ GEM net-smtp (0.5.1) net-protocol nio4r (2.7.4) - nokogiri (1.18.4) - mini_portile2 (~> 2.8.2) + nokogiri (1.19.3-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.4-aarch64-linux-gnu) + nokogiri (1.19.3-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.18.4-aarch64-linux-musl) + nokogiri (1.19.3-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.18.4-arm-linux-gnu) + nokogiri (1.19.3-arm-linux-musl) racc (~> 1.4) - nokogiri (1.18.4-arm-linux-musl) + nokogiri (1.19.3-arm64-darwin) racc (~> 1.4) - nokogiri (1.18.4-arm64-darwin) + nokogiri (1.19.3-x86_64-darwin) racc (~> 1.4) - nokogiri (1.18.4-x86_64-darwin) + nokogiri (1.19.3-x86_64-linux-gnu) racc (~> 1.4) - nokogiri (1.18.4-x86_64-linux-gnu) - racc (~> 1.4) - nokogiri (1.18.4-x86_64-linux-musl) + nokogiri (1.19.3-x86_64-linux-musl) racc (~> 1.4) octokit (5.6.1) faraday (>= 1, < 3) @@ -293,13 +291,13 @@ GEM nio4r (~> 2.0) raabro (1.4.0) racc (1.8.1) - rack (3.2.5) - rack-session (2.1.0) + rack (3.2.6) + rack-session (2.1.2) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.2.0) rack (>= 1.3) - rackup (2.2.1) + rackup (2.3.1) rack (>= 3) rails (8.0.2) actioncable (= 8.0.2) @@ -319,12 +317,12 @@ GEM actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1) - rails-dom-testing (2.2.0) + rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.2) - loofah (~> 2.21) + rails-html-sanitizer (1.7.0) + loofah (~> 2.25) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) rails_db (2.5.0) activerecord @@ -343,15 +341,17 @@ GEM thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.1) + rake (13.4.2) ransack (4.3.0) activerecord (>= 6.1.5) activesupport (>= 6.1.5) i18n - rdoc (6.12.0) + rdoc (7.2.0) + erb psych (>= 4.0.0) + tsort regexp_parser (2.11.3) - reline (0.6.0) + reline (0.6.3) io-console (~> 0.5) responders (3.1.1) actionpack (>= 5.2) @@ -438,13 +438,13 @@ GEM activejob (>= 7.2) activerecord (>= 7.2) railties (>= 7.2) - solid_queue (1.1.4) + solid_queue (1.4.0) activejob (>= 7.1) activerecord (>= 7.1) concurrent-ruby (>= 1.3.1) - fugit (~> 1.11.0) + fugit (~> 1.11) railties (>= 7.1) - thor (~> 1.3.1) + thor (>= 1.3.1) stimulus-rails (1.3.4) railties (>= 6.0.0) stringio (3.2.0) @@ -453,7 +453,7 @@ GEM unicode-display_width (~> 3.0) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) - thor (1.3.2) + thor (1.5.0) thruster (0.1.12) thruster (0.1.12-aarch64-linux) thruster (0.1.12-arm64-darwin) @@ -490,7 +490,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.2) + zeitwerk (2.8.0) zip (2.0.2) PLATFORMS @@ -541,7 +541,7 @@ DEPENDENCIES shoulda-matchers (~> 7.0) solid_cable solid_cache - solid_queue + solid_queue (~> 1.3) stimulus-rails thruster tsort diff --git a/config/database.yml b/config/database.yml index 5bb10df..d6ae37f 100644 --- a/config/database.yml +++ b/config/database.yml @@ -17,7 +17,12 @@ default: &default encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling - pool: <%= ENV.fetch("DB_POOL") { 5 } %> + # + # Sized for Puma's 3 request threads + Solid Queue's async-mode threads + # (3 worker + dispatcher + scheduler) sharing this pool. The previous + # default of 5 was right at the edge and produced ConnectionTimeoutError + # under any load once SQ threads moved into the Puma process. + pool: <%= ENV.fetch("DB_POOL") { 8 } %> development: diff --git a/config/puma.rb b/config/puma.rb index a248513..2978d42 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -36,6 +36,16 @@ # Run the Solid Queue supervisor inside of Puma for single-server deployments plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] +# Run Solid Queue in async mode: worker/dispatcher/scheduler run as threads +# inside the Puma master process instead of being forked into 4 subprocesses. +# Required on Render's 512MB free plan — fork mode adds ~460MB of process +# overhead (supervisor + 3 children) and pushes the container into OOM-loop. +# Async mode collapses that to ~50MB of additional thread overhead in Puma +# master. Trade-off: less isolation between SQ threads and Puma — a hung or +# leaky SQ thread affects request serving. For low-traffic student projects +# the trade-off is right; revisit if upgrading to a paid plan with more RAM. +solid_queue_mode :async if ENV["SOLID_QUEUE_IN_PUMA"] + # Specify the PID file. Defaults to tmp/pids/server.pid in development. # In other environments, only set the PID file if requested. pidfile ENV["PIDFILE"] if ENV["PIDFILE"]