Concurrency problems selecting connection to a specific database based on user with Rails ActiveRecord / PostgreSQL

Viewed 278

I have a setup where Rails 5 / Ruby 2.4.1 MRI application requires a connection to two databases, both on the same PostgreSQL instance, one shared between all users, one picked based on authenticated user. Both databases have connection pooling set to 5 connections.

I'm using base ActiveRecord classes for both cases, one something like

class Base1 < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "base1_#{Rails.env}".to_sym
end
class Base2 < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "base2_#{Rails.env}".to_sym
end

To dynamically select database per user I have before_action block in ApplicationController doing

dbconf = Base2.configurations[Rails.env].dup
dbconf['database'] = current_user.db
Base2.establish_connection(dbconf)

This seemed to work under Rails 4.2 single process per request model but after switching to Rails 5 / Puma I get occasional errors where queries related to classes inherited from Base2 fail. Puma has to run at least with two threads and requests have to come in parallel for the error to occur. Some debugging shows Base2ChildClass.connection.current_database to be the one specified for Base1.

Is it some (almost) known error in ActiveRecord connection pooling? Is my approach to db switching valid? I would understand if thread safety violations would mix up different db instances under Base2 but how on earth could Base2 child classes get Base1 connection into their hands?

0 Answers
Related