Reputation: 1539
I'm building a SaaS where users can create their own websites (like Wix or SquareSpace). That's what happens behind scenes:
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:
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:
ActiveRecord::Base.establish_connection :production
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?
NOTE: It is not the same as connecting to multiple databases through database.yml. The goal here is to connect and disconnect to dynamic created databases by multiple users simultaneously.
Upvotes: 8
Views: 1489
Reputation: 3945
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.
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. The rails application monitors the queue for these and then does something with the information.
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.
Upvotes: 1
Reputation: 1539
Probably not the best approach but it can be achieved by calling an external script that creates the database, in a separated ruby file:
Put db creation script inside this file
ActiveRecord::Base.connection.execute("CREATE DATABASE #{name}")
ActiveRecord::Base.establish_connection(<dynamic db data>)
Execute with 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.
You can pass arguments to your script with ARGV:
rails runner lib/create_database.rb db_name
And catch it inside the script with ARGV[0]
:
name = ARGV[0]
puts name
> db_name
Upvotes: 0
Reputation: 1703
This gem may help. However,you may need to rename some of your models to use the external database namespace instead of ApplicationRecord
https://github.com/ankane/multiverse
Upvotes: 1