Rails controllers without a database

At first glance, I'm sure the headline makes this sound like a question asked a million times earlier ... but it is not.

My application uses a database, but only some parts of the application actually rely on a database that works and works. I would like to make sure that parts of the application that are NOT database dependent can work correctly if / when the database is down.

The problem is that ... as soon as the Rails application realizes that it has lost its connection to the database, NO part of the application (except for static content) will work. (i.e., an exception is thrown before the execution thread ever reaches a controller that is database independent - a controller that will be very good if it is allowed to do this.)

Is there any way to achieve what I'm looking for? Any help is appreciated!

Update:

After careful study, I believe that the question boils down to the following:

Is there a way to use the "lazy" database connection pool so that the database connection is not retrieved from the pool until it is needed? If possible, this will allow queries that do not use the database at all to continue, even if / when the database is down.

Thoughts?

Update 2:

Adding a stack trace. This shows that control never reaches the controller when the database connection is unavailable. (The database is clearly down intentionally, so I can verify this.)

PG::Error could not connect to server: Connection refused Is the server running on host "localhost" (127.0.0.1) and accepting TCP/IP connections on port 5432? activerecord (4.0.0.beta1) lib/active_record/connection_adapters/postgresql_adapter.rb:771:in `initialize' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/postgresql_adapter.rb:771:in `new' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/postgresql_adapter.rb:771:in `connect' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/postgresql_adapter.rb:493:in `initialize' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/postgresql_adapter.rb:41:in `new' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/postgresql_adapter.rb:41:in `postgresql_connection' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:446:in `new_connection' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:456:in `checkout_new_connection' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:427:in `acquire_connection' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:364:in `block in checkout' /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:363:in `checkout' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:273:in `block in connection' /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:272:in `connection' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:552:in `retrieve_connection' activerecord (4.0.0.beta1) lib/active_record/connection_handling.rb:79:in `retrieve_connection' activerecord (4.0.0.beta1) lib/active_record/connection_handling.rb:53:in `connection' activerecord (4.0.0.beta1) lib/active_record/query_cache.rb:51:in `restore_query_cache_settings' activerecord (4.0.0.beta1) lib/active_record/query_cache.rb:43:in `rescue in call' activerecord (4.0.0.beta1) lib/active_record/query_cache.rb:32:in `call' activerecord (4.0.0.beta1) lib/active_record/connection_adapters/abstract/connection_pool.rb:632:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/callbacks.rb:29:in `block in call' activesupport (4.0.0.beta1) lib/active_support/callbacks.rb:373:in `_run__2745032424595922925__call__callbacks' activesupport (4.0.0.beta1) lib/active_support/callbacks.rb:78:in `run_callbacks' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/callbacks.rb:27:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/reloader.rb:64:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/remote_ip.rb:76:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/debug_exceptions.rb:17:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call' railties (4.0.0.beta1) lib/rails/rack/logger.rb:38:in `call_app' railties (4.0.0.beta1) lib/rails/rack/logger.rb:21:in `block in call' activesupport (4.0.0.beta1) lib/active_support/tagged_logging.rb:67:in `block in tagged' activesupport (4.0.0.beta1) lib/active_support/tagged_logging.rb:25:in `tagged' activesupport (4.0.0.beta1) lib/active_support/tagged_logging.rb:67:in `tagged' railties (4.0.0.beta1) lib/rails/rack/logger.rb:21:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/request_id.rb:21:in `call' rack (1.5.2) lib/rack/methodoverride.rb:21:in `call' rack (1.5.2) lib/rack/runtime.rb:17:in `call' activesupport (4.0.0.beta1) lib/active_support/cache/strategy/local_cache.rb:72:in `call' rack (1.5.2) lib/rack/lock.rb:17:in `call' actionpack (4.0.0.beta1) lib/action_dispatch/middleware/static.rb:64:in `call' railties (4.0.0.beta1) lib/rails/engine.rb:510:in `call' railties (4.0.0.beta1) lib/rails/application.rb:96:in `call' rack (1.5.2) lib/rack/lock.rb:17:in `call' rack (1.5.2) lib/rack/content_length.rb:14:in `call' rack (1.5.2) lib/rack/handler/webrick.rb:60:in `service' /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/httpserver.rb:138:in `service' /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/httpserver.rb:94:in `run' /usr/local/rvm/rubies/ruby-2.0.0-p0/lib/ruby/2.0.0/webrick/server.rb:295:in `block in start_thread' 
+4
source share
3 answers

I'm sorry that I'm answering about Rails 3, but I hope this helps a bit.

I have the same problem in slightly different circumstances, so I dug up the code for stacktraces and rails and figured out how to fix this in two lines.

TL DR:

 # config/application.rb # before your application declaration ActiveRecord::Railtie.initializers.reject! { |i| i.name == 'active_record.set_reloader_hooks' } # inside your application declaration config.middleware.delete ActiveRecord::QueryCache 

First, when the rails load, it calls ActionDispatch::Reloader.prepare! (once for production, each development request). This method is similar to installation in unit tests, it drops many things, among other things, it cleans connections from the connection pool and clears the cache of the circuit, and the latter checks the database connection from the pool. So the first line removes the initializer in active_record/railtie , which adds this callback. You probably want to do this only for the production environment, but this modification must be in application.rb or higher to work.

Secondly, rails really checks the database connection for each query, but this is done in the ActiveRecord::QueryCache middleware. I decided that I could do without him. If you really need it, I think you can enable and reset the cache in around_filter .

+1
source

You can get rid of database errors in your controllers with

 rescue_from ActiveRecord::StatementInvalid do |e| logger.info 'ActiveRecord error ignored in databaseless controller:' logger.info e.message end 

Rescue from ActiveRecord::StatementInvalid will probably cover all those database exceptions that you want to ignore, but you will have to figure it out yourself. The documentation states that ActiveRecord::StatementInvalid is called in the following case:

Raised when the SQL query cannot be executed by the database (for example, it often happens for MySQL when the old Ruby driver is used).

Otherwise, you could also get rid of the more general superclass ActiveRecord::ActiveRecordError , but that would cover all Active Record errors and probably not what you want.

Edit: this will not handle the case where you have no connection at all, so it is almost useless in your case. I will still keep the answer here if someone else should look for it.

0
source

what you probably want to do is similar to the above. create a parent controller, which all its inherited controllers are those that do not need db access. use rescue_from to capture these db errors ( StatementInvalid in this example)

 class DbDontCare < ApplicationController rescue_from ActiveRecord::StatementInvalid do |exception| # do some logging or whatever end end 

Then add your protected controllers to this area.

 class IHateDb < DbDontCare end 

etc.

You may need to add several rescue_from definitions to your parent controller to handle all possible exceptions.

0
source

Source: https://habr.com/ru/post/1485905/


All Articles