influitive / apartment

Database multi-tenancy for Rack (and Rails) applications
2.68k stars 461 forks source link

Apartment::TenantNotFound: Error when using with sidekiq #515

Open ajinkyapisal opened 6 years ago

ajinkyapisal commented 6 years ago

Steps to reproduce

  1. Checkout minimal sample app - https://github.com/ajinkyapisal/testapp
  2. Setup databases and create new tenant testapp_1
  3. run sample script rails runner sample.rb

Expected behavior

All messages are processed successfully by Sidekiq

Actual behavior

Messages are not processed as apartment gives TenantNotFound, Connection Pool errors Error log: https://github.com/ajinkyapisal/testapp/blob/master/sidekiq_error.log

System configuration

ajinkyapisal commented 6 years ago

After looking into the code, we saw that if we use mysql2_adapter then we did not see any connection pool issue. As we are using JRuby, so had a look at the JDBC Mysql adapter code and saw that it differs from the mysql2 adapter (it has the schema based adapter). We copied Mysql2SchemaAdapter to JDBCMysqlSchemaAdapter and after that, it worked.

I have created a branch for it.

https://github.com/ajinkyapisal/apartment/commit/ee9fad934494c08f20e7712a6d88be413046b17b

ajinkyapisal commented 6 years ago

@mikecmpbll can you please check above fix and let us know if what we did is correct fix or not? Thanks

ajinkyapisal commented 6 years ago

Update1: It works well when using the application but when creating new tenant. Apartment creates the database with no scehma_migrations table. I found out that it looks for default database schema_migrations when doing that hence it never runs any migration on a new tenant.

It looks my copy-pasting of Mysql2SchemaAdapter to JDBCMysqlSchemaAdapter was incorrect.

Final Update: Fixed main issue and above issue with migration using below code

require "apartment/adapters/abstract_jdbc_adapter"

module Apartment

  module Tenant
    def self.jdbc_mysql_adapter(config)
      adapter = Adapters::JDBCMysqlAdapter.new(config)
      adapter.process_excluded_models
      adapter
    end
  end

  module Adapters
    class JDBCMysqlAdapter < AbstractJDBCAdapter

      def initialize(config)
        super
        Apartment.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
        reset
      end

      def reset_on_connection_exception?
        true
      end
    end
  end
end

for some reason, I had to call adapter.process_excluded_models even though it's already called in Apartment::Tenant. If I dont' do that then it looks for Customer (excluded model) in current database instead of default database

Summary: This fix did not work : https://github.com/ajinkyapisal/apartment/commit/ee9fad934494c08f20e7712a6d88be413046b17b This fix did work : https://github.com/ajinkyapisal/apartment/commit/91c892538cb0d1ba1bcee7aeee41455fcdfa7f19

mikecmpbll commented 6 years ago

hi @ajinkyapisal , before I take a look at your code can you tell me what apartment configuration you're using?

edit: whups, didn't spot the sample app, perfect thx ;)