I’ve run into this a couple of times, where I really want to temporarily connect all my models to a different database, with the DB specified via URL instead of from the normal configuration.
ActiveRecord::Base.connected_to(url: 'postgres://....') do
Dog.create!
end
Right now [I think] you can accomplish the same thing this way:
original_connection_config = ActiveRecord::Base.connection_config
begin
ActiveRecord::Base.establish_connection(url)
Dog.create!
ensure
ActiveRecord::Base.establish_connection(original_connection_config)
end
I tried diving into the code but I got lost around the point it began using connection handlers. I could maybe pull something off with a bit of guidance! LMK and I’ll ask a bunch more questions
PS - I totes haven’t tried contributing to Rails core before, please please let me know if I should adjust my language / tone etc etc
If each of the several databases has the same (or very similar) schema, then this is essentially multi-tenancy – that is, these are different pots of data that use the same overall structure.
If that’s the case then might want to consider setting up Citus for Postgres and then using the activerecord-multi-tenant gem. Or check out using the Tenants on Different Servers option for the ros-apartment gem.
None of this works exactly. I’m trying to connect exactly the same code to two different databases with exactly the same schema - it’s just that one is staging and so doesn’t really have data, and one is prod and so does.
The larger context is writing a tool that’ll pull a coherent slice of data (so, from a record, all the associated records), since it can be pretty challenging to make good fake data. Why not use the real thing?
So - I want to connect to prod, pull some data (into, say, hashes) and then reconnect to whatever I was originally connected to (staging or local), and then say, push those hashes (with some tweaks) back into the “local” database.
Hey, so there’s a good reason this doesn’t work currently (and I won’t accept a change to support it) because what this would be doing in your example is completely removing and replacing the connection that was established on ActiveRecord::Base which is going to be your default database connection that’s created on boot. Doing this in a request is extremely dangerous since you are clobbering and making a brand new connection to a totally separate database. You could theoretically hand a user the wrong data when doing this. I originally did support creating connections on the fly but got so many bug reports I removed it since it required internal knowledge of how connections work. Supporting this would put applications in danger.
Essentially connected_to changes the context in which a connection is looked up (role or shard) but it does not actually hand back the connection nor does it ever establish a connection, only the models in the application will lookup or establish a connection. Since you could have a model Post that points to db_one and a model Dog that points to db_2 switching Base to a URL here would only change the connection for Post due to the way inheritance and connections work.
Since it sounds like you want this only to get data from one env and give it to another env you should simply write a script that calls establish connection and then returns it to the original connection. To avoid the clobbering problem you could consider making an abstract class and calling establish connection on that. Connections are uniquely keyed on the class name, so as long as the class that is establishing a connection is different from an existing connection class (like Base or ApplicationRecord) then you’ll avoid clobbering the existing connection and potentially creating a security vulnerability.