Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,9 @@ end
# Additional gems for AppDev
gem "active_link_to"
gem "ai-chat"
gem "appdev_support"
gem "awesome_print"
gem "devise"
gem "dotenv"
gem "carrierwave"
gem "cloudinary"
gem "faker"
gem "htmlbeautifier"
gem "http"
gem "kaminari"
gem "pagy"
Expand All @@ -77,6 +72,14 @@ gem "simple_form"
gem "strip_attributes"
gem "validate_url"

group :development, :test do
gem "appdev_support"
gem "awesome_print"
gem "dotenv"
gem "faker"
gem "htmlbeautifier"
end

group :development do
gem "annotaterb"
gem "better_errors"
Expand Down
2 changes: 1 addition & 1 deletion app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title><%= content_for(:title) || "Rails 8 Template" %></title>
<title><%= @page_title || "Default title" %></title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
Expand Down
2 changes: 1 addition & 1 deletion bin/render-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
set -o errexit

# Ruby on Rails
bundle exec rails server
exec bundle exec rails server -e "${RAILS_ENV:-production}" -b 0.0.0.0 -p "${PORT:-3000}"
3 changes: 0 additions & 3 deletions config/database.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,13 +85,10 @@ production:
encoding: utf8
cache:
<<: *primary_production
database: database_production_cache
migrations_paths: db/cache_migrate
queue:
<<: *primary_production
database: database_production_queue
migrations_paths: db/queue_migrate
cable:
<<: *primary_production
database: database_production_cable
migrations_paths: db/cable_migrate
2 changes: 1 addition & 1 deletion config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
config.cache_store = :solid_cache_store

# Replace the default in-process and non-durable queuing backend for Active Job.
config.active_job.queue_adapter = :solid_queue
config.active_job.queue_adapter = ENV.fetch("RAILS_QUEUE_ADAPTER", "solid_queue").to_sym

# Ignore bad email addresses and do not raise email delivery errors.
# Set this to true and configure the email server for immediate delivery to raise delivery errors.
Expand Down
14 changes: 8 additions & 6 deletions config/initializers/appdev_support.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
AppdevSupport.config do |config|
config.action_dispatch = true
config.active_record = true
config.pryrc = :minimal
end
if defined?(AppdevSupport) && (Rails.env.development? || Rails.env.test?)
AppdevSupport.config do |config|
config.action_dispatch = true
config.active_record = true
config.pryrc = :minimal
end

AppdevSupport.init
AppdevSupport.init
end
12 changes: 11 additions & 1 deletion config/puma.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,24 @@
threads_count = ENV.fetch("RAILS_MAX_THREADS", 3)
threads threads_count, threads_count

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
web_concurrency = ENV.fetch("WEB_CONCURRENCY", 0).to_i
workers web_concurrency if web_concurrency > 1

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
port ENV.fetch("PORT", 3000)

# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart

# Run the Solid Queue supervisor inside of Puma for single-server deployments
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
solid_queue_in_puma = ENV.fetch("SOLID_QUEUE_IN_PUMA", "").strip.downcase
plugin :solid_queue if %w[1 true yes].include?(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.
Expand Down
89 changes: 89 additions & 0 deletions perf_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Performance and Memory Audit Recommendations

The following recommendations are designed to reduce memory usage for your Rails 8 application on Render.com, specifically targeting the "Free" plan limits.

## 1. Reduce Puma Workers (`WEB_CONCURRENCY`)

**Issue:** The current configuration in `render.yaml` sets `WEB_CONCURRENCY` to `2`.
```yaml
- key: WEB_CONCURRENCY
value: 2
```
This forces Puma to run in "Clustered Mode" with 2 worker processes. Each worker forks the application, effectively doubling the memory footprint required for the Rails application code. On a memory-constrained environment (like the free plan), this often leads to Out-Of-Memory (OOM) kills.

**Recommendation:**
Set `WEB_CONCURRENCY` to `1` (or effectively 0). This runs Puma in "Single Mode" (threads only). While this limits theoretical maximum throughput on multi-core systems, it drastically reduces memory usage, which is the bottleneck here.

**Action:**
Update `render.yaml`:
```yaml
- key: WEB_CONCURRENCY
value: 1
```

## 2. Tune Memory Allocator (`MALLOC_ARENA_MAX`)

**Issue:** The default glibc memory allocator can create fragmentation in multi-threaded Ruby applications, causing "bloat" where memory is reserved but not used.

**Recommendation:**
Set the `MALLOC_ARENA_MAX` environment variable to `2`. This is a standard optimization for Ruby apps to trade a tiny bit of performance for significantly tighter memory usage.

**Action:**
Add to `render.yaml` environment variables:
```yaml
- key: MALLOC_ARENA_MAX
value: 2
```

## 3. Run Solid Queue in Puma

**Issue:** Rails 8 uses Solid Queue for background jobs. You need a way to process these jobs. Running a separate "worker" service costs money and memory. Running a separate process inside the web service also consumes more RAM.

**Recommendation:**
Use the `solid_queue` Puma plugin to run job processing threads *inside* the web process. This shares the memory of the Rails app between web requests and background jobs.

**Action:**
1. Ensure your `config/puma.rb` has this line (it currently does):
```ruby
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
```
2. Enable it in `render.yaml`:
```yaml
- key: SOLID_QUEUE_IN_PUMA
value: true
```

## 4. Puma Configuration Adjustment

**Issue:** The `config/puma.rb` script sets `workers` based on `WEB_CONCURRENCY` if the variable exists. If you set `WEB_CONCURRENCY` to 1, some configurations might still attempt to use cluster mode (workers = 1), which has higher overhead than single mode (workers = 0).

**Recommendation:**
Update `config/puma.rb` to explicitly only enable workers if the count is greater than 1.

**Action:**
Update `config/puma.rb`:
```ruby
# ...
web_concurrency = ENV.fetch("WEB_CONCURRENCY", 0).to_i
workers web_concurrency if web_concurrency > 1
# ...
```

## Summary of `render.yaml` Changes

```yaml
services:
- type: web
# ...
envVars:
- key: SECRET_KEY_BASE
generateValue: true
- key: DATABASE_URL
sync: false
- key: WEB_CONCURRENCY
value: 1
- key: MALLOC_ARENA_MAX
value: 2
- key: SOLID_QUEUE_IN_PUMA
value: true
```
Loading