Skip to content

Commit

Permalink
Make Solid Cache the default for new Rails apps
Browse files Browse the repository at this point in the history
The defaults for the cache are pulled in from the [template](https://github.com/rails/solid_cache/blob/87fd0e9611237b5608b5e0f4197aec74fe333ca4/lib/generators/solid_cache/install/templates/config/solid_cache.yml.tt)
in Solid Cache. Currently 1 week max age and 256MB max size.

Right now this sets up Solid Cache to be used in development and
production - maybe we'd want to stick with a memory cache in development?

When using Sqlite, we'll use a separate database for the cache, with
the migrations for that db going into `db/cache/migrate`. We then need
to adjust the db change task to move the migrations if required.

For all databases we'll use a separate connection pool for the database
even if it points to the primary database. This stops cache queries
from being affected by primary database transactions.

As a result the `config/database.yml` templates have been converted to
the three-tier format. We could keep the two-tier format when not using
Solid Cache, but the templates would start to get messy.
  • Loading branch information
djmb committed Aug 21, 2024
1 parent 888d284 commit 488a049
Show file tree
Hide file tree
Showing 14 changed files with 380 additions and 141 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ gem "cgi", ">= 0.3.6", require: false

gem "prism"

gem "solid_cache", ">= 1.0.0"

group :lint do
gem "syntax_tree", "6.1.1", require: false
end
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,10 @@ GEM
rake
serverengine (~> 2.0.5)
thor
solid_cache (1.0.0)
activejob (>= 7.2)
activerecord (>= 7.2)
railties (>= 7.2)
sorted_set (1.0.3)
rbtree
set (~> 1.0)
Expand Down Expand Up @@ -689,6 +693,7 @@ DEPENDENCIES
selenium-webdriver (>= 4.20.0)
sidekiq
sneakers
solid_cache (>= 1.0.0)
sprockets-rails (>= 2.0.0)
sqlite3 (>= 2.0)
stackprof
Expand Down
15 changes: 15 additions & 0 deletions railties/lib/rails/generators/app_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ def self.add_shared_options_for(name)
class_option :skip_kamal, type: :boolean, default: false,
desc: "Skip Kamal setup"

class_option :skip_solid_cache, type: :boolean, default: false,
desc: "Skip Solid Cache setup"

class_option :dev, type: :boolean, default: nil,
desc: "Set up the #{name} with Gemfile pointing to your Rails checkout"

Expand Down Expand Up @@ -158,6 +161,7 @@ def gemfile_entries # :doc:
css_gemfile_entry,
jbuilder_gemfile_entry,
cable_gemfile_entry,
solid_cache_gemfile_entry,
].flatten.compact.select(&@gem_filter)
end

Expand Down Expand Up @@ -392,6 +396,10 @@ def skip_asset_pipeline? # :doc:
options[:skip_asset_pipeline]
end

def skip_solid_cache?
options[:skip_solid_cache]
end

def skip_sprockets?
skip_asset_pipeline? || options[:asset_pipeline] != "sprockets"
end
Expand Down Expand Up @@ -640,6 +648,13 @@ def cable_gemfile_entry
GemfileEntry.new("redis", ">= 4.0.1", comment, {}, true)
end

def solid_cache_gemfile_entry
return if options[:skip_solid_cache]

comment = "Use Solid Cache for caching (http://github.com/rails/solid_cache)"
GemfileEntry.new("solid_cache", ">= 1.0.0", comment, {}, false)
end

def bundle_command(command, env = {})
say_status :run, "bundle #{command}"

Expand Down
11 changes: 11 additions & 0 deletions railties/lib/rails/generators/rails/app/app_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class AppGenerator < AppBase
:skip_hotwire,
:skip_javascript,
:skip_jbuilder,
:skip_solid_cache,
:skip_system_test,
],
api: [
Expand Down Expand Up @@ -474,6 +475,16 @@ def create_devcontainer_files
build(:devcontainer)
end

def install_solid_cache
unless skip_solid_cache?
if options[:database] == "sqlite3"
rails_command "DATABASE=cache solid_cache:install", inline: false, capture: options[:quiet]
else
rails_command "solid_cache:install", inline: false, capture: options[:quiet]
end
end
end

def delete_app_assets_if_api_option
if options[:api]
remove_dir "app/assets"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,25 @@ default: &default
<% end -%>

development:
<<: *default
database: <%= app_name %>_development
primary:<% unless options[:skip_solid_cache] %> &development_primary<% end %>
<<: *default
database: <%= app_name %>_development
<% unless options[:skip_solid_cache] -%>
cache:
<<: *development_primary
<% end -%>

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: <%= app_name %>_test
primary:<% unless options[:skip_solid_cache] %> &test_primary<% end %>
<<: *default
database: <%= app_name %>_test
<% unless options[:skip_solid_cache] -%>
cache:
<<: *test_primary
<% end -%>

# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
Expand All @@ -53,7 +63,12 @@ test:
# for a full overview on how database connection configuration can be specified.
#
production:
<<: *default
database: <%= app_name %>_production
username: <%= app_name %>
password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
primary:<% unless options[:skip_solid_cache] %> &production_primary<% end %>
<<: *default
database: <%= app_name %>_production
username: <%= app_name %>
password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
<% unless options[:skip_solid_cache] -%>
cache:
<<: *production_primary
<% end -%>
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +27,52 @@ default: &default
<% end %>

development:
<<: *default
database: <%= app_name %>_development
primary:<% unless options[:skip_solid_cache] %> &development_primary<% end %>
<<: *default
database: <%= app_name %>_development

# The specified database role being used to connect to PostgreSQL.
# To create additional roles in PostgreSQL see `$ createuser --help`.
# When left blank, PostgreSQL will use the default role. This is
# the same name as the operating system user running Rails.
#username: <%= app_name %>
# The specified database role being used to connect to PostgreSQL.
# To create additional roles in PostgreSQL see `$ createuser --help`.
# When left blank, PostgreSQL will use the default role. This is
# the same name as the operating system user running Rails.
#username: <%= app_name %>

# The password associated with the PostgreSQL role (username).
#password:
# The password associated with the PostgreSQL role (username).
#password:

# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost
# Connect on a TCP socket. Omitted by default since the client uses a
# domain socket that doesn't need configuration. Windows does not have
# domain sockets, so uncomment these lines.
#host: localhost

# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432
# The TCP port the server listens on. Defaults to 5432.
# If your server runs on a different port number, change accordingly.
#port: 5432

# Schema search path. The server defaults to $user,public
#schema_search_path: myapp,sharedapp,public
# Schema search path. The server defaults to $user,public
#schema_search_path: myapp,sharedapp,public

# Minimum log levels, in increasing order:
# debug5, debug4, debug3, debug2, debug1,
# log, notice, warning, error, fatal, and panic
# Defaults to warning.
#min_messages: notice
# Minimum log levels, in increasing order:
# debug5, debug4, debug3, debug2, debug1,
# log, notice, warning, error, fatal, and panic
# Defaults to warning.
#min_messages: notice
<% unless options[:skip_solid_cache] -%>
cache:
<<: *development_primary
<% end -%>

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: <%= app_name %>_test
primary: <% unless options[:skip_solid_cache] %> &test_primary<% end %>
<<: *default
database: <%= app_name %>_test
<% unless options[:skip_solid_cache] -%>
cache:
<<: *test_primary
<% end -%>

# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
Expand All @@ -85,7 +95,12 @@ test:
# for a full overview on how database connection configuration can be specified.
#
production:
<<: *default
database: <%= app_name %>_production
username: <%= app_name %>
password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
primary:<% unless options[:skip_solid_cache] %> &production_primary<% end %>
<<: *default
database: <%= app_name %>_production
username: <%= app_name %>
password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
<% unless options[:skip_solid_cache] -%>
cache:
<<: *production_primary
<% end -%>
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,29 @@ default: &default
timeout: 5000

development:
<<: *default
database: storage/development.sqlite3
primary:
<<: *default
database: storage/development.sqlite3
<% unless options[:skip_solid_cache] -%>
cache:
<<: *default
database: storage/development_cache.sqlite3
migrations_paths: "db/cache/migrate"
<%- end -%>

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: storage/test.sqlite3

primary:
<<: *default
database: storage/test.sqlite3
<% unless options[:skip_solid_cache] -%>
cache:
<<: *default
database: storage/test_cache.sqlite3
migrations_paths: "db/cache/migrate"
<%- end -%>

<%- if options.skip_kamal? -%>
# SQLite3 write its data on the local filesystem, as such it requires
Expand All @@ -29,12 +42,26 @@ test:
# Similarly, if you deploy your application as a Docker container, you must
# ensure the database is located in a persisted volume.
production:
<<: *default
# database: path/to/persistent/storage/production.sqlite3
primary:
<<: *default
# database: path/to/persistent/storage/production.sqlite3
<% unless options[:skip_solid_cache] -%>
cache:
<<: *default
# database: path/to/persistent/storage/production_cache.sqlite3
# migrations_paths: "db/cache/migrate"
<%- end -%>
<%- else -%>
# Store production database in the storage/ directory, which by default
# is mounted as a persistent Docker volume in config/deploy.yml.
production:
<<: *default
database: storage/production.sqlite3
<%- end -%>
primary:
<<: *default
database: storage/production.sqlite3
<% unless options[:skip_solid_cache] -%>
cache:
<<: *default
database: storage/production_cache.sqlite3
migrations_paths: "db/cache/migrate"
<%- end -%>
<%- end -%>
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,25 @@ default: &default
<% end -%>

development:
<<: *default
database: <%= app_name %>_development
primary:<% unless options[:skip_solid_cache] %> &development_primary<% end %>
<<: *default
database: <%= app_name %>_development
<% unless options[:skip_solid_cache] -%>
cache:
<<: *development_primary
<% end -%>

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: <%= app_name %>_test
primary:<% unless options[:skip_solid_cache] %> &test_primary<% end %>
<<: *default
database: <%= app_name %>_test
<% unless options[:skip_solid_cache] -%>
cache:
<<: *test_primary
<% end -%>

# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
Expand All @@ -53,7 +63,12 @@ test:
# for a full overview on how database connection configuration can be specified.
#
production:
<<: *default
database: <%= app_name %>_production
username: <%= app_name %>
password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
primary:<% unless options[:skip_solid_cache] %> &production_primary<% end %>
<<: *default
database: <%= app_name %>_production
username: <%= app_name %>
password: <%%= ENV["<%= app_name.upcase %>_DATABASE_PASSWORD"] %>
<% unless options[:skip_solid_cache] -%>
cache:
<<: *production_primary
<% end -%>
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ Rails.application.configure do
end

# Change this to :null_store to avoid any caching
<%- if options[:skip_solid_cache] -%>
config.cache_store = :memory_store
<%- else -%>
config.cache_store = :solid_cache_store
<% end %>
<%- unless skip_active_storage? -%>

# Store uploaded files on the local file system (see config/storage.yml for options).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,13 @@ Rails.application.configure do
# want to log everything, set the level to "debug".
config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info")

<%- if options[:skip_solid_cache] -%>
# Use a different cache store in production.
# config.cache_store = :mem_cache_store
<%- else -%>
# Cache with Solid Cache
config.cache_store = :solid_cache_store
<% end %>

<%- unless options[:skip_active_job] -%>
# Use a real queuing backend for Active Job (and separate queues per environment).
Expand Down
Loading

0 comments on commit 488a049

Please sign in to comment.