Skip to content

Commit f86ac0a

Browse files
authored
Merge pull request #202 from rails/change-database-yml
Change database yml directly and double down on separate DB
2 parents 3e10ec4 + f97ad8b commit f86ac0a

File tree

7 files changed

+288
-116
lines changed

7 files changed

+288
-116
lines changed

README.md

Lines changed: 14 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,76 +4,15 @@ Solid Cache is a database-backed Active Support cache store that let's you keep
44

55
## Installation
66

7-
Solid Cache is configured by default in new Rails 8 applications. But if you're running an earlier version, you can add it manually using `bundle add solid_cache`.
7+
Solid Cache is configured by default in new Rails 8 applications. But if you're running an earlier version, you can add it manually following these steps:
88

9-
#### Cache database configuration
9+
1. `bundle add solid_cache`
10+
2. `bin/rails solid_cache:install`
11+
3. `bin/rails db:migrate`
1012

11-
The default installation of Solid Cache expects a database named `cache` in `database.yml`. It should
12-
have it's own connection pool to avoid mixing cache queries in other transactions.
13+
This will configure Solid Queue as the production cache store, create `config/solid_cache.yml`, and alter `config/database.yml` to include the new cache database.
1314

14-
You can use the primary database for your cache like this:
15-
16-
```yaml
17-
# config/database.yml
18-
production:
19-
primary: &production_primary
20-
...
21-
cache:
22-
<<: *production_primary
23-
```
24-
25-
Or a separate database like this:
26-
27-
```yaml
28-
production:
29-
primary:
30-
...
31-
cache:
32-
database: cache_development
33-
host: 127.0.0.1
34-
migrations_paths: "db/cache/migrate"
35-
```
36-
37-
#### Install Solid Cache
38-
39-
Now, you need to install the necessary migrations and configure the cache store. You can do both at once using the provided generator:
40-
41-
```bash
42-
# If using the primary database
43-
$ bin/rails generate solid_cache:install
44-
45-
# Or if using a dedicated database
46-
$ DATABASE=cache bin/rails generate solid_cache:install
47-
```
48-
49-
This will set solid_cache as the cache store in production, and will copy the optional configuration file and the required migration over to your app.
50-
51-
Alternatively, you can add only the migration to your app:
52-
53-
```bash
54-
# If using the primary database
55-
$ bin/rails generate solid_cache:install:migrations
56-
57-
# Or if using a dedicated database
58-
$ DATABASE=cache bin/rails generate solid_cache:install:migrations
59-
```
60-
61-
And set Solid Cache as your application's cache store backend manually, in your environment config:
62-
63-
```ruby
64-
# config/environments/production.rb
65-
config.cache_store = :solid_cache_store
66-
```
67-
68-
#### Run migrations
69-
70-
Finally, you need to run the migrations:
71-
72-
```bash
73-
$ bin/rails db:migrate
74-
```
75-
76-
### Configuration
15+
## Configuration
7716

7817
Configuration will be read from `config/solid_cache.yml`. You can change the location of the config file by setting the `SOLID_CACHE_CONFIG` env variable.
7918

@@ -101,7 +40,7 @@ production: &production
10140
10241
For the full list of keys for `store_options` see [Cache configuration](#cache-configuration). Any options passed to the cache lookup will overwrite those specified here.
10342

104-
#### Connection configuration
43+
### Connection configuration
10544

10645
You can set one of `database`, `databases` and `connects_to` in the config file. They will be used to configure the cache databases in `SolidCache::Record#connects_to`.
10746

@@ -119,10 +58,9 @@ SolidCache::Record.connects_to shards: { cache_db1: { writing: :cache_db1 }, ca
11958

12059
If `connects_to` is set, it will be passed directly.
12160

122-
If none of these are set, Solid Cache will use the `ActiveRecord::Base` connection pool. This means that cache reads and writes will be part of any wrapping
123-
database transaction.
61+
If none of these are set, Solid Cache will use the `ActiveRecord::Base` connection pool. This means that cache reads and writes will be part of any wrapping database transaction.
12462

125-
#### Engine configuration
63+
### Engine configuration
12664

12765
There are three options that can be set on the engine:
12866

@@ -140,7 +78,7 @@ Rails.application.configure do
14078
end
14179
```
14280

143-
#### Cache configuration
81+
### Cache configuration
14482

14583
Solid Cache supports these options in addition to the standard `ActiveSupport::Cache::Store` options:
14684

@@ -160,7 +98,7 @@ Solid Cache supports these options in addition to the standard `ActiveSupport::C
16098

16199
For more information on cache clusters, see [Sharding the cache](#sharding-the-cache)
162100

163-
### Cache expiry
101+
## Cache expiry
164102

165103
Solid Cache tracks writes to the cache. For every write it increments a counter by 1. Once the counter reaches 50% of the `expiry_batch_size` it adds a task to run on a background thread. That task will:
166104

@@ -176,7 +114,7 @@ Only triggering expiry when we write means that if the cache is idle, the backgr
176114

177115
If you want the cache expiry to be run in a background job instead of a thread, you can set `expiry_method` to `:job`. This will enqueue a `SolidCache::ExpiryJob`.
178116

179-
### Sharding the cache
117+
## Sharding the cache
180118

181119
Solid Cache uses the [Maglev](https://static.googleusercontent.com/media/research.google.com/en//pubs/archive/44824.pdf) consistent hashing scheme to shard the cache across multiple databases.
182120

@@ -207,7 +145,7 @@ production:
207145
databases: [cache_shard1, cache_shard2, cache_shard3]
208146
```
209147

210-
### Enabling encryption
148+
## Enabling encryption
211149

212150
To encrypt the cache values, you can add set the encrypt property.
213151

@@ -245,7 +183,7 @@ config.solid_cache.encryption_context_properties = {
245183
Encryption currently does not work for PostgreSQL, as Rails does not yet support encrypting binary columns for it.
246184
See https://github.com/rails/rails/pull/52650.
247185

248-
### Index size limits
186+
## Index size limits
249187
The Solid Cache migrations try to create an index with 1024 byte entries. If that is too big for your database, you should:
250188

251189
1. Edit the index size in the migration.

db/migrate/20240820123641_create_solid_cache_entries.rb

Lines changed: 0 additions & 29 deletions
This file was deleted.

lib/generators/solid_cache/install/install_generator.rb

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,70 @@
33
class SolidCache::InstallGenerator < Rails::Generators::Base
44
source_root File.expand_path("templates", __dir__)
55

6-
class_option :skip_migrations, type: :boolean, default: nil,
7-
desc: "Skip migrations"
8-
96
def add_rails_cache
10-
if (env_config = Pathname(destination_root).join("config/environments/production.rb")).exist?
11-
gsub_file env_config, /(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_store"
12-
end
7+
gsub_file app_root.join("config/environments/production.rb"),
8+
/(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_store"
139
end
1410

1511
def create_config_solid_cache_yml
1612
template "config/solid_cache.yml"
1713
end
1814

19-
def create_migrations
20-
unless options[:skip_migrations]
21-
rails_command "railties:install:migrations FROM=solid_cache", inline: true
15+
def add_cache_db_to_database_yml
16+
if app_is_using_sqlite?
17+
gsub_file database_yml, /production:\s*<<: \*default.*/m, sqlite_database_config_with_cache
18+
else
19+
gsub_file database_yml, /production:\s*<<: \*default.*/m, generic_database_config_with_cache
2220
end
2321
end
22+
23+
def add_solid_cache_db_schema
24+
template "db/cache_schema.rb"
25+
end
26+
27+
private
28+
def app_root
29+
Pathname.new(destination_root)
30+
end
31+
32+
def database_yml
33+
app_root.join("config/database.yml")
34+
end
35+
36+
def app_is_using_sqlite?
37+
database_yml.read.match?(/production:.*sqlite3/m)
38+
end
39+
40+
def sqlite_database_config_with_cache
41+
<<~YAML
42+
production:
43+
primary:
44+
<<: *default
45+
database: storage/production.sqlite3
46+
cache:
47+
<<: *default
48+
database: storage/production_cache.sqlite3
49+
migrations_paths: db/cache_migrate
50+
YAML
51+
end
52+
53+
def app_name_from_production_database_name
54+
database_yml.read.scan(/database: (\w+)_production/).flatten.first
55+
end
56+
57+
def generic_database_config_with_cache
58+
app_name = app_name_from_production_database_name
59+
60+
<<~YAML
61+
production:
62+
primary: &production_primary
63+
<<: *default
64+
database: #{app_name}_production
65+
username: #{app_name}
66+
password: <%= ENV["#{app_name.upcase}_DATABASE_PASSWORD"] %>
67+
cache:
68+
<<: *production_primary
69+
database: #{app_name}_production_cache
70+
YAML
71+
end
2472
end
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# frozen_string_literal: true
2+
3+
ActiveRecord::Schema[8.0].define(version: 1) do
4+
create_table "solid_cache_entries", force: :cascade do |t|
5+
t.binary "key", limit: 1024, null: false
6+
t.binary "value", limit: 536870912, null: false
7+
t.datetime "created_at", null: false
8+
t.integer "key_hash", limit: 8, null: false
9+
t.integer "byte_size", limit: 4, null: false
10+
t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
11+
t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
12+
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
13+
end
14+
end

test/configs/mysql-database.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# MySQL. Versions 5.5.8 and up are supported.
2+
#
3+
# Install the MySQL driver
4+
# gem install mysql2
5+
#
6+
# Ensure the MySQL gem is defined in your Gemfile
7+
# gem "mysql2"
8+
#
9+
# And be sure to use new-style password hashing:
10+
# https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
11+
#
12+
default: &default
13+
adapter: mysql2
14+
encoding: utf8mb4
15+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
16+
username: root
17+
password:
18+
host: <%= ENV.fetch("DB_HOST") { "127.0.0.1" } %>
19+
20+
development:
21+
<<: *default
22+
database: bongo_development
23+
24+
# Warning: The database defined as "test" will be erased and
25+
# re-generated from your development database when you run "rake".
26+
# Do not set this db to the same as development or production.
27+
test:
28+
<<: *default
29+
database: bongo_test
30+
31+
# As with config/credentials.yml, you never want to store sensitive information,
32+
# like your database password, in your source code. If your source code is
33+
# ever seen by anyone, they now have access to your database.
34+
#
35+
# Instead, provide the password or a full connection URL as an environment
36+
# variable when you boot the app. For example:
37+
#
38+
# DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
39+
#
40+
# If the connection URL is provided in the special DATABASE_URL environment
41+
# variable, Rails will automatically merge its configuration values on top of
42+
# the values provided in this file. Alternatively, you can specify a connection
43+
# URL environment variable explicitly:
44+
#
45+
# production:
46+
# url: <%= ENV["MY_APP_DATABASE_URL"] %>
47+
#
48+
# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
49+
# for a full overview on how database connection configuration can be specified.
50+
#
51+
production:
52+
primary: &production_primary
53+
<<: *default
54+
database: bongo_production
55+
username: bongo
56+
password: <%= ENV["BONGO_DATABASE_PASSWORD"] %>
57+
cache:
58+
<<: *production_primary
59+
database: bongo_production_cache

test/configs/sqlite-database.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# SQLite. Versions 3.8.0 and up are supported.
2+
# gem install sqlite3
3+
#
4+
# Ensure the SQLite 3 gem is defined in your Gemfile
5+
# gem "sqlite3"
6+
#
7+
default: &default
8+
adapter: sqlite3
9+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10+
timeout: 5000
11+
12+
development:
13+
<<: *default
14+
database: storage/development.sqlite3
15+
16+
# Warning: The database defined as "test" will be erased and
17+
# re-generated from your development database when you run "rake".
18+
# Do not set this db to the same as development or production.
19+
test:
20+
<<: *default
21+
database: storage/test.sqlite3
22+
23+
24+
# Store production database in the storage/ directory, which by default
25+
# is mounted as a persistent Docker volume in config/deploy.yml.
26+
production:
27+
<<: *default
28+
database: storage/production.sqlite3

0 commit comments

Comments
 (0)