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/cli/tenants.rb,
lib/apartment/pool_reaper.rb,
lib/apartment/pool_manager.rb,
lib/apartment/schema_cache.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/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/adapters/postgresql_schema_adapter.rb,
lib/apartment/adapters/postgresql_database_adapter.rb,
lib/generators/apartment/install/install_generator.rb
Defined Under Namespace
Modules: Adapters, Configs, Elevators, Instrumentation, Model, Patches, SchemaCache, SchemaDumperPatch, Tenant, TenantNameValidator Classes: AdapterNotFound, ApartmentError, CLI, Config, ConfigurationError, Current, InstallGenerator, Migrator, PendingMigrationError, PoolExhausted, PoolManager, PoolReaper, Railtie, SchemaLoadError, TenantExists, TenantNotFound
Constant Summary collapse
- VERSION =
'4.0.0.alpha1'
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.
-
.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
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=.
44 45 46 |
# File 'lib/apartment.rb', line 44 def adapter @adapter ||= build_adapter end |
.config ⇒ Object (readonly)
Returns the value of attribute config.
39 40 41 |
# File 'lib/apartment.rb', line 39 def config @config end |
.pool_manager ⇒ Object (readonly)
Returns the value of attribute pool_manager.
39 40 41 |
# File 'lib/apartment.rb', line 39 def pool_manager @pool_manager end |
.pool_reaper ⇒ Object (readonly)
Returns the value of attribute pool_reaper.
39 40 41 |
# File 'lib/apartment.rb', line 39 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.
122 123 124 125 126 |
# File 'lib/apartment.rb', line 122 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.
131 132 133 134 135 136 137 138 139 140 |
# File 'lib/apartment.rb', line 131 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
64 65 66 |
# File 'lib/apartment.rb', line 64 def activated? @activated == true end |
.clear_config ⇒ Object
Reset all configuration and stop background tasks.
105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/apartment.rb', line 105 def clear_config teardown_old_state # Reset per-model processing flags so re-configuration re-establishes connections. @pinned_models&.each do |klass| next unless klass.instance_variable_defined?(:@apartment_connection_established) klass.remove_instance_variable(:@apartment_connection_established) end @config = nil @pool_manager = nil @pool_reaper = nil @pinned_models = nil @activated = false end |
.configure {|new_config| ... } ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/apartment.rb', line 80 def configure raise(ConfigurationError, 'Apartment.configure requires a block') unless block_given? new_config = Config.new yield(new_config) new_config.validate! new_config.freeze! # Validation passed — tear down old state and swap in new. teardown_old_state @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.
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/apartment.rb', line 145 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 |
.pinned_model?(klass) ⇒ Boolean
Check if a class (or any of its ancestors) is a pinned model. Used by ConnectionHandling to skip tenant pool routing.
60 61 62 |
# File 'lib/apartment.rb', line 60 def pinned_model?(klass) klass.ancestors.any? { |a| a.is_a?(Class) && pinned_models.include?(a) } end |
.pinned_models ⇒ Object
Registry of models that declared pin_tenant. Uses Concurrent::Set for thread safety (Zeitwerk autoload in threaded servers).
50 51 52 |
# File 'lib/apartment.rb', line 50 def pinned_models @pinned_models ||= Concurrent::Set.new end |
.process_pinned_model(klass) ⇒ Object
68 69 70 |
# File 'lib/apartment.rb', line 68 def process_pinned_model(klass) adapter&.process_pinned_model(klass) end |
.register_pinned_model(klass) ⇒ Object
54 55 56 |
# File 'lib/apartment.rb', line 54 def register_pinned_model(klass) pinned_models.add(klass) end |