Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ jobs:
ports:
- 6379:6379
postgres:
image: postgres:15.1
image: postgres:17
env:
POSTGRES_HOST_AUTH_METHOD: "trust"
ports:
- 55432:5432
mysql:
image: mysql:8.0.31
image: mysql:9
env:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
ports:
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@ Solid Cache is configured by default in new Rails 8 applications. But if you're
1. `bundle add solid_cache`
2. `bin/rails solid_cache:install`

This will configure Solid Cache as the production cache store, create `config/cache.yml`, and create `db/cache_schema.rb`.
This will configure Solid Cache as the production cache store and create `config/cache.yml`.

If your application uses `config.active_record.schema_format = :ruby` (the default), the installer creates `db/cache_schema.rb`.

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).

### Configuring the cache database

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:

Expand Down Expand Up @@ -39,7 +45,9 @@ production:
migrations_paths: db/cache_migrate
```

Then run `db:prepare` in production to ensure the database is created and the schema is loaded.
### Finalizing installation

After configuring `database.yml`, run `db:prepare` in production to ensure the cache database is created and the schema is loaded.

## Configuration

Expand Down
94 changes: 94 additions & 0 deletions bin/generate_structure_sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

# Called by bin/generate_sql_schemas with TARGET_DB environment variable set

require "bundler/setup"
require "fileutils"

# Change to test/dummy directory
dummy_dir = File.expand_path("../test/dummy", __dir__)
Dir.chdir(dummy_dir)

# Load the test/dummy Rails app
APP_PATH = File.expand_path("config/application", Dir.pwd)
require File.expand_path("config/boot", Dir.pwd)
require APP_PATH
Dummy::Application.load_tasks

# Patch Rails to use Docker containers for mysqldump/pg_dump for repeatable results
module DockerDatabaseCommands
private
def run_cmd(cmd, *args, **opts)
case cmd
when "mysqldump"
database, output_file = extract_database_and_output_file(args, file_arg: "--result-file")
docker_cmd = ["docker", "exec", "solid_cache-mysql-1", "mysqldump",
"--set-gtid-purged=OFF", "--no-data", "--routines", "--skip-comments", database]

fail run_cmd_error(cmd, args, "structure_dump") unless Kernel.system(*docker_cmd, out: output_file, **opts)
when "pg_dump"
database, output_file = extract_database_and_output_file(args, file_arg: "--file")
docker_cmd = ["docker", "exec", "solid_cache-postgres-1", "pg_dump",
"-U", "postgres", "--schema-only", "--no-privileges", "--no-owner", database]

fail run_cmd_error(cmd, args) unless Kernel.system(*docker_cmd, out: output_file, **opts)
else
super
end
end

def extract_database_and_output_file(args, file_arg:)
args = args.flatten
database = args.last
output_file = nil

args.each_with_index do |arg, i|
if arg == file_arg
output_file = args[i + 1]
break
end
end

[database, output_file]
end
end

ActiveRecord::Tasks::MySQLDatabaseTasks.prepend(DockerDatabaseCommands)
ActiveRecord::Tasks::PostgreSQLDatabaseTasks.prepend(DockerDatabaseCommands)

OUTPUT_FILES = {
"sqlite" => "cache_structure.sqlite3.sql",
"mysql" => "cache_structure.mysql.sql",
"postgres" => "cache_structure.postgresql.sql"
}

target_db = ENV["TARGET_DB"] || "sqlite"
output_file = OUTPUT_FILES[target_db]

unless output_file
puts " ✗ Error: Unknown TARGET_DB: #{target_db}"
exit 1
end

templates_dir = File.expand_path("../../lib/generators/solid_cache/install/templates/db", Dir.pwd)
output_path = File.join(templates_dir, output_file)
cache_schema_path = File.join(templates_dir, "cache_schema.rb")

begin
puts " Creating database..."
Rake::Task["db:create"].invoke

puts " Loading Ruby schema..."
load cache_schema_path

puts " Dumping structure..."
db_config = ActiveRecord::Base.connection_db_config
ActiveRecord::Tasks::DatabaseTasks.structure_dump(db_config, output_path)

puts " ✓ Saved to #{output_file}"
rescue => e
puts " ✗ Error: #{e.message}"
puts " #{e.backtrace.first(5).join("\n ")}"
exit 1
end
28 changes: 28 additions & 0 deletions bin/generate_structure_sqls
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# frozen_string_literal: true

# Generate SQL schema files for PostgreSQL, MySQL, and SQLite
# These files are used when installing SolidCache with schema_format = :sql

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

export BUNDLE_GEMFILE="$PROJECT_ROOT/gemfiles/rails_8_1.gemfile"

echo "Generating SQL schema files for SolidCache..."
echo ""

for db in sqlite mysql postgres; do
echo "==> Generating schema for $db..."
TARGET_DB=$db "$SCRIPT_DIR/generate_structure_sql"
done

echo ""
echo "✓ All SQL schema files generated successfully!"
echo ""
echo "Generated files:"
echo " - cache_structure.sqlite3.sql"
echo " - cache_structure.mysql.sql"
echo " - cache_structure.postgresql.sql"
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ volumes:

services:
postgres:
image: postgres:15.1
image: postgres:17
environment:
POSTGRES_HOST_AUTH_METHOD: "trust"
volumes:
- postgres:/var/lib/postgres
ports: [ "127.0.0.1:55432:5432" ]
mysql:
image: mysql:8.0.31
image: mysql:9
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
volumes:
Expand Down
45 changes: 44 additions & 1 deletion lib/generators/solid_cache/install/install_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,54 @@ class SolidCache::InstallGenerator < Rails::Generators::Base

def copy_files
template "config/cache.yml"
template "db/cache_schema.rb"

if Rails.application.config.active_record.schema_format == :sql
copy_sql_schema_for_adapter
else
template "db/cache_schema.rb"
end
end

def configure_cache_store_adapter
gsub_file Pathname.new(destination_root).join("config/environments/production.rb"),
/(# )?config\.cache_store = (:.*)/, "config.cache_store = :solid_cache_store"
end

private
def copy_sql_schema_for_adapter
sql_file = sql_schema_file_for_adapter

if sql_file
copy_file sql_file, "db/cache_structure.sql"
else
raise_unsupported_adapter_error
end
end

def sql_schema_file_for_adapter
case ActiveRecord::Base.connection_db_config.adapter
when "postgresql"
"db/cache_structure.postgresql.sql"
when "mysql2", "trilogy"
"db/cache_structure.mysql.sql"
when "sqlite3"
"db/cache_structure.sqlite3.sql"
else
nil
end
end

def raise_unsupported_adapter_error
error_message = <<~ERROR

ERROR: Unsupported database adapter for SQL schema format: #{adapter.inspect}

SolidCache supports installing for the following Rails adapters with schema_format = :sql:
- PostgreSQL (postgresql)
- MySQL (mysql2, trilogy)
- SQLite (sqlite3)
ERROR

raise error_message
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
DROP TABLE IF EXISTS `ar_internal_metadata`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `ar_internal_metadata` (
`key` varchar(255) NOT NULL,
`value` varchar(255) DEFAULT NULL,
`created_at` datetime(6) NOT NULL,
`updated_at` datetime(6) NOT NULL,
PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `schema_migrations`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `schema_migrations` (
`version` varchar(255) NOT NULL,
PRIMARY KEY (`version`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `solid_cache_entries`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `solid_cache_entries` (
`id` bigint NOT NULL AUTO_INCREMENT,
`key` varbinary(1024) NOT NULL,
`value` longblob NOT NULL,
`created_at` datetime(6) NOT NULL,
`key_hash` bigint NOT NULL,
`byte_size` int NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `index_solid_cache_entries_on_key_hash` (`key_hash`),
KEY `index_solid_cache_entries_on_byte_size` (`byte_size`),
KEY `index_solid_cache_entries_on_key_hash_and_byte_size` (`key_hash`,`byte_size`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

Loading