I use multible databases with ActiveRecord. I must put establish_connection db in all models. But i want to call the connection of libraries file. While put establish_connection db in for all models Database connection count is too much . How can I do other methods?
My project is a Ruby on Sinatra.
Build a global hash of {db_name => connection_instance } for all possible connections you have and retrieve them from your models intelligently:
def get_or_establish_connection db
$connections[db] ||= establish_connection db
end
That way you’ll reuse all already established connections.
NB I wrote “global” because there is no way to suggest anything more robust without knowledge of your architecture details. In the real life one would put this hash as a class variable somewhere accessible from everywhere.
What we did in our projects that used multiple databases was creating one class per database, that establish the connection, and make models inherit from it. That way, only one connection is created per database.
class UsageTable < ActiveRecord::Base
self.abstract_class = true
establish_connection :usage
end
class User < UsageTable
Related
Hanami uses Sequel as default ORM
There are different plugins for Sequel
Sometimes to use it you need to access to database connection object
For example to use PostgreSQL advisory locking for mutexes
There are such gems for this: sequel-advisory-locking, sequel-pg_advisory_lock
First, you should load an extension for Sequel::Database instance
DB.extension(:advisory_locking)
Then use it in your app like this
DB.advisory_lock('my_key') do
# do stuff with lock
end
But there is problem to use such libraries with Hanami
There is no information about it in official guide
On database configuration page such info:
Hanami models use ROM as a low-level backend. This means that you can easily use any Sequel plugins in your app. For this you need to define a gateway block in your model configuration, add the extension by calling extension on gateway.connection and pass the extension name in:
# config/environment.rb
Hanami.configure do
model do
gateway do |g|
g.connection.extension(:connection_validator)
end
end
end
What I tried is define constant in this config
Hanami.configure do
model do
gateway do |g|
DB = g.connection
DB.extension(:connection_validator)
DB.extension(:advisory_locking)
end
end
end
And then use this DB somewhere in the app, for example as DB.advisory_lock('my_key') { do_stuff_with_lock }
But such constants define within a block looks not good
How to get connection instance in Hanami app?
There is config object in Hanami -- Hanami::Model.configuration
In fact it is the getter for #configuration instance variable
It is instance of Hanami::Model::Configuration and has such instance variables like #url, #entities, #logger and others. So it's possible to call getters to get them
It also has connection method. This method returns gateway.connection
And this gateway.connection is just what is needed. This is exactly the instance of connection to the database, on which you can call methods
For example
Hanami::Model.configuration.connection.advisory_lock('my_lock') do
# do stuff with lock
end
I want to note that all these methods are indicated by the comment # #api private. It means that their implementation may be changed in future versions.
I'm writing a module which will use Sequel ORM. I want to be able to set the DB constant so it's available in the global namespace but I can't figure out a non-hacky way to do so. Here's what I have so far:
require 'sequel'
module BB
class Dal
def initialize(db_url)
self.class.connect(db_url)
end
def self.connect(db_url)
#db = Sequel.connect( db_url )
end
end
BB::Dal.connect(ENV['DATABASE_URL']) if ENV['DATABASE_URL']
end
As you can see I'd like to enable two modes of initialization. Either through the ENV['DATABASE_URL'] variable or through a constructor. What I'd like to do now is replace #db with DB. Any ideas?
You can just kick that out of the module context if you want:
DB = BB::Dal.connect(ENV['DATABASE_URL']) if ENV['DATABASE_URL']
It's worth noting that jamming these things in the root namespace is probably bad form. You might want to have a database connection module to organize these:
module DB
def connection
# Lazy initializer pattern applied here
#connection ||= BB::Dal.connect(ENV['DATABASE_URL'])
end
end
# ...
# Will connect the first time it's called, recycle the connection
# for all subsequent calls.
DB.connection.do_stuff
It really depends on how this will be used. The advantage of a method is it's very easy to upgrade to a connection pool should the occasion arise, whereas a constant is very rigid in terms of function, it must return a singular value with no opportunity to run code first.
I'm trying to upgrade the ActiveRecord gem to the latest 3.1.0 release and seeing a lot of exceptions being raised, I think it's due to how we handle multiple databases.
For each of our databases we specify a separate base class which inherits from ActiveRecord::Base, and call establish_connection in there. There are no cross-database relations. This has worked fine for us up until now.
Having upgraded to ActiveRecord 3.1.0 I'm seeing that it fails with an ActiveRecord::ConnectionNotEstablished exception, when traversing relations (i.e. it will successfully pull a single entity or set of them from the DB, but fails when navigating to a related class).
The top line of the backtrace is C:/Ruby/lib/ruby/gems/1.9.1/gems/activerecord-3.1.0/lib/active_record/connection_adapters/abstract/connection_pool.rb:410:in 'retrieve_connection', so I dug into this a little. The method is defined as follows:
def retrieve_connection(klass) #:nodoc:
pool = retrieve_connection_pool(klass)
(pool && pool.connection) or raise ConnectionNotEstablished
end
My simple test (puts Customer.first.address) calls retrieve_connection 3 times. Twice with Customer as the klass parameter, and once with ActiveRecord::Base as the parameter - which is when it fails as establish_connection has not been called for ActiveRecord::Base.
To the actual question then - is there a new recommended way of handling multiple database connections in ActiveRecord? If so, what is it?
If not, what could be causing this problem?
I ran into the same issue yesterday while upgrading to ActiveRecord 3.1.0. I can't speak to whether there is a new recommended way of handling multiple database connections in ActiveRecord 3.1, but I did find a way to unblock myself.
It appears a connection must now be established on ActiveRecord::Base in order for it to determine the table name lengths/rules of the adapter. Along with the rest of my connections established in my database initializer, I now also have an ActiveRecord::Base connection established to one of my DBs (it doesn't matter which one).
I'd like to think there's a better solution to be found, but I'm happy to be unblocked for now.
I am using this solution - What I was seeing was that when establish_connection was called in each of the OtherDb classes - there seemed to be alot of overhead reloading table definitions and I would randomly see issues every time the class def was reloaded.
# The idea here is to specify that a given model should use another
# database without having to change the entire inheritance hierarchy
# declare model for table in primary connection
class Bar < ActiveRecord::Base
# assume we have logic here that we don't want to refactor into a module
# but we do want to inherit in OtherDb::Bar
end
module Foo
# base model in Foo namespace - uses another db
class BaseConnection < ActiveRecord::Base
# OtherDb::Title.database contains a db config hash
# This would probably go in the initializers
establish_connection OtherDb::Title.database
end
# module used to override db connection
module OtherDb::Base
def retrieve_connection
# connection_handler.retrieve_connection(self) # normal behavior
connection_handler.retrieve_connection(Foo::BaseConnection) # use db from Foo::BaseConnection
end
end
# Foo::Bar is identical to ::Bar but is in another db
class Bar < ::Bar
extend OtherDb::Base
end
end
as all of us, I have some application. In database I store sensitive data and I want to overwrite ActiveRecord (or somewhere else?) to always add AND user_id = current_user statement to all SQL queries sent to database (I will add user_id column to all tables). I basically want to ensure, that all operations done by user, are performed ONLY on his data.
Regards,
Mateusz
Sounds like what you're after is a strategy for implementing multitenancy in your application.
You might want to checkout the multitenant gem which can help you to isolate your queries to data belonging to the current tenant.
Alternatively, you can enforce this at the database level using PostgreSQL schemas. Gay Naor has an excellent talk on implementing this strategy among others.
If you search on "multi-tenant rails apps" you'll find several other discussions on this problem.
There is no way to enforce it on application level, the only way to enforce it is to forbid to team to use Class.find/where/etc methods and allow to call them only on collections. So instead of Task.find you should always use current_user.tasks.find etc.
You can add AbstractModel with default scope and then inherit all other models from it:
class AbstractModel < ActiveRecord::Base
self.abstract_class = true
default_scope where(:user_id => Thread.current[:current_user])
end
class SomeModel < AbstractModel
...
end
I've used Thread.current[:current_user] because of current_user is inaccessible inside of models. You should assign current_user to Thread.current in some before_filter method in ApplicationController etc.
I'm looking for a database agnostic way to create new schemas. Something like
postgres_uri = "postgres://username:password#hostname/database"
mysql_uri = "mysql://username:password#hostname/database"
[postgres_uri, mysql_uri].each do |db|
connection = DB.connect(db_uri)
connection.create_schema("xyz")
end
Bonus marks for something that will work easily with the connection active_record establishes in rails.
The only thing that comes to mind is somehow having two active_record "instances" and running activerecord migrations on each. Sequel allows for the same thing, and even allows for string instance names.