Hi, I’m junior developer on Rails and trying to implement multi-tenant sharding support to project that i’m working on.
I’m trying implement this feature as it said in the guides here on section 7 Caveats which is:
7.1 Automatic swapping for horizontal sharding
While Rails now supports an API for connecting to and swapping connections of shards, it does not yet support an automatic swapping strategy. Any shard swapping will need to be done manually in your app via a middleware or around_action
.
I defined an around_action
method both on ApplicationController
and ApplicationMailer
that decides with shard should be connected based on the tenant. I get the tenant information via current_user
method defined by Devise
gem in controller but i don’t have the same on ApplicationMailer
so i tried to pass the tenant information as parameter to mailer like this:
AccountMailer.with(account: current_user.account).inform_account.deliver_later
with this i can reach the tenant information in ApplicationMailer
like this:
class ApplicationMailer < ActionMailer::Base
around_action :db_shard, unless: :over_account_layer?
def db_shard(&block)
ActiveRecord::Base.connected_to(role: :writing, shard: fetch_shard, &block)
end
def fetch_shard
params[:account].shard.to_sym
end
def over_account_layer?
# If the mail is not bounded by a single account then DB connection must be set in that mailer
params[:over_account_layer]
end
end
This is my solution for the multi-tenant sharding support. But when i tried to do the same on the background jobs i couldn’t find the same functionality in the ActiveJob
as in ActionMailer
. ActiveJob
has an attribute called arguments
which is array not hash. I can do the similar functionality with this:
class ApplicationJob < ActiveJob::Base
around_perform :db_shard, unless: :the_job_over_account_layer?
def db_shard(&block)
ActiveRecord::Base.connected_to(role: :writing, shard: fetch_shard, &block)
end
def fetch_shard
# Assuming first argument will be always `Account` object
arguments.first.shard.to_sym
end
def the_job_over_account_layer?
# Things are getting difficult to handle here for me. I don't have a smooth solution here for now
# to disable sharding here and let the sharding on the subclass.
end
end
When i call a background job like this:
ExportProductsJob.perform_later(account, products)
I can get the account information with arguments.first
. But i think this is not a smooth way to do it since its not explicit. I can pass all the parameters in hash to a job but this way the arguments
array would looks like this:
#=> [{account: (account object), products: (products array)}]
Only one hash element in an array.
My Proposal:
It would be nice if i can call a background job similar to mailer like this:
ExportProductsJob.with(account: account).perform_later(account, products)
so that i could reach the account data in ApplicationJob
as in ApplicationMailer
with:
def fetch_shard
params[:account].shard.to_sym
end
This is my first post here and I tried my best while i explaining the situation.I’m sorry for my grammar and semantic mistakes. Thanks in advance.