Class: Apartment::Config
- Inherits:
-
Object
- Object
- Apartment::Config
- Defined in:
- lib/apartment/config.rb
Overview
Configuration object for Apartment v4. Created via Apartment.configure block; validated after the block yields.
Constant Summary collapse
- VALID_STRATEGIES =
rubocop:disable Metrics/ClassLength
%i[schema database_name shard database_config].freeze
- VALID_ENVIRONMENTIFY_STRATEGIES =
[nil, :prepend, :append].freeze
Instance Attribute Summary collapse
-
#active_record_log ⇒ Object
Returns the value of attribute active_record_log.
-
#app_role ⇒ Object
Returns the value of attribute app_role.
-
#check_pending_migrations ⇒ Object
Returns the value of attribute check_pending_migrations.
-
#default_tenant ⇒ Object
Returns the value of attribute default_tenant.
-
#default_tenant_switch_allowed ⇒ Object
Returns the value of attribute default_tenant_switch_allowed.
-
#elevator ⇒ Object
Returns the value of attribute elevator.
-
#elevator_options ⇒ Object
Returns the value of attribute elevator_options.
-
#environmentify_strategy ⇒ Object
Returns the value of attribute environmentify_strategy.
-
#excluded_models ⇒ Object
Returns the value of attribute excluded_models.
-
#force_separate_pinned_pool ⇒ Object
Returns the value of attribute force_separate_pinned_pool.
-
#heal_tainted_connections ⇒ Object
Returns the value of attribute heal_tainted_connections.
-
#max_tenant_connections ⇒ Object
Returns the value of attribute max_tenant_connections.
-
#max_tenant_pools ⇒ Object
Returns the value of attribute max_tenant_pools.
-
#max_total_connections ⇒ Object
Returns the value of attribute max_total_connections.
-
#migration_role ⇒ Object
Returns the value of attribute migration_role.
-
#mysql_config ⇒ Object
readonly
Returns the value of attribute mysql_config.
-
#parallel_migration_threads ⇒ Object
Returns the value of attribute parallel_migration_threads.
-
#pool_idle_timeout ⇒ Object
Returns the value of attribute pool_idle_timeout.
-
#pool_overflow_policy ⇒ Object
Returns the value of attribute pool_overflow_policy.
-
#postgres_config ⇒ Object
readonly
Returns the value of attribute postgres_config.
-
#reap_in_test ⇒ Object
Returns the value of attribute reap_in_test.
-
#reaper_interval ⇒ Object
Returns the value of attribute reaper_interval.
-
#schema_cache_per_tenant ⇒ Object
Returns the value of attribute schema_cache_per_tenant.
-
#schema_file ⇒ Object
Returns the value of attribute schema_file.
-
#schema_load_strategy ⇒ Object
Returns the value of attribute schema_load_strategy.
-
#seed_after_create ⇒ Object
Returns the value of attribute seed_after_create.
-
#seed_data_file ⇒ Object
Returns the value of attribute seed_data_file.
-
#shard_key_prefix ⇒ Object
Returns the value of attribute shard_key_prefix.
-
#sql_query_tags ⇒ Object
Returns the value of attribute sql_query_tags.
-
#tenant_not_found_handler ⇒ Object
Returns the value of attribute tenant_not_found_handler.
-
#tenant_pool_size ⇒ Object
Returns the value of attribute tenant_pool_size.
-
#tenant_strategy ⇒ Object
Returns the value of attribute tenant_strategy.
-
#tenant_validator ⇒ Object
Returns the value of attribute tenant_validator.
-
#tenants_provider ⇒ Object
Returns the value of attribute tenants_provider.
-
#test_fixture_cleanup ⇒ Object
Returns the value of attribute test_fixture_cleanup.
Instance Method Summary collapse
-
#apply_defaults! ⇒ Object
Apply derived defaults that depend on user-set values.
-
#configure_mysql {|@mysql_config| ... } ⇒ Object
Configure MySQL-specific options via block.
-
#configure_postgres {|@postgres_config| ... } ⇒ Object
Configure PostgreSQL-specific options via block.
-
#effective_pool_budget ⇒ Object
The pool-count bound the admission controller enforces: the stricter of the explicit pool cap (max_tenant_pools) and the pool budget derived from the connection ceiling (max_tenant_connections / tenant_pool_size, floored).
-
#freeze! ⇒ Object
Deep-freeze the config after validation to prevent post-boot mutation.
-
#initialize ⇒ Config
constructor
rubocop:disable Metrics/AbcSize.
-
#rails_env_name ⇒ Object
Returns the current Rails environment name, falling back to env vars and a safe default.
-
#validate! ⇒ Object
Validate configuration completeness and consistency.
Constructor Details
#initialize ⇒ Config
rubocop:disable Metrics/AbcSize
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/apartment/config.rb', line 33 def initialize # rubocop:disable Metrics/AbcSize @tenant_strategy = nil @tenants_provider = nil @default_tenant = nil @default_tenant_switch_allowed = true @excluded_models = [] @tenant_pool_size = nil @pool_idle_timeout = 300 @reaper_interval = nil @max_total_connections = nil @max_tenant_pools = nil @max_tenant_connections = nil @pool_overflow_policy = :evict_idle @seed_after_create = false @seed_data_file = nil @schema_load_strategy = nil @schema_file = nil @parallel_migration_threads = 0 @environmentify_strategy = nil @elevator = nil @elevator_options = {} @tenant_not_found_handler = nil @tenant_validator = nil @active_record_log = false @sql_query_tags = false @postgres_config = nil @mysql_config = nil @shard_key_prefix = 'apartment' @migration_role = nil @app_role = nil @schema_cache_per_tenant = false @check_pending_migrations = true @force_separate_pinned_pool = false @reap_in_test = false # Reset a tenant connection left in an aborted transaction when it is checked # back into its pool. On by default: without it the poisoned connection is # served to the next caller, and under pool-per-tenant that connection is the # tenant's ONLY connection, so the tenant is dead on that worker until the # process restarts. ActiveRecord's active? cannot detect the state. # PostgreSQL-only in effect. See docs/designs/transaction-taint-detection.md. @heal_tainted_connections = true @test_fixture_cleanup = true end |
Instance Attribute Details
#active_record_log ⇒ Object
Returns the value of attribute active_record_log.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def active_record_log @active_record_log end |
#app_role ⇒ Object
Returns the value of attribute app_role.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def app_role @app_role end |
#check_pending_migrations ⇒ Object
Returns the value of attribute check_pending_migrations.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def check_pending_migrations @check_pending_migrations end |
#default_tenant ⇒ Object
Returns the value of attribute default_tenant.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def default_tenant @default_tenant end |
#default_tenant_switch_allowed ⇒ Object
Returns the value of attribute default_tenant_switch_allowed.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def default_tenant_switch_allowed @default_tenant_switch_allowed end |
#elevator ⇒ Object
Returns the value of attribute elevator.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def elevator @elevator end |
#elevator_options ⇒ Object
Returns the value of attribute elevator_options.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def @elevator_options end |
#environmentify_strategy ⇒ Object
Returns the value of attribute environmentify_strategy.
14 15 16 |
# File 'lib/apartment/config.rb', line 14 def environmentify_strategy @environmentify_strategy end |
#excluded_models ⇒ Object
Returns the value of attribute excluded_models.
14 15 16 |
# File 'lib/apartment/config.rb', line 14 def excluded_models @excluded_models end |
#force_separate_pinned_pool ⇒ Object
Returns the value of attribute force_separate_pinned_pool.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def force_separate_pinned_pool @force_separate_pinned_pool end |
#heal_tainted_connections ⇒ Object
Returns the value of attribute heal_tainted_connections.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def heal_tainted_connections @heal_tainted_connections end |
#max_tenant_connections ⇒ Object
Returns the value of attribute max_tenant_connections.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def max_tenant_connections @max_tenant_connections end |
#max_tenant_pools ⇒ Object
Returns the value of attribute max_tenant_pools.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def max_tenant_pools @max_tenant_pools end |
#max_total_connections ⇒ Object
Returns the value of attribute max_total_connections.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def max_total_connections @max_total_connections end |
#migration_role ⇒ Object
Returns the value of attribute migration_role.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def migration_role @migration_role end |
#mysql_config ⇒ Object (readonly)
Returns the value of attribute mysql_config.
14 15 16 |
# File 'lib/apartment/config.rb', line 14 def mysql_config @mysql_config end |
#parallel_migration_threads ⇒ Object
Returns the value of attribute parallel_migration_threads.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def parallel_migration_threads @parallel_migration_threads end |
#pool_idle_timeout ⇒ Object
Returns the value of attribute pool_idle_timeout.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def pool_idle_timeout @pool_idle_timeout end |
#pool_overflow_policy ⇒ Object
Returns the value of attribute pool_overflow_policy.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def pool_overflow_policy @pool_overflow_policy end |
#postgres_config ⇒ Object (readonly)
Returns the value of attribute postgres_config.
14 15 16 |
# File 'lib/apartment/config.rb', line 14 def postgres_config @postgres_config end |
#reap_in_test ⇒ Object
Returns the value of attribute reap_in_test.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def reap_in_test @reap_in_test end |
#reaper_interval ⇒ Object
Returns the value of attribute reaper_interval.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def reaper_interval @reaper_interval end |
#schema_cache_per_tenant ⇒ Object
Returns the value of attribute schema_cache_per_tenant.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def schema_cache_per_tenant @schema_cache_per_tenant end |
#schema_file ⇒ Object
Returns the value of attribute schema_file.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def schema_file @schema_file end |
#schema_load_strategy ⇒ Object
Returns the value of attribute schema_load_strategy.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def schema_load_strategy @schema_load_strategy end |
#seed_after_create ⇒ Object
Returns the value of attribute seed_after_create.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def seed_after_create @seed_after_create end |
#seed_data_file ⇒ Object
Returns the value of attribute seed_data_file.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def seed_data_file @seed_data_file end |
#shard_key_prefix ⇒ Object
Returns the value of attribute shard_key_prefix.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def shard_key_prefix @shard_key_prefix end |
#sql_query_tags ⇒ Object
Returns the value of attribute sql_query_tags.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def @sql_query_tags end |
#tenant_not_found_handler ⇒ Object
Returns the value of attribute tenant_not_found_handler.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def tenant_not_found_handler @tenant_not_found_handler end |
#tenant_pool_size ⇒ Object
Returns the value of attribute tenant_pool_size.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def tenant_pool_size @tenant_pool_size end |
#tenant_strategy ⇒ Object
Returns the value of attribute tenant_strategy.
14 15 16 |
# File 'lib/apartment/config.rb', line 14 def tenant_strategy @tenant_strategy end |
#tenant_validator ⇒ Object
Returns the value of attribute tenant_validator.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def tenant_validator @tenant_validator end |
#tenants_provider ⇒ Object
Returns the value of attribute tenants_provider.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def tenants_provider @tenants_provider end |
#test_fixture_cleanup ⇒ Object
Returns the value of attribute test_fixture_cleanup.
17 18 19 |
# File 'lib/apartment/config.rb', line 17 def test_fixture_cleanup @test_fixture_cleanup end |
Instance Method Details
#apply_defaults! ⇒ Object
Apply derived defaults that depend on user-set values. Runs after the configure block yields and before validate!, so validate! stays read-only.
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/apartment/config.rb', line 132 def apply_defaults! # PostgreSQL's default schema is 'public'; avoid forcing every user to set it. @default_tenant ||= 'public' if @tenant_strategy == :schema # Reap on the idle-timeout cadence unless an explicit interval decouples # the two (reap more often without shrinking the idle window). @reaper_interval = @pool_idle_timeout if @reaper_interval.nil? # max_total_connections is deprecated: the name said "connections" but it # always capped tenant-pool COUNT. Alias it to its true meaning without # changing behavior. Only fill when max_tenant_pools was not set explicitly, # so validate!'s both-set guard can still catch a genuine double-spec. return unless @max_total_connections warn '[Apartment] DEPRECATION: config.max_total_connections is deprecated and will be ' \ 'removed in v5. It caps tenant-pool COUNT, not connections; rename it to ' \ 'max_tenant_pools. For a true connection ceiling, set max_tenant_connections.' @max_tenant_pools = @max_total_connections if @max_tenant_pools.nil? end |
#configure_mysql {|@mysql_config| ... } ⇒ Object
Configure MySQL-specific options via block.
112 113 114 115 116 |
# File 'lib/apartment/config.rb', line 112 def configure_mysql @mysql_config = Configs::MysqlConfig.new yield(@mysql_config) if block_given? @mysql_config end |
#configure_postgres {|@postgres_config| ... } ⇒ Object
Configure PostgreSQL-specific options via block.
105 106 107 108 109 |
# File 'lib/apartment/config.rb', line 105 def configure_postgres @postgres_config = Configs::PostgresqlConfig.new yield(@postgres_config) if block_given? @postgres_config end |
#effective_pool_budget ⇒ Object
The pool-count bound the admission controller enforces: the stricter of the explicit pool cap (max_tenant_pools) and the pool budget derived from the connection ceiling (max_tenant_connections / tenant_pool_size, floored). Returns nil (uncapped) when neither knob is set. Relies on a global tenant_pool_size, so tenant-pool connections <= budget * tenant_pool_size.
304 305 306 307 |
# File 'lib/apartment/config.rb', line 304 def effective_pool_budget derived = @max_tenant_connections && @tenant_pool_size ? @max_tenant_connections / @tenant_pool_size : nil [@max_tenant_pools, derived].compact.min end |
#freeze! ⇒ Object
Deep-freeze the config after validation to prevent post-boot mutation. Freezes mutable collections and sub-configs, then freezes self.
120 121 122 123 124 125 126 127 128 |
# File 'lib/apartment/config.rb', line 120 def freeze! @excluded_models.freeze @elevator_options.freeze @postgres_config&.freeze! @mysql_config&.freeze! # schema_file is a simple string, no deep freeze needed @app_role.freeze if @app_role.is_a?(String) freeze end |
#rails_env_name ⇒ Object
Returns the current Rails environment name, falling back to env vars and a safe default.
295 296 297 |
# File 'lib/apartment/config.rb', line 295 def rails_env_name (Rails.env if defined?(Rails.env)) || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'default_env' end |
#validate! ⇒ Object
Validate configuration completeness and consistency. Raises ConfigurationError on invalid state.
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/apartment/config.rb', line 153 def validate! # rubocop:disable Metrics/AbcSize raise(ConfigurationError, 'tenant_strategy is required') unless @tenant_strategy if @default_tenant.is_a?(String) && @default_tenant.strip.empty? raise(ConfigurationError, 'default_tenant cannot be an empty string') end unless [true, false].include?(@default_tenant_switch_allowed) raise(ConfigurationError, 'default_tenant_switch_allowed must be true or false, got: ' \ "#{@default_tenant_switch_allowed.inspect}") end unless @tenants_provider.respond_to?(:call) raise(ConfigurationError, 'tenants_provider must be a callable (e.g., -> { Tenant.pluck(:name) })') end if @postgres_config && @mysql_config raise(ConfigurationError, 'Cannot configure both Postgres and MySQL at the same time') end @postgres_config&.validate! if @tenant_pool_size && (!@tenant_pool_size.is_a?(Integer) || @tenant_pool_size < 1) raise(ConfigurationError, "tenant_pool_size must be a positive integer or nil, got: #{@tenant_pool_size.inspect}") end unless @pool_idle_timeout.is_a?(Numeric) && @pool_idle_timeout.positive? raise(ConfigurationError, "pool_idle_timeout must be a positive number, got: #{@pool_idle_timeout.inspect}") end # nil is valid pre-apply_defaults! (it derives from pool_idle_timeout); a # set value must be a positive number. if @reaper_interval && (!@reaper_interval.is_a?(Numeric) || !@reaper_interval.positive?) raise(ConfigurationError, "reaper_interval must be a positive number or nil, got: #{@reaper_interval.inspect}") end if @max_total_connections && (!@max_total_connections.is_a?(Integer) || @max_total_connections < 1) raise(ConfigurationError, "max_total_connections must be a positive integer or nil, got: #{@max_total_connections.inspect}") end if @max_tenant_pools && (!@max_tenant_pools.is_a?(Integer) || @max_tenant_pools < 1) raise(ConfigurationError, "max_tenant_pools must be a positive integer or nil, got: #{@max_tenant_pools.inspect}") end if @max_total_connections && @max_tenant_pools && @max_total_connections != @max_tenant_pools raise(ConfigurationError, 'max_total_connections and max_tenant_pools are the same setting under two names; ' \ "set only one. Got max_total_connections=#{@max_total_connections}, " \ "max_tenant_pools=#{@max_tenant_pools}") end if @max_tenant_connections && (!@max_tenant_connections.is_a?(Integer) || @max_tenant_connections < 1) raise(ConfigurationError, "max_tenant_connections must be a positive integer or nil, got: #{@max_tenant_connections.inspect}") end if @max_tenant_connections && @tenant_pool_size.nil? raise(ConfigurationError, 'max_tenant_connections requires tenant_pool_size to be set (the pool budget is ' \ 'derived as max_tenant_connections / tenant_pool_size)') end if @max_tenant_connections && @tenant_pool_size && @max_tenant_connections < @tenant_pool_size raise(ConfigurationError, "max_tenant_connections (#{@max_tenant_connections}) must be >= tenant_pool_size " \ "(#{@tenant_pool_size}); it cannot fit a single tenant pool") end unless %i[evict_idle raise].include?(@pool_overflow_policy) raise(ConfigurationError, 'pool_overflow_policy must be :evict_idle or :raise, ' \ "got: #{@pool_overflow_policy.inspect}") end unless [nil, :schema_rb, :sql].include?(@schema_load_strategy) raise(ConfigurationError, "Invalid schema_load_strategy: #{@schema_load_strategy.inspect}. " \ 'Must be nil, :schema_rb, or :sql') end if @migration_role && !@migration_role.is_a?(Symbol) raise(ConfigurationError, "migration_role must be nil or a Symbol, got: #{@migration_role.inspect}") end if @app_role && !@app_role.is_a?(String) && !@app_role.respond_to?(:call) raise(ConfigurationError, "app_role must be nil, a String, or a callable, got: #{@app_role.inspect}") end unless [true, false].include?(@schema_cache_per_tenant) raise(ConfigurationError, "schema_cache_per_tenant must be true or false, got: #{@schema_cache_per_tenant.inspect}") end unless [true, false].include?(@check_pending_migrations) raise(ConfigurationError, "check_pending_migrations must be true or false, got: #{@check_pending_migrations.inspect}") end unless [true, false].include?(@force_separate_pinned_pool) raise(ConfigurationError, "force_separate_pinned_pool must be true or false, got: #{@force_separate_pinned_pool.inspect}") end unless [true, false].include?(@test_fixture_cleanup) raise(ConfigurationError, "test_fixture_cleanup must be true or false, got: #{@test_fixture_cleanup.inspect}") end unless [true, false].include?(@reap_in_test) raise(ConfigurationError, "reap_in_test must be true or false, got: #{@reap_in_test.inspect}") end unless [true, false].include?(@heal_tainted_connections) raise(ConfigurationError, 'heal_tainted_connections must be true or false, ' \ "got: #{@heal_tainted_connections.inspect}") end unless @tenant_validator.nil? || @tenant_validator == false || @tenant_validator.respond_to?(:call) raise(ConfigurationError, 'tenant_validator must be nil, false, or a callable, ' \ "got: #{@tenant_validator.inspect}") end if @tenant_not_found_handler && !@tenant_not_found_handler.respond_to?(:call) raise(ConfigurationError, 'tenant_not_found_handler must be nil or a callable, ' \ "got: #{@tenant_not_found_handler.inspect}") end return if @shard_key_prefix.is_a?(String) && @shard_key_prefix.match?(/\A[a-z_][a-z0-9_]*\z/) raise(ConfigurationError, 'shard_key_prefix must be a lowercase string matching /[a-z_][a-z0-9_]*/, ' \ "got: #{@shard_key_prefix.inspect}") end |