Module: Apartment
- Defined in:
- lib/apartment.rb,
lib/apartment/cli.rb,
lib/apartment/config.rb,
lib/apartment/errors.rb,
lib/apartment/tenant.rb,
lib/apartment/current.rb,
lib/apartment/railtie.rb,
lib/apartment/version.rb,
lib/apartment/cli/pool.rb,
lib/apartment/migrator.rb,
lib/apartment/cli/seeds.rb,
lib/apartment/lifecycle.rb,
lib/apartment/cli/tenants.rb,
lib/apartment/pool_reaper.rb,
lib/apartment/pool_manager.rb,
lib/apartment/schema_cache.rb,
lib/apartment/test_fixtures.rb,
lib/apartment/cli/migrations.rb,
lib/apartment/concerns/model.rb,
lib/apartment/elevators/host.rb,
lib/apartment/instrumentation.rb,
lib/apartment/elevators/domain.rb,
lib/apartment/elevators/header.rb,
lib/apartment/tenant_validator.rb,
lib/apartment/elevators/generic.rb,
lib/apartment/elevators/host_hash.rb,
lib/apartment/elevators/subdomain.rb,
lib/apartment/schema_dumper_patch.rb,
lib/apartment/configs/mysql_config.rb,
lib/apartment/tenant_name_validator.rb,
lib/apartment/adapters/mysql2_adapter.rb,
lib/apartment/adapters/sqlite3_adapter.rb,
lib/apartment/adapters/trilogy_adapter.rb,
lib/apartment/adapters/abstract_adapter.rb,
lib/apartment/configs/postgresql_config.rb,
lib/apartment/elevators/first_subdomain.rb,
lib/apartment/patches/connection_handling.rb,
lib/apartment/patches/live_tenant_propagation.rb,
lib/apartment/adapters/postgresql_schema_adapter.rb,
lib/apartment/adapters/postgresql_database_adapter.rb,
lib/generators/apartment/install/install_generator.rb
Overview
rubocop:disable Metrics/ModuleLength
Defined Under Namespace
Modules: Adapters, Configs, Elevators, Instrumentation, Lifecycle, Model, Patches, SchemaCache, SchemaDumperPatch, Tenant, TenantNameValidator, TestFixtures Classes: AdapterNotFound, ApartmentError, CLI, Config, ConfigurationError, Current, DefaultTenantNotConfigured, DefaultTenantRequired, FixtureLifecycleViolation, InstallGenerator, Migrator, PendingMigrationError, PoolExhausted, PoolManager, PoolReaper, Railtie, SchemaLoadError, TenantExists, TenantNotFound, TenantRequired, TenantValidator
Constant Summary collapse
- ALWAYS_VALID_TENANT =
An always-valid validator, used when config.tenant_validator is false.
->(_name) { true }
- BUILT_IN_VALIDATOR_MUTEX =
Guards lazy construction of the built-in validator. A constant (not an ivar) so it survives clear_config, which nils @built_in_tenant_validator.
Mutex.new
- VERSION =
'4.0.0.alpha2'
Class Attribute Summary collapse
-
.adapter ⇒ Object
Lazy-loading adapter.
-
.config ⇒ Object
readonly
Returns the value of attribute config.
-
.pool_manager ⇒ Object
readonly
Returns the value of attribute pool_manager.
-
.pool_reaper ⇒ Object
readonly
Returns the value of attribute pool_reaper.
Class Method Summary collapse
-
.activate! ⇒ Object
Activate the ConnectionHandling patch on ActiveRecord::Base.
-
.activate_sql_query_tags! ⇒ Object
Register a :tenant tag with ActiveRecord::QueryLogs so SQL queries include a /* tenant=‘name’ */ comment.
- .activated? ⇒ Boolean
-
.clear_config ⇒ Object
Reset all configuration and stop background tasks.
-
.configure {|new_config| ... } ⇒ Object
Configure Apartment v4.
-
.deregister_shard(pool_key) ⇒ Object
Deregister a single tenant’s shard from AR’s ConnectionHandler.
-
.excluded_models ⇒ Object
v3 compatibility: Apartment.excluded_models returns the excluded models list.
-
.pinned_model?(klass) ⇒ Boolean
Check if a class (or any of its ancestors) is a pinned model.
-
.pinned_models ⇒ Object
Registry of models that declared pin_tenant.
- .process_pinned_model(klass) ⇒ Object
- .register_pinned_model(klass) ⇒ Object
-
.reset_tenant_pools! ⇒ void
Deregister all tenant pools from AR’s ConnectionHandler and clear the pool manager cache.
-
.tenant_names ⇒ Object
Returns the current tenant list.
-
.tenant_validator ⇒ Object
Resolves config.tenant_validator to a callable: false -> always valid, nil -> the process’s built-in TenantValidator (memoized), a callable -> itself.
Class Attribute Details
.adapter ⇒ Object
Lazy-loading adapter. Built on first access via build_adapter. Can be set manually (e.g., in tests) via Apartment.adapter=.
51 52 53 |
# File 'lib/apartment.rb', line 51 def adapter @adapter ||= build_adapter end |
.config ⇒ Object (readonly)
Returns the value of attribute config.
46 47 48 |
# File 'lib/apartment.rb', line 46 def config @config end |
.pool_manager ⇒ Object (readonly)
Returns the value of attribute pool_manager.
46 47 48 |
# File 'lib/apartment.rb', line 46 def pool_manager @pool_manager end |
.pool_reaper ⇒ Object (readonly)
Returns the value of attribute pool_reaper.
46 47 48 |
# File 'lib/apartment.rb', line 46 def pool_reaper @pool_reaper end |
Class Method Details
.activate! ⇒ Object
Activate the ConnectionHandling patch on ActiveRecord::Base. Idempotent — prepend on an already-prepended module is a no-op.
195 196 197 198 199 |
# File 'lib/apartment.rb', line 195 def activate! require_relative('apartment/patches/connection_handling') ActiveRecord::Base.singleton_class.prepend(Patches::ConnectionHandling) @activated = true end |
.activate_sql_query_tags! ⇒ Object
Register a :tenant tag with ActiveRecord::QueryLogs so SQL queries include a /* tenant=‘name’ */ comment. No-op when sql_query_tags is false or ActiveRecord::QueryLogs is not available.
204 205 206 207 208 209 210 211 212 213 |
# File 'lib/apartment.rb', line 204 def return unless @config&. return unless defined?(ActiveRecord::QueryLogs) return if ActiveRecord::QueryLogs..include?(:tenant) ActiveRecord::QueryLogs.taggings = ActiveRecord::QueryLogs.taggings.merge( tenant: -> { Apartment::Current.tenant } ) ActiveRecord::QueryLogs. = ActiveRecord::QueryLogs. + [:tenant] end |
.activated? ⇒ Boolean
95 96 97 |
# File 'lib/apartment.rb', line 95 def activated? @activated == true end |
.clear_config ⇒ Object
Reset all configuration and stop background tasks.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/apartment.rb', line 175 def clear_config teardown_old_state # Restore (un-qualify) pinned models, but keep them registered. pin_tenant # runs once when a model's class body loads and never re-runs, so the # registry is the only record of which models are pinned. Discarding it # would strand every pinned model unprocessed after the next configure. # The registry is bounded in production (pinned models are named # constants); a test process that pins anonymous classes accumulates them # here — acceptable, but count-sensitive specs must isolate it themselves. @pinned_models&.each { |klass| klass.apartment_restore! if klass.respond_to?(:apartment_restore!) } @built_in_tenant_validator&.shutdown @built_in_tenant_validator = nil @config = nil @pool_manager = nil @pool_reaper = nil @activated = false end |
.configure {|new_config| ... } ⇒ Object
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/apartment.rb', line 147 def configure raise(ConfigurationError, 'Apartment.configure requires a block') unless block_given? new_config = Config.new yield(new_config) new_config.apply_defaults! new_config.validate! new_config.freeze! # Validation passed — tear down old state and swap in new. teardown_old_state @built_in_tenant_validator&.shutdown @built_in_tenant_validator = nil @config = new_config @pool_manager = PoolManager.new @pool_reaper = PoolReaper.new( pool_manager: @pool_manager, interval: new_config.pool_idle_timeout, idle_timeout: new_config.pool_idle_timeout, max_total: new_config.max_total_connections, default_tenant: new_config.default_tenant, shard_key_prefix: new_config.shard_key_prefix ) @pool_reaper.start @config end |
.deregister_shard(pool_key) ⇒ Object
Deregister a single tenant’s shard from AR’s ConnectionHandler. Safe to call when AR is not loaded or config is not set (no-op). Used by PoolReaper eviction, AbstractAdapter#drop, and teardown.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/apartment.rb', line 218 def deregister_shard(pool_key) return unless @config && defined?(ActiveRecord::Base) _, separator, role_str = pool_key.to_s.rpartition(':') role = separator.empty? || role_str.empty? ? ActiveRecord.writing_role : role_str.to_sym shard_key = :"#{@config.shard_key_prefix}_#{pool_key}" ActiveRecord::Base.connection_handler.remove_connection_pool( 'ActiveRecord::Base', role: role, shard: shard_key ) rescue StandardError => e warn "[Apartment] Failed to deregister AR pool for #{pool_key}: #{e.class}: #{e.}" end |
.excluded_models ⇒ Object
v3 compatibility: Apartment.excluded_models returns the excluded models list. Deprecated in v4 (use Apartment::Model + pin_tenant instead).
124 125 126 127 128 |
# File 'lib/apartment.rb', line 124 def excluded_models raise(ConfigurationError, 'Apartment not configured. Call Apartment.configure first.') unless @config @config.excluded_models end |
.pinned_model?(klass) ⇒ Boolean
Check if a class (or any of its ancestors) is a pinned model. Delegates to the class’s own apartment_pinned? (defined by the Apartment::Model concern). Falls back to registry lookup for models registered via the excluded_models shim without the concern.
87 88 89 90 91 92 93 |
# File 'lib/apartment.rb', line 87 def pinned_model?(klass) if klass.respond_to?(:apartment_pinned?) klass.apartment_pinned? else klass.ancestors.any? { |a| a.is_a?(Class) && pinned_models.include?(a) } end end |
.pinned_models ⇒ Object
Registry of models that declared pin_tenant. Uses Concurrent::Set for thread safety (Zeitwerk autoload in threaded servers).
75 76 77 |
# File 'lib/apartment.rb', line 75 def pinned_models @pinned_models ||= Concurrent::Set.new end |
.process_pinned_model(klass) ⇒ Object
130 131 132 133 134 135 136 137 |
# File 'lib/apartment.rb', line 130 def process_pinned_model(klass) unless adapter warn "[Apartment] Cannot process pinned model #{klass.name || klass.inspect}: " \ 'adapter not initialized. Model registered but unprocessed.' return end adapter.process_pinned_model(klass) end |
.register_pinned_model(klass) ⇒ Object
79 80 81 |
# File 'lib/apartment.rb', line 79 def register_pinned_model(klass) pinned_models.add(klass) end |
.reset_tenant_pools! ⇒ void
This method returns an undefined value.
Deregister all tenant pools from AR’s ConnectionHandler and clear the pool manager cache. Pools rebuild lazily on the next connection_pool call.
Execution context (Apartment::Current: tenant, tenant_override, etc.) is left untouched — pool lifecycle and tenant context are separate concerns. A caller that also wants to drop tenant context resets it explicitly via Apartment::Tenant.reset.
Called automatically by Apartment::TestFixtures before Rails’ fixture setup iterates shards. Can also be called manually in custom test harnesses that cycle tenant pools between examples.
249 250 251 252 253 |
# File 'lib/apartment.rb', line 249 def reset_tenant_pools! guard_pinned_pools_during_fixtures! deregister_all_tenant_pools @pool_manager&.clear end |
.tenant_names ⇒ Object
Returns the current tenant list. Single resolver used by Tenant.each, Migrator, SchemaCache, and the CLI commands. Honors the per-block override set by Tenant.with_tenants_provider / with_tenants when present; otherwise resolves through @config.tenants_provider.
The override (or the configured provider) may itself be a callable, in which case it is invoked on every access. Whatever the source, the resolved value must respond to :each.
107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/apartment.rb', line 107 def tenant_names raise(ConfigurationError, 'Apartment not configured. Call Apartment.configure first.') unless @config override = Current.tenant_override source = override || @config.tenants_provider result = source.respond_to?(:call) ? source.call : source unless result.respond_to?(:each) source_label = override ? 'tenant_override' : 'tenants_provider' raise(ConfigurationError, "#{source_label} must return an Enumerable, got #{result.class}") end result end |
.tenant_validator ⇒ Object
Resolves config.tenant_validator to a callable: false -> always valid, nil -> the process’s built-in TenantValidator (memoized), a callable -> itself.
65 66 67 68 69 70 71 |
# File 'lib/apartment.rb', line 65 def tenant_validator case (configured = @config&.tenant_validator) when false then ALWAYS_VALID_TENANT when nil then built_in_tenant_validator else configured end end |