multiple databases on global level in Rails - ruby-on-rails

I am aware of multiple database support in Rails 6 but it seems to be on model level, like we tell for each model that from which database it belongs to.
A client wants two databases for production server with similar schema say
primary_live_database
secondary_test_database
idea is that app will send a header parameter to give a hint to its test app or live, and depending upon this we will switch database.
how can we achieve this?

You really just want to setup an additional environment. This is done by setting the RAILS_ENV environmental variable. Usually such an environment is called staging but its really up to you whatever you want to call it.
Setting RAILS_ENV = 'staging' will:
Cause Rails to load config/environments/staging.rb.
Rails will use the staging settings in config/database.yml.
The secrets for staging will be loaded from config/secrets.yml.enc if your Rails version supports per env secrets.
Rails.env == 'staging' and Rails.env.staging? == true.
This lets you run a near copy of the production environment but with different settings. For example you often want to limit access to the staging environment to collaborators or push changes to the staging environment to collaborate on features that may not be production ready.
If your clients reaction is Waaaaaaah, so much work! then you can use a callback in your ApplicationController to call ActiveRecord::Base.establish_connection. I don't recommend it though.

Related

Good approach to switch database for Test mode / Sand box mode : Rails 6

I am working on an app where I now need to add test mode or sandbox mode features like Stripe provides in the dashboard. In this feature, when the user turned the test mode on, the user can test the functionality and can create dummy data in the same login session/token.
I have tried to use the Rails 6 feature to use multiple databases but have a few questions:
is it good to switch the connection in production for test mode even we will not have many test requests?
will it be good to have a separate instance for test mode with the test subdomain? in this case how we should manage the login sessions? should we copy data to the test the database? will it be good and common practice?
I only can manage to implement this if I have user data in the test database so that when I switch database connection system will not send an unauthenticated response.
Note:
for login and user update, it will use the primary database all time and for other actions, it will use the test_mode database. I am doing this by skip_around_action in a specific controller.
We are doing this so that when the user turned off test mode, it will update the primary database and the next request will use the primary database as per around_action logic
Here is my current code in application_controller.rb:
around_action :setup_db_connection
def setup_db_connection
database_key = (user.test_mode?) ? :test_mode : :primary
ActiveRecord::Base.connected_to(database: database_key) do
yield
end
end
test_mode database key has test database configuration in database.yml and similarly for primary database key. Both are completely two different databases.
Can anyone please tell me if I am going in to correct direction? Any help or thought will be appriciated. Thank you in advance!!!!
You should just create an additional environment as this is the exact problem that they are designed to solve.
Rails just ships preconfigured with development, test and production as thats the bare minimum you can get by with. You can actually have an unlimeted number of environments.
So lets say you want to create an enviroment named staging which closely matches your production environment. You can start by copying the productions settings:
cp app/environments/production.rb app/environments/staging.rb
And then just setup an additional hash in your database.yml and credentials files.
staging:
<<: *default
database: my_app_staging
You can then set the environment by using the RAILS_ENV env var or the command line arguments when starting the Rails server. Your staging environment can be run for example in a separate Heroku application made available on a subdomain. If you're using Heroku or some other SAAS platform a hobby tier will often be sufficient for staging.
When it comes to data you can either work on a set of dummy data generated by wiping and seeding or regularily wipe the staging database with a backup from your production database.
I would not consider using a "test mode switch" a good idea as it makes far to easy to inadvertantly mess things up. Using a separate environment lets you use a completely different set of credentials as well so you won't accentially do something like create a real credit card charge or destroy data on third party services. Sandboxes should be isolated.

better way to configure the email address with rails application

Currently, I'm working on the application which is developed in rails 3* and ruby 1.9.3. I have configured some email address in the initializers section as YML file for each environments.
But, the requirement is keep on changing (but it'll happened every month 1 or 2 times) that need to add/remove the email address from the configuration. Hence, I need to restart the server on every changes. Because, I configured those address in the initializers.
Is there any better way to handle this situation?
If you want to change the email without re-deploying / restarting the server, you can always create a Email model and persist it to the database. By adding a current field / column (boolean value) and a scope scope :current, -> { where(current: true) } you can access the email via Email.current.first.address, for instance. You might need to ensure that one and only one 'current' Email object ist present at any given time.
edit
creating a model does not mean you have to create UI for it. Just use the console to change the email if you have to.
the configuration and models are loaded at startup, and if you don't want to do any reloading in production, which is slow and not recommended, you have to use the DB for persistence.
if you really want to go down the "reload" route, set config.cache_classes = true in config/environments/production.rb and specify the email in a constant outside the config directory (in some model or controller) like this EMAIL = 'whatever#email.com'. You would have to change the production code on each production machine, without a server restart. Sounds very hacky. Look into zero-downtime deploys à la Github for a more elegant solution. Redeploying should be cheap and painless.

Re-initialize ActiveRecord after rails startup

I'm building a system which allows the user to modify the database.yml contents via an admin frontend interface.
That changes to database.yml obviously don't have any affect until the application is restarted. Rather than forcing the user (who may not have SSH access to the physical box) to restar the application manually, I'd like a way to force ActiveRecord to reload the config post startup.
Rather than requiring them to restart the server, is there a way to force ActiveRecord to re-initialize after initial startup?
Rationale
There are two use cases for this - a) initial setup wizard b) moving from sqlite evaluation database to production supported database.
Initial Setup Wizard
After installing our application and starting it for the first time the user will be presented with a setup wizard, which amongst other things, allows the user to choose between the built in evaluation database using sqlite or to specify a production supported database. They need to specify the database properties. Rather than asking users to edit yml files on the server we wish the present a frontend to do so during initial setup.
Moving from sqlite evaluation database to production supported database
If the user opted to go with the built in evaluation database, but alter wants to migrate to a production database they need to update the database settings to reflect this change. Same reasons as above, a front end rather than requiring the user to edit config files is desired, especially since we can validate connectivity, permissions etc from the application rather than the user finding out it didn't work when they try to start the application and they get an activerecord exception.
Restart your Rails stack on the next request just as you would if you had access to the server.
system("touch #{File.join(Rails.root,'tmp','restart.txt')")
Building on #wless1's answer in order to get ERB-like ENV vars (e.g. <%= ENV['DB_HOST'] %>) working try this:
YAML::load(ERB.new(File.read(File.join(Rails.root, "config/database.yml"))).result)
ActiveRecord::Base.establish_connection config[Rails.env]
Theoretically, you could achieve this with the following:
config = YAML::load File.read(File.join(Rails.root, "config/database.yml"))
ActiveRecord::Base.establish_connection config[Rails.env]
You'd have to execute the reconnection code in every rails process you're running for it to be effective. There's a bunch of dangerous things to worry about here though - race conditions when changing the database config, syntax problems in the new database.yml, etc.

Rails: differentiating staging from production

I've got a production server and a staging sever in which new features are tested before moving them to production. The staging server is physically different from the production one (different hosts with different urls), but it mimics it as much as possible (i.e. same packages, same gems, etc).
Rails.env = 'production' on both servers.
My problem is that in some cases I need different behaviour on staging than in production.
For example, a new feature might send massive emails to users on production; but while I'm testing it I'd rather have them sent to a 'test' email account.
What is the best way to detect the server I'm at?
I'd like to do it as "raily" as possible.
Thanks a lot.
Generally, this is why you'd use different environments. Practically speaking, a staging environment is usually very close to production, but with things like real emails turned off.
You aren't limited to development/test/production - you can run under an environment named anything you want. Just create a config/environments/staging.rb file, set the values you want in there, and start your app with RAILS_ENV=staging - that's all there is to it. That way you can emulate your production environment, but twiddle features on or off as desired when you don't want them active before you actually go live.
I am afraid this answer isn't terribly helpful.
The railsy way is to have environments differ only in configuration (asset host, database etc.) for different environments. So a different database with users having dummy or test email addresses would be easiest way to go about it.
If you are generally cloning from Production, I recommend updating emails of all users, either through script/dbconsole or script/console or just a plain simple rake task.
And if you want to limit / control the features, then I'd recommend doing that through source control, i.e. by have different revisions deployed.

Why does the server need to restart when a model file is updated?

I'd like to know why is there a need to restart the server (Mongrel/WEBrick) every time a model file is updated? I know it doesn't get loaded in if you don't do it - but is there any documentation out there that would explain why it does so?
Thanks!
Development environments do not require you to restart the server if you change a model. They will reload the environment for each request if necessary.
Production environments are a different story. A Rails server (mongrel/passenger/webrick/etc) running in a production environment will only load your Rails environment once when the process is started. This takes a couple of seconds, as you might notice when starting the console which also loads your Rails environment. To avoid this overhead for each request the server will spawn a new thread from the loaded environment to handle each incoming request.
Because the server only responds to HTTP requests, and the usual signals. There's no good way to force an environment reload beyond always loading a fresh environment (like a development environment, or restarting the server.

Resources