Skip to content

Commit f3d5aaa

Browse files
committed
Support installing with SQL schema format
As with the schema format, we'll just add a structure file containing the SQL for creating the cache tables as this will work with `bin/rails db:prepare` even if you only run the cache database in production. We bundle the SQL files for MySQL, PostgreSQL, and SQLite - they are generated with `bin/generate_sql_schemas`.
1 parent ac239f3 commit f3d5aaa

File tree

10 files changed

+412
-7
lines changed

10 files changed

+412
-7
lines changed

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ jobs:
3636
ports:
3737
- 6379:6379
3838
postgres:
39-
image: postgres:15.1
39+
image: postgres:17
4040
env:
4141
POSTGRES_HOST_AUTH_METHOD: "trust"
4242
ports:
4343
- 55432:5432
4444
mysql:
45-
image: mysql:8.0.31
45+
image: mysql:9
4646
env:
4747
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
4848
ports:

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ Solid Cache is configured by default in new Rails 8 applications. But if you're
99
1. `bundle add solid_cache`
1010
2. `bin/rails solid_cache:install`
1111

12-
This will configure Solid Cache as the production cache store, create `config/cache.yml`, and create `db/cache_schema.rb`.
12+
This will configure Solid Cache as the production cache store and create `config/cache.yml`.
13+
14+
If your application uses `config.active_record.schema_format = :ruby` (the default), the installer creates `db/cache_schema.rb`.
15+
16+
If your application uses `config.active_record.schema_format = :sql`, the installer creates `db/cache_structure.sql` with the appropriate SQL for your database adapter (PostgreSQL, MySQL, or SQLite).
17+
18+
### Configuring the cache database
1319

1420
You will then have to add the configuration for the cache database in `config/database.yml`. If you're using sqlite, it'll look like this:
1521

@@ -39,7 +45,9 @@ production:
3945
migrations_paths: db/cache_migrate
4046
```
4147
42-
Then run `db:prepare` in production to ensure the database is created and the schema is loaded.
48+
### Finalizing installation
49+
50+
After configuring `database.yml`, run `db:prepare` in production to ensure the cache database is created and the schema is loaded.
4351

4452
## Configuration
4553

bin/generate_structure_sql

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env ruby
2+
# frozen_string_literal: true
3+
4+
# Called by bin/generate_sql_schemas with TARGET_DB environment variable set
5+
6+
require "bundler/setup"
7+
require "fileutils"
8+
9+
# Change to test/dummy directory
10+
dummy_dir = File.expand_path("../test/dummy", __dir__)
11+
Dir.chdir(dummy_dir)
12+
13+
# Load the test/dummy Rails app
14+
APP_PATH = File.expand_path("config/application", Dir.pwd)
15+
require File.expand_path("config/boot", Dir.pwd)
16+
require APP_PATH
17+
Dummy::Application.load_tasks
18+
19+
# Patch Rails to use Docker containers for mysqldump/pg_dump for repeatable results
20+
module DockerDatabaseCommands
21+
private
22+
def run_cmd(cmd, *args, **opts)
23+
case cmd
24+
when "mysqldump"
25+
database, output_file = extract_database_and_output_file(args, file_arg: "--result-file")
26+
docker_cmd = ["docker", "exec", "solid_cache-mysql-1", "mysqldump",
27+
"--set-gtid-purged=OFF", "--no-data", "--routines", "--skip-comments", database]
28+
29+
fail run_cmd_error(cmd, args, "structure_dump") unless Kernel.system(*docker_cmd, out: output_file, **opts)
30+
when "pg_dump"
31+
database, output_file = extract_database_and_output_file(args, file_arg: "--file")
32+
docker_cmd = ["docker", "exec", "solid_cache-postgres-1", "pg_dump",
33+
"-U", "postgres", "--schema-only", "--no-privileges", "--no-owner", database]
34+
35+
fail run_cmd_error(cmd, args) unless Kernel.system(*docker_cmd, out: output_file, **opts)
36+
else
37+
super
38+
end
39+
end
40+
41+
def extract_database_and_output_file(args, file_arg:)
42+
args = args.flatten
43+
database = args.last
44+
output_file = nil
45+
46+
args.each_with_index do |arg, i|
47+
if arg == file_arg
48+
output_file = args[i + 1]
49+
break
50+
end
51+
end
52+
53+
[database, output_file]
54+
end
55+
end
56+
57+
ActiveRecord::Tasks::MySQLDatabaseTasks.prepend(DockerDatabaseCommands)
58+
ActiveRecord::Tasks::PostgreSQLDatabaseTasks.prepend(DockerDatabaseCommands)
59+
60+
OUTPUT_FILES = {
61+
"sqlite" => "cache_structure.sqlite3.sql",
62+
"mysql" => "cache_structure.mysql.sql",
63+
"postgres" => "cache_structure.postgresql.sql"
64+
}
65+
66+
target_db = ENV["TARGET_DB"] || "sqlite"
67+
output_file = OUTPUT_FILES[target_db]
68+
69+
unless output_file
70+
puts " ✗ Error: Unknown TARGET_DB: #{target_db}"
71+
exit 1
72+
end
73+
74+
templates_dir = File.expand_path("../../lib/generators/solid_cache/install/templates/db", Dir.pwd)
75+
output_path = File.join(templates_dir, output_file)
76+
cache_schema_path = File.join(templates_dir, "cache_schema.rb")
77+
78+
begin
79+
puts " Creating database..."
80+
Rake::Task["db:create"].invoke
81+
82+
puts " Loading Ruby schema..."
83+
load cache_schema_path
84+
85+
puts " Dumping structure..."
86+
db_config = ActiveRecord::Base.connection_db_config
87+
ActiveRecord::Tasks::DatabaseTasks.structure_dump(db_config, output_path)
88+
89+
puts " ✓ Saved to #{output_file}"
90+
rescue => e
91+
puts " ✗ Error: #{e.message}"
92+
puts " #{e.backtrace.first(5).join("\n ")}"
93+
exit 1
94+
end

bin/generate_structure_sqls

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env bash
2+
# frozen_string_literal: true
3+
4+
# Generate SQL schema files for PostgreSQL, MySQL, and SQLite
5+
# These files are used when installing SolidCache with schema_format = :sql
6+
7+
set -e
8+
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
11+
12+
export BUNDLE_GEMFILE="$PROJECT_ROOT/gemfiles/rails_8_1.gemfile"
13+
14+
echo "Generating SQL schema files for SolidCache..."
15+
echo ""
16+
17+
for db in sqlite mysql postgres; do
18+
echo "==> Generating schema for $db..."
19+
TARGET_DB=$db "$SCRIPT_DIR/generate_structure_sql"
20+
done
21+
22+
echo ""
23+
echo "✓ All SQL schema files generated successfully!"
24+
echo ""
25+
echo "Generated files:"
26+
echo " - cache_structure.sqlite3.sql"
27+
echo " - cache_structure.mysql.sql"
28+
echo " - cache_structure.postgresql.sql"

docker-compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ volumes:
66

77
services:
88
postgres:
9-
image: postgres:15.1
9+
image: postgres:17
1010
environment:
1111
POSTGRES_HOST_AUTH_METHOD: "trust"
1212
volumes:
1313
- postgres:/var/lib/postgres
1414
ports: [ "127.0.0.1:55432:5432" ]
1515
mysql:
16-
image: mysql:8.0.31
16+
image: mysql:9
1717
environment:
1818
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
1919
volumes:

lib/generators/solid_cache/install/install_generator.rb

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,54 @@ class SolidCache::InstallGenerator < Rails::Generators::Base
55

66
def copy_files
77
template "config/cache.yml"
8-
template "db/cache_schema.rb"
8+
9+
if Rails.application.config.active_record.schema_format == :sql
10+
copy_sql_schema_for_adapter
11+
else
12+
template "db/cache_schema.rb"
13+
end
914
end
1015

1116
def configure_cache_store_adapter
1217
gsub_file Pathname.new(destination_root).join("config/environments/production.rb"),
1318
/(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_store"
1419
end
20+
21+
private
22+
def copy_sql_schema_for_adapter
23+
sql_file = sql_schema_file_for_adapter
24+
25+
if sql_file
26+
copy_file sql_file, "db/cache_structure.sql"
27+
else
28+
raise_unsupported_adapter_error
29+
end
30+
end
31+
32+
def sql_schema_file_for_adapter
33+
case ActiveRecord::Base.connection_db_config.adapter
34+
when "postgresql"
35+
"db/cache_structure.postgresql.sql"
36+
when "mysql2", "trilogy"
37+
"db/cache_structure.mysql.sql"
38+
when "sqlite3"
39+
"db/cache_structure.sqlite3.sql"
40+
else
41+
nil
42+
end
43+
end
44+
45+
def raise_unsupported_adapter_error
46+
error_message = <<~ERROR
47+
48+
ERROR: Unsupported database adapter for SQL schema format: #{adapter.inspect}
49+
50+
SolidCache supports installing for the following Rails adapters with schema_format = :sql:
51+
- PostgreSQL (postgresql)
52+
- MySQL (mysql2, trilogy)
53+
- SQLite (sqlite3)
54+
ERROR
55+
56+
raise error_message
57+
end
1558
end
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
3+
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
4+
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
5+
/*!50503 SET NAMES utf8mb4 */;
6+
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
7+
/*!40103 SET TIME_ZONE='+00:00' */;
8+
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
9+
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
10+
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
11+
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
12+
DROP TABLE IF EXISTS `ar_internal_metadata`;
13+
/*!40101 SET @saved_cs_client = @@character_set_client */;
14+
/*!50503 SET character_set_client = utf8mb4 */;
15+
CREATE TABLE `ar_internal_metadata` (
16+
`key` varchar(255) NOT NULL,
17+
`value` varchar(255) DEFAULT NULL,
18+
`created_at` datetime(6) NOT NULL,
19+
`updated_at` datetime(6) NOT NULL,
20+
PRIMARY KEY (`key`)
21+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
22+
/*!40101 SET character_set_client = @saved_cs_client */;
23+
DROP TABLE IF EXISTS `schema_migrations`;
24+
/*!40101 SET @saved_cs_client = @@character_set_client */;
25+
/*!50503 SET character_set_client = utf8mb4 */;
26+
CREATE TABLE `schema_migrations` (
27+
`version` varchar(255) NOT NULL,
28+
PRIMARY KEY (`version`)
29+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
30+
/*!40101 SET character_set_client = @saved_cs_client */;
31+
DROP TABLE IF EXISTS `solid_cache_entries`;
32+
/*!40101 SET @saved_cs_client = @@character_set_client */;
33+
/*!50503 SET character_set_client = utf8mb4 */;
34+
CREATE TABLE `solid_cache_entries` (
35+
`id` bigint NOT NULL AUTO_INCREMENT,
36+
`key` varbinary(1024) NOT NULL,
37+
`value` longblob NOT NULL,
38+
`created_at` datetime(6) NOT NULL,
39+
`key_hash` bigint NOT NULL,
40+
`byte_size` int NOT NULL,
41+
PRIMARY KEY (`id`),
42+
UNIQUE KEY `index_solid_cache_entries_on_key_hash` (`key_hash`),
43+
KEY `index_solid_cache_entries_on_byte_size` (`byte_size`),
44+
KEY `index_solid_cache_entries_on_key_hash_and_byte_size` (`key_hash`,`byte_size`)
45+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
46+
/*!40101 SET character_set_client = @saved_cs_client */;
47+
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
48+
49+
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
50+
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
51+
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
52+
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
53+
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
54+
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
55+
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
56+

0 commit comments

Comments
 (0)