-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flexible switching with rails 5.2 #1
Conversation
Printing an Error message is a little bit annoying until invoke db:create. # Conflicts: # lib/apartment/railtie.rb
Use postgres db as default so that we can create/drop tenant databases as needed
Old connections are persisting among rake tasks called during db:reset
- Useful for when we want to reset our state, but hold on to the config
- Fixes some issues I was seeing with tests - Add a guard to return excluded models spec name
- If the default tenant db does not exist, setup_connection_specification_name was not getting called - Other tenants would fail to get created as a result because the switching mechanism was not triggering
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although we agree it isn't ideal, this is a fair compromise that meets our needs right now. Ideally, I think we have a hardcoded connection to the postgres
db that happens on init so we don't have to deal with the db not being found initially.
@derosm2 Moving the conversation from influitive#438 over here. I'm running rails 5.2.3 and ruby 2.6.1. My config file isn't set up yet as I can't run generate apartment:install. Once I installed the gem from the branch, my app is unable to start or run any rails commands. I created the config file manually and tried to set it up but it doesn't appear to make a difference. My question really is if there is a different setup or config that I need to get this gem working or the same config options from the original repo woks? I have a situation where I need to support tenants on different hosts running postgres db. I tried config.tenant_resolver = Apartment::Resolvers::Database and use_schema options per the documentation and that's doesn't work. Thanks! |
@derosm2 Just checking if I could please get some help. Thanks! |
@muyiwaoyeniyi What issues are you seeing when you try to run the rails commands? Also are you able to use Rails 6? |
@derosm2 Thanks for responding. I haven't tested with Rails 6. I'm running rails 5.2.3 and ruby 2.6.1. When I start my rails server, I get the error in the attachment below. It is coming from this file - abstract_adapter.rb:81 ... When I try running - bundle exec rails generate apartment:install - I get basically the same error and the command hangs And so I think there is a configuration you might have on your end that I'm missing which is causing the error. Could be related to this #1 (review) ? This is what I have in my gemfile: Thanks for the help! |
@muyiwaoyeniyi Looks like it's entering an infinite loop because it trying to connect to the default tenant, but there isn't one. Might be worth starting with an example config from the main repo # You can have Apartment route to the appropriate Tenant by adding some Rack middleware.
# Apartment can support many different "Elevators" that can take care of this routing to your data.
# Require whichever Elevator you're using below or none if you have a custom one.
#
# require 'apartment/elevators/generic'
# require 'apartment/elevators/domain'
require 'apartment/elevators/subdomain'
# require 'apartment/resolvers/schema'
#
# Apartment Configuration
#
Apartment.configure do |config|
# Add any models that you do not want to be multi-tenanted, but remain in the global (public) namespace.
# A typical example would be a Customer or Tenant model that stores each Tenant's information.
#
# config.excluded_models = %w{ Tenant }
# In order to migrate all of your Tenants you need to provide a list of Tenant names to Apartment.
# You can make this dynamic by providing a Proc object to be called on migrations.
# This object should yield either:
# - an array of strings representing each Tenant name.
# - a hash which keys are tenant names, and values custom db config (must contain all key/values required in database.yml)
#
# config.tenant_names = lambda{ Customer.pluck(:tenant_name) }
# config.tenant_names = ['tenant1', 'tenant2']
# config.tenant_names = [
# {
# adapter: 'postgresql',
# host: 'some_server',
# port: 5555,
# database: 'postgres' # this is not the name of the tenant's db
# # but the name of the database to connect to before creating the tenant's db
# # mandatory in postgresql
# schema_search_path: '"tenant1"'
# },
# 'tenant2' => {
# adapter: 'postgresql',
# database: 'postgres' # this is not the name of the tenant's db
# # but the name of the database to connect to before creating the tenant's db
# # mandatory in postgresql
#
# }
# }
#
config.tenant_names = lambda { ToDo_Tenant_Or_User_Model.pluck :database }
# The tenant decorator setting should be a callable which receives the tenant
# as an argument, and returns the a modified version of the tenant name which
# you want to use in the resolver as a database or schema name, for example.
#
# A typical use-case might be prepending or appending the rails environment,
# as shown below.
#
# config.tenant_decorator = ->(tenant){ "#{Rails.env}_#{tenant}" }
# The resolver is used to convert a tenant name into a full spec. The two
# provided resolvers are Database and Schema. When you issue
# Apartment.switch("some_tenant"){ ... }, Apartment passes "some_tenant" to
# the selected resolver (after it's been decorated). The Database resolver
# takes the decorated tenant name, and inserts it into the :database key of a
# full connection specification (the full spec is whatever the database spec
# was at Apartment initialization. The schema resolver, does the same but
# for the :schema_search_path option in the configuration.
#
# config.tenant_resolver = Apartment::Resolvers::Schema
end
# Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that
# you want to switch to.
# Rails.application.config.middleware.use 'Apartment::Elevators::Generic', lambda { |request|
# request.host.split('.').first
# }
# Rails.application.config.middleware.use 'Apartment::Elevators::Domain'
Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain'
# You can have Apartment route to the appropriate Tenant by adding some Rack middleware.
# Apartment can support many different "Elevators" that can take care of this routing to your data.
# Require whichever Elevator you're using below or none if you have a custom one.
#
# require 'apartment/elevators/generic'
# require 'apartment/elevators/domain'
require 'apartment/elevators/subdomain'
# require 'apartment/resolvers/schema'
#
# Apartment Configuration
#
Apartment.configure do |config|
# Add any models that you do not want to be multi-tenanted, but remain in the global (public) namespace.
# A typical example would be a Customer or Tenant model that stores each Tenant's information.
#
# config.excluded_models = %w{ Tenant }
# In order to migrate all of your Tenants you need to provide a list of Tenant names to Apartment.
# You can make this dynamic by providing a Proc object to be called on migrations.
# This object should yield either:
# - an array of strings representing each Tenant name.
# - a hash which keys are tenant names, and values custom db config (must contain all key/values required in database.yml)
#
# config.tenant_names = lambda{ Customer.pluck(:tenant_name) }
# config.tenant_names = ['tenant1', 'tenant2']
# config.tenant_names = [
# {
# adapter: 'postgresql',
# host: 'some_server',
# port: 5555,
# database: 'postgres' # this is not the name of the tenant's db
# # but the name of the database to connect to before creating the tenant's db
# # mandatory in postgresql
# schema_search_path: '"tenant1"'
# },
# 'tenant2' => {
# adapter: 'postgresql',
# database: 'postgres' # this is not the name of the tenant's db
# # but the name of the database to connect to before creating the tenant's db
# # mandatory in postgresql
#
# }
# }
#
config.tenant_names = lambda { ToDo_Tenant_Or_User_Model.pluck :database }
# The tenant decorator setting should be a callable which receives the tenant
# as an argument, and returns the a modified version of the tenant name which
# you want to use in the resolver as a database or schema name, for example.
#
# A typical use-case might be prepending or appending the rails environment,
# as shown below.
#
# config.tenant_decorator = ->(tenant){ "#{Rails.env}_#{tenant}" }
# The resolver is used to convert a tenant name into a full spec. The two
# provided resolvers are Database and Schema. When you issue
# Apartment.switch("some_tenant"){ ... }, Apartment passes "some_tenant" to
# the selected resolver (after it's been decorated). The Database resolver
# takes the decorated tenant name, and inserts it into the :database key of a
# full connection specification (the full spec is whatever the database spec
# was at Apartment initialization. The schema resolver, does the same but
# for the :schema_search_path option in the configuration.
#
# config.tenant_resolver = Apartment::Resolvers::Schema
end
# Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that
# you want to switch to.
# Rails.application.config.middleware.use 'Apartment::Elevators::Generic', lambda { |request|
# request.host.split('.').first
# }
# Rails.application.config.middleware.use 'Apartment::Elevators::Domain'
Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain' |
@derosm2 I will look at it again. I believe I set it up correctly but maybe not. I'll let you know. Thanks. |
I am running out of database connections. Is there a way of always establishing a single connect for each thread and not use a connection pool when connecting to tenant databases? I have a max connections in Postgres of 800 and I experience fork issues when raising it to 1000. |
This branch should obey the pool size and reuse connections where possible (and not create more pools). Not sure I'm following your situation exactly. can you provide some more details? |
Based off of this fork with thread safe fixes, so please review those changes as well!