Multiple database with subdomain use apartment gem
Bài đăng này đã không được cập nhật trong 3 năm
Làm thế nào để cùng một soures code duy nhất có thể chạy với nhiều subdomain
khác nhau mà không làm lẫn dữ liệu giữa chúng?
Có lẽ bài toán này đã quá quen thuộc với cộng đồng lập trình viên nói chung, có khá nhiều cách để làm việc này tuy nhiên hôm nay mình xin giới thiệu gem apartment
sử dụng trong framework Ruby on Rails.
Về cơ bản các bạn có thể hiểu gem apartment
cung cấp cách cho nhiều databases có cấu trúc bảng giống nhau chạy trên cùng một ứng dụng. Mỗi một database sẽ được trỏ bởi một subdomain khác nhau và lưu dữ liệu khi người dùng truy nhập ứng dụng bằng subdmain nào sẽ được lưu vào database tương ứng. Do vậy không có khả năng bị lẫn dữ liệu giữa các subdomain khi truy vấn vì bản chất dữ liệu được lưu ở những database khác nhau.
Cài đặt
Cách cài đặt gem apartment
cũng tương tự như những gem khác:
Thêm dòng sau trong Gemfile
gem "apartment", ">= 1.2.0"
Chạy lệnh
bundle install
Tạo ra file config của gem bằng lệnh
bundle exec rails generate apartment:install
SLệnh trên sẽ tự động tạo file config/initializers/apartment.rb
trong thư mục initializers
có dạng:
require 'apartment/elevators/subdomain'
Apartment.configure do |config|
#apartment config
end
Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain'
Cách sử dụng
Chúng ta sẽ tìm hiểu cách sử dụng gem apartment
thông qua một ví dụ cụ thể.
1. Tạo một bảng User để chứa thông tin subdomain
rails g scaffold User email subdomain
2. Thêm config sau trong file config/initializers/apartment.rb
config.excluded_models = %w{ User }
# Config này tương ứng với việc bảng User sẽ được public giữa tất các database nghĩa là dù có truy vấn bằng subdomain nào thì dữ liệu lấy ra đều giống nhau
config.tenant_names = lambda { User.pluck :subdomain }
# Config này nghĩa là sẽ sử dụng dữ liệu trong cột subdomain của bảng User để tạo các tenant DB
3. Thêm config sau trong application.rb
config.middleware.use Apartment::Elevators::Subdomain
Cho phép ứng dụng chạy với nhiều subdomain khác nhau.
Note: Cần thêm require 'apartment/elevators/subdomain'
vào application.rb
trước khi thêm config trên.
4. Run
rake db:migrate
Cho kết quả như sau cùng với một số cảnh báo về việc sử dụng tenant DB.
framgia@framgia0216-lt:~/Jobs/viblo201705$ rake db:migrate
== 20170524141552 CreateUsers: migrating ======================================
-- create_table(:users)
-> 0.0025s
== 20170524141552 CreateUsers: migrated (0.0026s) =============================
[WARNING] - The list of tenants to migrate appears to be empty. This could mean a few things:
1. You may not have created any, in which case you can ignore this message
2. You've run `apartment:migrate` directly without loading the Rails environment
* `apartment:migrate` is now deprecated. Tenants will automatically be migrated with `db:migrate`
Note that your tenants currently haven't been migrated. You'll need to run `db:migrate` to rectify this.
5. Thêm đoạn code sau để tạo tự động database khi thêm một bản ghi vào bảng User
class User < ApplicationRecord
after_create :create_tenant
private
def create_tenant
Apartment::Tenant.create subdomain
end
end
Đây là cách thức hoạt động khi thêm một bản ghi mới vào bảng User
Started POST "/users" for 127.0.0.1 at 2017-05-24 21:25:39 +0700
Processing by UsersController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"dy92oqOX22ACMXFYNB5j2Ve/TL5ndqRL09Xvt1xE7yJz/Udyd1z0G2k1EhDesG/mVGUAvysIS6LzCZh7cLkEtg==", "user"=>{"email"=>"giang.phan1106@gmail.com", "subdomain"=>"subdomain"}, "commit"=>"Create User"}
(0.3ms) begin transaction
SQL (0.4ms) INSERT INTO "users" ("email", "subdomain", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["email", "giang.phan1106@gmail.com"], ["subdomain", "subdomain"], ["created_at", "2017-05-24 14:25:39.550230"], ["updated_at", "2017-05-24 14:25:39.550230"]]
(0.1ms) SELECT "users"."subdomain" FROM "users"
(156.6ms) CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "email" varchar, "subdomain" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL)
(160.9ms) CREATE TABLE "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY)
(0.4ms) SELECT version FROM "schema_migrations"
(120.7ms) INSERT INTO "schema_migrations" (version) VALUES (20170524141552)
(114.4ms) CREATE TABLE "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL)
ActiveRecord::InternalMetadata Load (0.7ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ? [["key", "environment"], ["LIMIT", 1]]
(0.3ms) begin transaction
SQL (0.9ms) INSERT INTO "ar_internal_metadata" ("key", "value", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["key", "environment"], ["value", "development"], ["created_at", "2017-05-24 14:25:40.192029"], ["updated_at", "2017-05-24 14:25:40.192029"]]
(106.9ms) commit transaction
(0.2ms) SELECT "users"."subdomain" FROM "users"
(87.5ms) commit transaction
Bây giờ thì run rails server
để kiểm tra kết quả nhé.
6. Kết quả
Tạo dữ liệu cho bảng User như sau:
Dù chạy bằng url gốc http://lvh.me:3000/users
hay subdomain http://subdomain.lvh.me:3000/users
thì kết quả cũng giống nhau lý do là bảng User đã được public giữa các database:
Bây giờ để kiểm chứng việc không bị trùng dữ liệu khi chạy các subdomain khác nhau chúng ta tạo một bảng private Project
vói cấu trúc dưới đây:
rails g scaffold Project name year
Khi chạy với subdomain http://lvh.me:3000/projects
sẽ cho kết quả như sau:
Tuy nhiên khi chạy với subdomain http://subdomain.lvh.me:3000/projects
sẽ không thấy dữ liệu do bảng Project
không được public với tất cả database:
7. Một số phương thức mà gem apartment
cung cấp
- Tạo một database mới
Apartment::Tenant.create('tenant_name')
- Chuyển đổi giữa các database với nhau
Apartment::Tenant.switch!('tenant_name')
- Kiểm tra xem thời điểm hiện tại đang sử dụng database nào
Apartment::Tenant.current_tenant
Việc sử dụng nhiều database riêng biệt cho từng subdomain quá dễ ràng phải không nào? Nếu bạn có ý định làm một ứng dụng với chức năng như vậy thì gem apartment
là một lựa chọn sáng suốt.
Còn rất nhiều điều mà gem apartment
có thể làm được, để tìm hiểu chi tiết hơn các bạn có thể tham khảo Tại đây
Thanks you for reading !!!
All rights reserved