Setting up multiple databases in Rails: the definitive guide
Bài đăng này đã không được cập nhật trong 6 năm
Có nhiều lý do khác nhau khiến bạn có thể cân nhắc việc có nhiều cơ sở dữ liệu trong ứng dụng Ruby on Rails. Trong trường hợp cụ thể của tôi, tôi cần phải lưu trữ số lượng lớn dữ liệu đại diện cho hành vi của người dùng: nhấp chuột, các trang truy cập, những thay đổi lịch sử, v.v ... Tôi đã đọc nhiều giải pháp khác nhau, tuy nhiên tôi không thể tìm thấy một trong đó có thể có đầy đủ bao gồm làm thế nào để:
- Có file migrate và schema riêng biệt cho mỗi cơ sở dữ liệu.
- Dùng Rails generate để tạo các file migration riêng biệt cho mỗi cơ sở dữ liệu.
- Cung cấp các rake task cho từng cơ sở dữ liệu cụ thể (Ví dụ
rake db:migrate
cho từng cơ sở dữ liệu khác nhau). - Tích hợp RSpec.
- Làm việc với Database Cleaner.
- Làm việc trên Heroku.
Create the custom database files
Trong bài hướng dẫn này, chúng ta sẽ thiết lập cơ sở dữ liệu thứ hai có tên là Stats
. Để làm được như vậy, chúng ta sẽ bắt trước cách Rails xử lý với cơ sở dữ liệu chính.
Đầu tiên, ta tạo một file config/database_stats.yml
là file cấu hình kết nối đến cơ sở dữ liệu Stats
(nó có chức năng giống như config/database.yml
mà chúng ta thường dùng cho cơ sở dữ liệu chính). Nội dung của file này như sau:
development:
adapter: postgresql
encoding: utf8
host: localhost
pool: 10
database: myapp_stats_development
username: postgres
password:
test:
adapter: postgresql
encoding: utf8
host: localhost
pool: 10
database: myapp_stats_test
username: postgres
password:
production:
adapter: postgresql
encoding: utf8
url: <%= ENV["DATABASE_STATS_URL"] %>
pool: <%= ENV["DB_POOL"] || 5 %>
Lưu ý rằng chúng ta đã đặt tên cụ thể cho các cơ sở dữ liệu, thực hiện theo các đúng quy ước đặt tên của Rails.
Tiếp đến, chúng ta sẽ tạo một thư mục để lưu trữ tất cả các file migration và file schema của cơ sở dữ liệu Stats
. Về cơ bản sao chép lại thư mục db
của dự án Rails. Tạo thư mục db_stats
trong thư Rails root và đảm bảo cấu trúc và các tệp tin giống với thư mục db
. Bạn sẽ có một cái gì đó như sau
-- db
|-- migrate
schema.rb
seeds.rb
-- db_stats
|-- migrate
schema.rb
seeds.rb
Các file schema.rb
và seeds.rb
, cùng với thư mục migrate
chỉ tạo ra và để trống.
Add Rake tasks
Để xử lý và điều khiển được cơ sở dữ liệu Stats
, cho phép create , migrate, drop v.v..., chúng ta sẽ cần tạo ra Rake task. Chúng ta sẽ tạo ra các hàm giống với các hàm mà Rails cung cấp đối với cơ sở dữ liệu chính.
Tạo file lib/tasks/db_stats.rake
có nội dung như sau:
task spec: ["stats:db:test:prepare"]
namespace :stats do
namespace :db do |ns|
task :drop do
Rake::Task["db:drop"].invoke
end
task :create do
Rake::Task["db:create"].invoke
end
task :setup do
Rake::Task["db:setup"].invoke
end
task :migrate do
Rake::Task["db:migrate"].invoke
end
task :rollback do
Rake::Task["db:rollback"].invoke
end
task :seed do
Rake::Task["db:seed"].invoke
end
task :version do
Rake::Task["db:version"].invoke
end
namespace :schema do
task :load do
Rake::Task["db:schema:load"].invoke
end
task :dump do
Rake::Task["db:schema:dump"].invoke
end
end
namespace :test do
task :prepare do
Rake::Task["db:test:prepare"].invoke
end
end
# append and prepend proper tasks to all the tasks defined here above
ns.tasks.each do |task|
task.enhance ["stats:set_custom_config"] do
Rake::Task["stats:revert_to_original_config"].invoke
end
end
end
task :set_custom_config do
# save current vars
@original_config = {
env_schema: ENV['SCHEMA'],
config: Rails.application.config.dup
}
# set config variables for custom database
ENV['SCHEMA'] = "db_stats/schema.rb"
Rails.application.config.paths['db'] = ["db_stats"]
Rails.application.config.paths['db/migrate'] = ["db_stats/migrate"]
Rails.application.config.paths['db/seeds'] = ["db_stats/seeds.rb"]
Rails.application.config.paths['config/database'] = ["config/database_stats.yml"]
end
task :revert_to_original_config do
# reset config variables to original values
ENV['SCHEMA'] = @original_config[:env_schema]
Rails.application.config = @original_config[:config]
end
end
Trong đoạn code trên, ta tạo tất cả các task có tên giống với các task mà Rails cung cấp (migrate
, setup
, v.v...) với namspace là stats:db
. Chúng ta chạy vòng lặp qua tất cả các task của namspace db
và đảm bảo rằng task stats:set_custom_config
luôn chạy trước các task khác, còn task stats:revert_to_original_config
luôn chạy sau khi mỗi task đã kết thúc
# append and prepend proper tasks to all tasks defined in stats:db namespace
ns.tasks.each do |task|
task.enhance ["stats:set_custom_config"] do
Rake::Task["stats:revert_to_original_config"].invoke
end
end
Chúng ta phải làm điều này vì, không may, Rails hỗ trợ cho nhiều cơ sở dữ liệu không phải là tuyệt đối, vì thế chúng ta cần cung cấp các thủ thuật nhỏ để làm cho mọi thứ hoạt động. Vì lý do này, chúng ta phải đặt các biến môi trường và biến cấu hình cụ thể cho các giá trị tùy chỉnh phù hợp với cơ sở dữ liệu Stats
của chúng ta trước khi chúng ta chạy các task và sau đó đảm bảo rằng các giá trị ban đầu được thiết lập lại khi các task đó đã được chạy. Hai task sau giúp làm điều đó:
task :set_custom_config do
# save current vars
@original_config = {
env_schema: ENV['SCHEMA'],
config: Rails.application.config.dup
}
# set config variables for custom database
ENV['SCHEMA'] = "db_stats/schema.rb"
Rails.application.config.paths['db'] = ["db_stats"]
Rails.application.config.paths['db/migrate'] = ["db_stats/migrate"]
Rails.application.config.paths['db/seeds'] = ["db_stats/seeds.rb"]
Rails.application.config.paths['config/database'] = ["config/database_stats.yml"]
end
task :revert_to_original_config do
# reset config variables to original values
ENV['SCHEMA'] = @original_config[:env_schema]
Rails.application.config = @original_config[:config]
end
Cuối cùng, nếu bạn đang sử dụng RSpec, bạn có thể thêm một task spec, để đảm bảo rằng cơ sở dữ liệu Stats
được tự động chuẩn bị khi chạy test:
task spec: ["stats:db:test:prepare"]
Việc cài đặt đã hoàn thành, bây giờ chúng ta có thể tạo và migrate cơ sở dữ liệu Stats
như sau:
$ rake stats:db:create
$ rake stats:db:migrate
Add a custom generator
Thật không may chúng ta không thể sử dụng ActiveRecord::Generators::MigrationGenerator
bởi vì nó đã được fix cứng code trong thư mục db.migrate/
như bên dưới:
def create_migration_file
set_local_assigns!
validate_file_name!
migration_template @migration_template, "db/migrate/#{file_name}.rb"
end
Do đó, chúng ta cần tạo một generator riêng cho cơ sở dữ liệu Stats
. Tạo generator trong file lib/generators/stats_migration_generator.rb
như sau
require 'rails/generators/active_record/migration/migration_generator'
class StatsMigrationGenerator < ActiveRecord::Generators::MigrationGenerator
source_root File.join(File.dirname(ActiveRecord::Generators::MigrationGenerator.instance_method(:create_migration_file).source_location.first), "templates")
def create_migration_file
set_local_assigns!
validate_file_name!
migration_template @migration_template, "db_stats/migrate/#{file_name}.rb"
end
end
Cách sử dụng generator này rất đơn giản như sau:
$ rails g stats_migration create_clicks create db_stats/migrate/20151201191642_create_clicks.rb
Sau đó ta chay rake stats:db:migrate
để tạo bảng cho cơ sở dữ liệu Stats
Finalize connection and models
Tạo file config/initializers/db_stats.rb
có nội dung như sau để đọc các cấu hình kết nối đến Stats
:
# save stats database settings in global var
DB_STATS = YAML::load(ERB.new(File.read(Rails.root.join("config","database_stats.yml"))).result)[Rails.env]
Sau đó trong mỗi model của cơ sở dữ liệu Stats
, ta thêm đoạn sau để kết nối đến Stats
. Ví dụ
class Click < ActiveRecord::Base
establish_connection DB_STATS
end
Kết luận
Như vậy, trong bài viết trên đây, đã trình bày cách cài đặt để một ứng dụng Rails có thể sử dụng nhiều cơ sở dữ liệu. Hy vọng nó sẽ giúp ích cho mọi người. Cảm ơn đã đọc bài viết.
Tài liệu tham khảo
http://www.ostinelli.net/setting-multiple-databases-rails-definitive-guide/
All rights reserved