简体   繁体   English

如何在Rails中与多个数据库并行建立连接?

[英]How to establish_connection with more than one database in parallel in Rails?

Context 上下文

I'm building a SaaS where users can create their own websites (like Wix or SquareSpace). 我正在构建一个SaaS,用户可以在其中创建自己的网站(如Wix或SquareSpace)。 That's what happens behind scenes: 这就是幕后发生的事情:

  • My app has its main database which stores users 我的应用程序有其主存储用户的数据库
  • When a user creates his website, an external database is created to store its data 当用户创建他的网站时,会创建一个外部数据库来存储其数据
  • SQL file runs in this external database to set default settings SQL文件在此外部数据库中运行以设置默认设置
  • Other users shall create their websites simultaneously 其他用户应同时创建其网站

Approach 途径

To create a new database and establish connection I do the following: 创建新数据库建立连接 ,请执行以下操作:

ActiveRecord::Base.connection.execute("CREATE DATABASE #{name}")
ActiveRecord::Base.establish_connection(<dynamic db data>)

Then I execute sql code in the db by doing: 然后我通过执行以下操作在db中执行sql代码:

sql = File.read(sql_file.sql)
statements = sql.split(/;$/)
statements.pop
ActiveRecord::Base.transaction do
  statements.each do |statement|
    connection.execute(statement)
  end
end

Then I reestablish connection with main db: 然后我重新建立与主db的连接:

ActiveRecord::Base.establish_connection :production

Problem 问题

  1. Establishing connection to dynamic database makes application's main database inacessible for a while: 建立与动态数据库的连接使应用程序的主数据库暂时无法使用:
    • User A is creating a website (establishes dynamic database connection) 用户A正在创建一个网站(建立动态数据库连接)
    • User B tries to access his user area (which requires application's main db data) 用户B尝试访问其用户区域(需要应用程序的主数据库数据)
    • Application throws an error because it tries to retrieve data of app-main-db (which connection is not established at the moment) 应用程序抛出错误,因为它尝试检索app-main-db的数据(此时未建立连接)

How can I handle many users creating their websites simultaneously without databases conflict? 如何在没有数据库冲突的情况下同时处理许多用户创建网站?

In other words, how can I establish_connection with more than one database in parallel? 换句话说,我怎么能establish_connection与并行多个数据库?


NOTE: It is not the same as connecting to multiple databases through database.yml. 注意:它与通过database.yml连接到多个数据库不同。 The goal here is to connect and disconnect to dynamic created databases by multiple users simultaneously. 这里的目标是同时连接和断开多个用户对动态创建的数据库的连接。

This gem may help. 这个宝石可能有帮助。 However,you may need to rename some of your models to use the external database namespace instead of ApplicationRecord 但是,您可能需要重命名某些模型以使用外部数据库命名空间而不是ApplicationRecord

https://github.com/ankane/multiverse https://github.com/ankane/multiverse

I admit that this doesn't answer the core of your initial question but IMO this probably needs to be done via a separate operation, say a pure SQL script triggered somehow via a queue. 我承认这不能回答你的初始问题的核心,但IMO可能需要通过一个单独的操作完成,比如通过队列以某种方式触发的纯SQL脚本。

You could have your rails app drop a "create message" onto a queue and have a separate service that monitors the queue that does the create operations, and then pass a message with info back to the queue. 您可以让rails应用程序将“创建消息”放入队列,并拥有一个单独的服务来监视执行创建操作的队列,然后将带有信息的消息传递回队列。 The rails application monitors the queue for these and then does something with the information. rails应用程序监视队列中的这些,然后对信息执行某些操作。

The larger issue is decoupling your operations. 更大的问题是解耦您的运营。 This will help you down the road with things like maintenance, scaling, etc. 这将帮助您顺利完成维护,缩放等操作。

FWIW here is a really cool website I found recently describing a lot of popular queuing services. FWIW这里是一个非常酷的网站,我最近发现它描述了很多流行的排队服务。

Probably not the best approach but it can be achieved by calling an external script that creates the database, in a separated ruby file: 可能不是最好的方法,但可以通过在分离的ruby文件中调用创建数据库的外部脚本来实现:

  • Create create_database.rb file in lib folder lib文件夹中创建create_database.rb文件
  • Put db creation script inside this file 将db creation脚本放在此文件中

     ActiveRecord::Base.connection.execute("CREATE DATABASE #{name}") ActiveRecord::Base.establish_connection(<dynamic db data>) 
  • Execute with Rails Runner 使用Rails Runner执行

     rails runner lib/create_database.rb 
  • or with system, if you want to call it from controller 或者使用系统,如果你想从控制器调用它

     system("rails runner lib/create_database.rb") 

This way you can create and access multiple databases without stopping your main database. 这样,您可以在不停止主数据库的情况下创建和访问多个数据库。


Passing arguments 传递参数

You can pass arguments to your script with ARGV: 您可以使用ARGV将参数传递给脚本:

rails runner lib/create_database.rb db_name

And catch it inside the script with ARGV[0] : 并使用ARGV[0]在脚本中捕获它:

name = ARGV[0]
puts name
> db_name

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM