0

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?

Eli Duke
  • 454
  • 1
  • 3
  • 14

2 Answers2

1

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.

Eli Duke
  • 454
  • 1
  • 3
  • 14
0

Thanks to Eli, got me in the right direction. I have updated Eli's code with latest chewy code.

# config/initializers/chewy_multi_tenancy.rb

module Chewy
  class Index
    def self.index_name(suggest = nil, prefix: nil, suffix: nil)
      tenant_prefix = [Apartment::Tenant.current, prefix]
      if suggest
        @base_name = (tenant_prefix + [suggest.to_s.presence]).reject(&:blank?).join('_')
      else
        (tenant_prefix + [ base_name, suffix ]).reject(&:blank?).join('_')
      end
    end
  end
end

and 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'].invoke
    end
  end
end
rlawrenz
  • 146
  • 3