Apakah mungkin untuk memiliki beberapa kumpulan koneksi database di rel untuk beralih di antara?

12

Sedikit latar belakang

Saya telah menggunakan permata Apartment untuk menjalankan aplikasi multi-sewa selama bertahun-tahun. Sekarang baru-baru ini kebutuhan untuk memperbesar basis data menjadi beberapa host telah tiba, server db tidak dapat mengikuti lagi (baik membaca dan menulis sudah terlalu banyak) - dan ya, saya meningkatkan perangkat keras ke max (didedikasikan perangkat keras, 64 core, 12 Nvm-e drive dalam serangan 10, ram 384Gb dll.).

Saya sedang mempertimbangkan untuk melakukan ini per-penyewa (1 penyewa = 1 konfigurasi koneksi database / kolam) karena itu akan menjadi cara "sederhana" dan efisien untuk mendapatkan hingga number-of-tenants-lebih banyak kapasitas tanpa melakukan banyak perubahan kode aplikasi.

Sekarang, saya menjalankan rel 4.2 atm., Segera meningkatkan ke 5.2. Saya dapat melihat bahwa rails 6 menambahkan dukungan untuk definisi koneksi per-model, namun itu tidak benar-benar apa yang saya butuhkan, karena saya memiliki skema database yang sepenuhnya dicerminkan untuk masing-masing dari 20 penyewa saya. Biasanya saya mengganti "database" per permintaan (di middleware) atau per latar belakang pekerjaan (sidekiq middleware), namun ini saat ini sepele dan ditangani oleh permata Apartment, karena hanya mengatur search_pathdi Postgresql dan tidak benar-benar mengubah koneksi yang sebenarnya. Saat beralih ke strategi hosting per-penyewa, saya harus mengganti seluruh koneksi per permintaan.

Pertanyaan:

  1. Saya mengerti bahwa saya bisa melakukan pekerjaan ActiveRecord::Base.establish_connection(config)per permintaan / latar belakang - namun, seperti yang saya juga mengerti, yang memicu jabat tangan koneksi database yang sama sekali baru untuk dibuat dan kolam db baru untuk muncul di rel - kan? Saya kira itu akan menjadi kinerja bunuh diri untuk membuat semacam overhead pada setiap permintaan tunggal untuk aplikasi saya.
  2. Karena itu saya bertanya-tanya apakah ada yang bisa melihat opsi dengan rel misalnya pra-membangun beberapa (total 20) koneksi database / pool dari awal (misalnya pada saat boot aplikasi), dan kemudian hanya beralih di antara kolam-kolam per permintaan? Sehingga koneksi dia sudah dibuat dan siap digunakan.
  3. Apakah semua ini hanya ide yang buruk dan haruskah saya mencari pendekatan yang berbeda? Misalnya 1 instance aplikasi = satu koneksi spesifik ke satu tenant tertentu. Atau sesuatu yang lain.
Niels Kristian
sumber
2
guides.rubyonrails.org/active_record_multiple_databases.html Saya pikir ini mungkin membantu Anda
Alex Golubenko
1
Anda mungkin tertarik dengan PR ini di repositori Rails 'GitHub yang baru-baru ini menambahkan fitur yang Anda butuhkan ke mastercabang Rails saat ini . Apakah menjalankan Rails Egde menjadi opsi atau memproteksi fitur itu ke versi Rails Anda saat ini?
spickermann
@spickermann ActiveRecord::Base.connected_to(shard: :shard_one) do ... endberarti bahwa pool akan digunakan (kembali), alih-alih membuat koneksi baru setiap saat?
Ben

Jawaban:

4

Seperti yang saya mengerti, ada 4 pola untuk aplikasi multi-tenancy:

1. Model khusus / Beberapa Lingkungan Produksi

Setiap instance atau basis data seluruhnya menampung aplikasi penyewa yang berbeda dan tidak ada yang dibagikan di antara penyewa.

Ini adalah aplikasi 1 contoh dan 1 database untuk 1 penyewa. Pengembangannya akan mudah seolah Anda hanya melayani 1 penyewa. Tetapi akan menjadi mimpi buruk bagi para biarawan jika Anda memiliki, katakanlah, 100 penyewa.

2. Pemisahan Fisik Penyewa

1 contoh aplikasi untuk semua penyewa tetapi 1 basis data untuk 1 penyewa. Ini yang Anda cari. Anda dapat menggunakan ActiveRecord::Base.establish_connection(config), atau menggunakan permata, atau memperbarui ke Rails 6 seperti yang disarankan lainnya. Lihat jawaban untuk (2) di bawah ini.

3. Model skema terisolasi / Segregasi Logis

Dalam Skema Terpencil, tabel penyewa atau komponen basis data adalah grup di bawah skema logis atau ruang nama dan dipisahkan dari skema penyewa lain, namun skema tersebut dihosting dalam instance database yang sama.

1 instance app dan 1 database untuk semua penyewa, seperti yang Anda lakukan dengan permata apartemen.

4. Komponen Sebagian Terisolasi

Dalam model ini, komponen yang memiliki fungsi umum dibagi di antara penyewa sementara komponen dengan fungsi yang unik atau tidak terkait diisolasi. Pada lapisan data, data umum seperti data yang mengidentifikasi penyewa dikelompokkan atau disimpan dalam satu tabel sementara data spesifik penyewa diisolasi pada tabel atau lapisan contoh.


Adapun (1), ActiveRecord::Base.establish_connection(config)tidak jabat tangan ke db per permintaan jika Anda menggunakannya dengan benar. Anda dapat memeriksa di sini dan membaca semua komentar di sini .

Adapun (2), Jika Anda tidak ingin menggunakan establish_connection, Anda dapat menggunakan permata multiverse (ini berfungsi untuk rel 4.2), atau permata lainnya. Atau, seperti saran lainnya, Anda dapat memperbarui ke Rails 6.

Sunting: Permata multiverse sedang digunakan establish_connection. Ini akan menambahkan database.yml, dan membuat kelas dasar sehingga setiap subclass berbagi koneksi / kolam yang sama. Pada dasarnya, ini mengurangi upaya kami untuk menggunakan establish_connection.

Adapun (3), jawabannya:

Jika Anda tidak memiliki banyak penyewa, dan aplikasi Anda cukup rumit, saya sarankan Anda menggunakan pola Model Khusus. Jadi Anda menggunakan 1 instance aplikasi = satu koneksi spesifik ke satu penyewa tertentu. Anda tidak perlu membuat aplikasi Anda lebih kompleks dengan menambahkan beberapa koneksi basis data.

Tetapi jika Anda memiliki banyak penyewa, saya sarankan Anda menggunakan Segregasi Fisik Penyewa atau Komponen Terpisah Sebagian tergantung pada proses bisnis Anda.

Either way, Anda harus memperbarui / menulis ulang aplikasi Anda untuk mematuhi arsitektur baru.

KSD Putra
sumber
Hai terima kasih atas jawabannya. Saya akan membutuhkan sedikit waktu untuk benar-benar menguji saran sebelum saya dapat menghargai salah satu jawaban karunia jika mereka solusi yang baik.
Niels Kristian
Saya punya beberapa pertanyaan mengenai 1 dan 2. 1: Saya tidak yakin saya mengerti referensi Anda. Apakah yang Anda katakan, yang dapat saya panggil .establish_connection (config) tanpa melakukan jabat tangan / menciptakan jajak pendapat db? Dalam hal ini, saya tidak yakin bagaimana kedua tautan menjelaskan hal itu? 2: Untuk multiverse, bukankah itu basis data per-model alih-alih seluruh db untuk seluruh aplikasi? Saya merasa dokumentasi mereka cukup samar
Niels Kristian
Saya pikir saya memiliki kesalahpahaman. Apakah Anda keberatan untuk menguraikan kalimat-kalimat ini? Saya mengerti bahwa saya dapat melakukan ActiveRecord :: Base.establish_connection (config) per pekerjaan permintaan / latar belakang - namun, seperti yang saya juga pahami, yang memicu jabat tangan koneksi database yang sama sekali baru untuk dibuat dan kumpulan db baru untuk muncul di rails It menyarankan bahwa satu permintaan membuat satu kumpulan db?
KSD Putra
Maksud saya: (1) Saya khawatir tentang kinerja / overhead jaringan ketika harus memanggil ActiveRecord :: Base.establish_connection (config) pada setiap permintaan, hanya untuk beralih di antara berbagai basis data / negara
Niels Kristian
Anda tidak perlu khawatir tentang overhead. Sekarang, jika Anda menggunakan DB tunggal, Anda memiliki satu kumpulan koneksi (Anda dapat memeriksa tautan tentang koneksi dalam jawaban (1) di atas). Jika Anda menggunakan establish_connectionmodel seperti ini class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); end:, dan mengatakan Anda memiliki 5 model, Anda membuat 5 kumpulan koneksi ke DB_SECOND_TENANT. Dan setiap kolam diperlakukan sama. Jadi, Anda tidak membuat kumpulan per permintaan, tetapi per establish_connection.
KSD Putra
3

Dari apa yang saya mengerti, (2) harus dimungkinkan dengan switching koneksi manual di Rails 6.

claasz
sumber
Namun terima kasih ini tampaknya cukup jauh dari kasus penggunaan saya. Itu menyiratkan menulis ulang seluruh aplikasi untuk menggunakan prosedur ini di mana-mana.
Niels Kristian
3

Hanya beberapa hari yang lalu sharding horizontal ditambahkan ke mastercabang Ruby on Rails di GitHub. Saat ini, fitur ini tidak dirilis secara resmi tetapi tergantung pada versi Rails aplikasi Anda, Anda mungkin ingin mempertimbangkan untuk menggunakan Rails masterdengan menambahkan ini ke Gemfile:

gem "rails", github: "rails/rails", branch: "master"

Dengan fitur baru ini, Anda dapat memanfaatkan kumpulan koneksi basis data Rails dan mengganti basis data berdasarkan kondisi.

Saya belum pernah menggunakan fitur baru ini, tetapi sepertinya cukup mudah:

# in your config/database.yml
production:
  primary:
    database: my_database
    # other config: user, password, etc
  primary_tenant_1:
    database: tenant_1_database
    # other config: user, password, etc

# in your controller for example when updating a tenant
ActiveRecord::Base.connected_to(shard: "primary_tenant_#{tenant.database_shard_number}") do
  tenant.save
end

Anda tidak menambahkan banyak detail tentang bagaimana Anda menentukan nomor penyewa atau bagaimana otorisasi dilakukan dalam aplikasi Anda. Tetapi saya akan mencoba untuk menentukan angka penyewa sesegera mungkin di application_controllerdalam around_action. Sesuatu seperti ini mungkin menjadi titik awal:

around_filter :determine_database_connection

private

def determine_database_connection
  # assuming you have a method to determine the current_tenant and that tenant
  # has a method that returns the number of the shard to use or even the 
  # full shard identifier
  shard = current_tenant.database_shard # returns for example `:primary_tenant_1` 

  ActiveRecord::Base.connected_to(shard: shard) do
    yield
  end
end
spickermann
sumber
Apakah itu masuk akal untuk beralih kembali ke koneksi default dalam kasus itu juga? github.com/influitive/apartment#middleware-considerations
Ben
1
Setelah Anda meninggalkan ActiveRecord::Base.connected_to ... doblok itu akan menggunakan koneksi default lagi.
spickermann
@ spickermann saya membaca ab permata ini, bukan hanya untuk rails6?
7urkm3n
@ 7urkm3n Sudah termasuk dalam mastercabang Rails saat ini .
spickermann
Hai terima kasih atas jawabannya. Saya akan membutuhkan sedikit waktu untuk benar-benar menguji saran sebelum saya dapat menghargai salah satu jawaban karunia jika mereka solusi yang baik.
Niels Kristian