Class: Magick::Config

Inherits:
Object
  • Object
show all
Defined in:
lib/magick/config.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize ⇒ Config

Returns a new instance of Config.



8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/magick/config.rb', line 8

def initialize
  @warn_on_deprecated = false
  @async_updates = false
  @async_queue_limit = nil # nil => Adapters::AsyncWriter::DEFAULT_QUEUE_LIMIT
  @async_enqueue_timeout = nil # nil => Adapters::AsyncWriter::DEFAULT_ENQUEUE_TIMEOUT
  @memory_ttl = 3600 # 1 hour
  @circuit_breaker_threshold = 5
  @circuit_breaker_timeout = 60
  @redis_namespace = 'magick:features'
  @redis_db = nil # Use default database (0) unless specified
  @environment = defined?(::Rails) ? ::Rails.env.to_s : 'development'
end

Instance Attribute Details

#active_record_model_class ⇒ Object

Returns the value of attribute active_record_model_class.



5
6
7
# File 'lib/magick/config.rb', line 5

def active_record_model_class
  @active_record_model_class
end

#adapter_registry ⇒ Object

Returns the value of attribute adapter_registry.



5
6
7
# File 'lib/magick/config.rb', line 5

def adapter_registry
  @adapter_registry
end

#async_enqueue_timeout ⇒ Object

Returns the value of attribute async_enqueue_timeout.



5
6
7
# File 'lib/magick/config.rb', line 5

def async_enqueue_timeout
  @async_enqueue_timeout
end

#async_queue_limit ⇒ Object

Returns the value of attribute async_queue_limit.



5
6
7
# File 'lib/magick/config.rb', line 5

def async_queue_limit
  @async_queue_limit
end

#async_updates(enabled: true, queue_limit: nil, enqueue_timeout: nil) ⇒ Object

Asynchronous Redis writes. All async writes go through ONE serialized writer thread, so they reach Redis in the order they were issued and a burst costs one thread, not one per write.

queue_limit     — pending writes held before backpressure kicks in.
enqueue_timeout — seconds a caller blocks on a full queue before the
                write is dropped with a warning.


167
168
169
# File 'lib/magick/config.rb', line 167

def async_updates
  @async_updates
end

#audit_log(enabled: true, adapter: nil, retention: AuditLog::DEFAULT_RETENTION, max_entries: AuditLog::DEFAULT_MAX_ENTRIES, persist: true) ⇒ Object

enabled: false disables audit logging entirely (Magick.audit_log is nil). adapter: optional host-supplied sink; receives #append(entry) for every entry. retention: entries kept per feature in the durable, shared store. max_entries: entries kept in the per-process ring, across all features. persist: false keeps the ring (and any host adapter) but writes nothing to Redis/ActiveRecord — for hosts whose own adapter is the system of record.



140
141
142
# File 'lib/magick/config.rb', line 140

def audit_log
  @audit_log
end

#circuit_breaker_threshold ⇒ Object

Returns the value of attribute circuit_breaker_threshold.



5
6
7
# File 'lib/magick/config.rb', line 5

def circuit_breaker_threshold
  @circuit_breaker_threshold
end

#circuit_breaker_timeout ⇒ Object

Returns the value of attribute circuit_breaker_timeout.



5
6
7
# File 'lib/magick/config.rb', line 5

def circuit_breaker_timeout
  @circuit_breaker_timeout
end

#environment(name) ⇒ Object

Returns the value of attribute environment.



5
6
7
# File 'lib/magick/config.rb', line 5

def environment
  @environment
end

#memory_ttl(seconds) ⇒ Object

Returns the value of attribute memory_ttl.



5
6
7
# File 'lib/magick/config.rb', line 5

def memory_ttl
  @memory_ttl
end

#performance_metrics(enabled: true, redis_tracking: nil, batch_size: 100, flush_interval: 60, **_options) ⇒ Object

Returns the value of attribute performance_metrics.



5
6
7
# File 'lib/magick/config.rb', line 5

def performance_metrics
  @performance_metrics
end

#redis_db ⇒ Object

Returns the value of attribute redis_db.



5
6
7
# File 'lib/magick/config.rb', line 5

def redis_db
  @redis_db
end

#redis_namespace ⇒ Object

Returns the value of attribute redis_namespace.



5
6
7
# File 'lib/magick/config.rb', line 5

def redis_namespace
  @redis_namespace
end

#redis_url ⇒ Object

Returns the value of attribute redis_url.



5
6
7
# File 'lib/magick/config.rb', line 5

def redis_url
  @redis_url
end

#versioning(enabled: true, max_versions: Versioning::DEFAULT_MAX_VERSIONS) ⇒ Object

Returns the value of attribute versioning.



5
6
7
# File 'lib/magick/config.rb', line 5

def versioning
  @versioning
end

#warn_on_deprecated(enabled: true) ⇒ Object

Returns the value of attribute warn_on_deprecated.



5
6
7
# File 'lib/magick/config.rb', line 5

def warn_on_deprecated
  @warn_on_deprecated
end

Instance Method Details

#active_record(model_class: nil, primary: false, **options) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/magick/config.rb', line 81

def active_record(model_class: nil, primary: false, **options)
  @active_record_model_class = model_class if model_class
  @active_record_primary = primary
  active_record_adapter = configure_active_record_adapter(model_class: model_class, **options)

  # Automatically create Registry adapter if it doesn't exist
  if @adapter_registry
    # If registry already exists, update it with the new Active Record adapter
    if active_record_adapter && @adapter_registry.is_a?(Adapters::Registry)
      @adapter_registry.instance_variable_set(:@active_record_adapter, active_record_adapter)
      # Update primary if specified
      @adapter_registry.instance_variable_set(:@primary, :active_record) if primary
    end
  else
    memory_adapter = configure_memory_adapter
    redis_adapter = configure_redis_adapter
    cb = Magick::CircuitBreaker.new(
      failure_threshold: @circuit_breaker_threshold,
      timeout: @circuit_breaker_timeout
    )
    primary_adapter = primary ? :active_record : :memory
    @adapter_registry = Adapters::Registry.new(
      memory_adapter,
      redis_adapter,
      active_record_adapter: active_record_adapter,
      circuit_breaker: cb,
      primary: primary_adapter,
      **async_registry_options
    )
  end

  active_record_adapter
end

#adapter(type, **options, &block) ⇒ Object

DSL methods for configuration



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/magick/config.rb', line 22

def adapter(type, **options, &block)
  case type.to_sym
  when :memory
    configure_memory_adapter(**options)
  when :redis
    configure_redis_adapter(**options)
  when :active_record
    configure_active_record_adapter(**options)
  when :registry
    if block_given?
      instance_eval(&block)
      configure_registry_adapter
    else
      configure_registry_adapter(**options)
    end
  else
    raise ArgumentError, "Unknown adapter type: #{type}"
  end
end

#apply! ⇒ Object



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
# File 'lib/magick/config.rb', line 210

def apply!
  # Apply configuration to Magick module
  Magick.adapter_registry = adapter_registry if adapter_registry

  # Set on whichever registry ends up live, including one built outside
  # this DSL, and only when the file said something: the registry's own
  # default must not be stomped by a nil here.
  if @refresh_interval_configured && Magick.adapter_registry.respond_to?(:refresh_interval=)
    Magick.adapter_registry.refresh_interval = @refresh_interval
  end

  # Apply performance metrics (preserve redis_tracking setting)
  if performance_metrics
    Magick.performance_metrics = performance_metrics
    # Re-apply redis_tracking setting after assignment (in case object was replaced)
    if defined?(@performance_metrics_redis_tracking) && !@performance_metrics_redis_tracking.nil?
      # Explicitly set value takes precedence
      Magick.performance_metrics.enable_redis_tracking(enable: @performance_metrics_redis_tracking)
    # Otherwise, auto-enable if Redis adapter is configured
    # Check Magick.adapter_registry (after it's been set) instead of local instance variable
    elsif Magick.adapter_registry.is_a?(Adapters::Registry) && Magick.adapter_registry.redis_available?
      # Always enable if Redis adapter is available (unless explicitly disabled above)
      Magick.performance_metrics.enable_redis_tracking(enable: true)
    end
  elsif Magick.performance_metrics
    # If no new performance_metrics was configured, but one exists, still try to enable Redis tracking
    # if Redis adapter is available and redis_tracking wasn't explicitly disabled
    # Only auto-enable if not explicitly disabled
    if Magick.adapter_registry.is_a?(Adapters::Registry) && Magick.adapter_registry.redis_available? && !(defined?(@performance_metrics_redis_tracking) && @performance_metrics_redis_tracking == false)
      Magick.performance_metrics.enable_redis_tracking(enable: true)
    end
  end

  # Read the ivars directly: calling the DSL methods here would re-run
  # them with their defaults and stomp explicit `enabled: false` settings.
  # Assign even when nil: `audit_log enabled: false` has to clear the
  # default instance Magick.configure creates, or opting out would leave
  # audit entries being written (durably, now) anyway.
  Magick.audit_log = @audit_log if @audit_log || @audit_log_configured
  Magick.versioning = @versioning if @versioning
  Magick.versioning_enabled = @versioning_enabled unless @versioning_enabled.nil?
  Magick.unknown_dependency_policy = @unknown_dependency_policy if @unknown_dependency_policy
  Magick.warn_on_deprecated = warn_on_deprecated
end

#circuit_breaker(threshold: nil, timeout: nil) ⇒ Object



155
156
157
158
# File 'lib/magick/config.rb', line 155

def circuit_breaker(threshold: nil, timeout: nil)
  @circuit_breaker_threshold = threshold if threshold
  @circuit_breaker_timeout = timeout if timeout
end

#memory(**options) ⇒ Object



42
43
44
# File 'lib/magick/config.rb', line 42

def memory(**options)
  configure_memory_adapter(**options)
end

#redis(url: nil, namespace: nil, db: nil, **options) ⇒ Object



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
76
77
78
79
# File 'lib/magick/config.rb', line 46

def redis(url: nil, namespace: nil, db: nil, **options)
  @redis_url = url if url
  @redis_namespace = namespace if namespace
  @redis_db = db if db
  redis_adapter = configure_redis_adapter(url: url, namespace: namespace, db: db, **options)

  # Automatically create Registry adapter if it doesn't exist
  # This allows users to just call `redis url: ...` without needing to call `adapter :registry`
  if @adapter_registry
    # If registry already exists, update it with the new Redis adapter
    # This allows reconfiguring Redis without recreating the registry
    if redis_adapter && @adapter_registry.is_a?(Adapters::Registry)
      # Swap the Redis adapter; the registry retires the subscriber that was
      # listening on the previous one before starting a new one.
      @adapter_registry.redis_adapter = redis_adapter
    end
  else
    memory_adapter = configure_memory_adapter
    active_record_adapter = configure_active_record_adapter if defined?(::ActiveRecord::Base)
    cb = Magick::CircuitBreaker.new(
      failure_threshold: @circuit_breaker_threshold,
      timeout: @circuit_breaker_timeout
    )
    @adapter_registry = Adapters::Registry.new(
      memory_adapter,
      redis_adapter,
      active_record_adapter: active_record_adapter,
      circuit_breaker: cb,
      **async_registry_options
    )
  end

  redis_adapter
end

#refresh_interval(seconds) ⇒ Object

Seconds between periodic re-reads of the shared backend (Adapters::Registry::DEFAULT_REFRESH_INTERVAL when unset). This bounds how stale a process can be when a Pub/Sub invalidation never reaches it. false (or nil / 0) turns the refresh off, leaving Pub/Sub as the only thing that updates a registered feature.



182
183
184
185
# File 'lib/magick/config.rb', line 182

def refresh_interval(seconds)
  @refresh_interval = seconds
  @refresh_interval_configured = true
end

#unknown_dependency_policy(policy = nil) ⇒ Object

How a prerequisite that exists neither in this process nor in the shared backend is treated: :satisfied (default, ignore it) or :unsatisfied (evaluate the dependent feature as off). See Magick.unknown_dependency_policy.



194
195
196
197
198
199
200
201
202
203
204
# File 'lib/magick/config.rb', line 194

def unknown_dependency_policy(policy = nil)
  return @unknown_dependency_policy if policy.nil?

  unless Magick::UNKNOWN_DEPENDENCY_POLICIES.include?(policy.to_sym)
    raise ArgumentError,
          "unknown_dependency_policy must be one of #{Magick::UNKNOWN_DEPENDENCY_POLICIES.inspect}, " \
          "got #{policy.inspect}"
  end

  @unknown_dependency_policy = policy.to_sym
end