Search code examples
ruby-on-railspostgresqlelasticsearchmulti-tenantapartment-gem

Elasticsearch, Chewy, Postgres, and Apartment Multi-Tenancy


I have a multi-tenant rails-api project with rails 4.2.3 and ruby 2.2.2. I found lots of resources out there for dealing with multi-tenancy with rails and postgres, but not much regarding elasticsearch and more specifically the chewy gem. I posted an issue on the chewy gem github page, and I got some good feed back there that helped me eventually find a solution to my problem. I figured that it wouldn't hurt to also post it here for the greater good. Here are the specifics of my question.


I have recently switched from MySQL over to Postgres with multiple schemas, and am having trouble with rake chewy:reset:all. It looks as though it is defaulting to the "public" schema, but I want to specify the schema. I am using the apartment gem, so I put this in one of my indexes:

Apartment::Tenant.switch!('tenant_name')

That fixed the rake problem temporarily, but it got me thinking bigger about elasticsearch and chewy and multi-tenancy in general. Does chewy have any sort of implementation of that? If not, do you have any recommendations?


Solution

  • I created a chewy monkey patch initializer:

    # config/initializers/chewy_multi_tenancy.rb
    
    module Chewy
      class Index
        def self.index_name(suggest = nil)
          prefix = Apartment::Tenant.current
          if suggest
            @index_name = build_index_name(suggest, prefix: prefix)
          else
            @index_name = build_index_name(
                name.sub(/Index\Z/, '').demodulize.underscore,
                prefix: prefix
              ) if name
            end
          end
          @index_name or raise UndefinedIndex
        end
      end
    end
    

    And a custom rake task:

    # lib/tasks/elastic.rake
    
    namespace :elastic do
      desc "resets all indexes for a given tenant ('rake elastic:reset TENANT=tenant_name')"
      task reset: :environment do
        if ENV['TENANT'].nil?
          puts "Uh oh! You didn't specify a tenant!\n"
          puts "Example: rake elastic:reset TENANT=tenant_name"
          exit
        elsif !Apartment.tenant_names.include?(ENV['TENANT'])
          puts "That tenant doesn't exist. Please choose from the following:\n"
          puts Apartment.tenant_names
          exit
        else
          Apartment::Tenant.switch!(ENV['TENANT'])
          Rake::Task['chewy:reset:all'].invoke
        end
      end
    end
    

    Since I have a completely separate test cluster we don't need to prefix our indexes with "test", so I redefined prefix with the current tenant name. As far as I can tell right now, chewy hits the index_name method every time a specific index is called. It then grabs the correct users index for the current tenant.