Module: Magick::AdapterFailure

Defined in:
lib/magick/adapter_failure.rb

Overview

Makes a failed write to a shared backend visible.

The registry writes memory first and never rolls it back, so when the Redis or ActiveRecord write behind it fails, this process keeps serving a value no other process has. That divergence used to be reported with a bare warn gated on Rails.env.development?, which meant production got no log line, no metric and no event — the split was invisible until someone noticed one container answering differently.

Every report now produces, in every environment:

* an error-severity log line (`Rails.logger` when there is one, `$stderr`
otherwise), sanitized through Magick::LogSafe
* a `magick.feature_flag.adapter_write_failed` event on the structured
event channel, so hosts can alert on it

Reporting is strictly best effort and never raises: a partial write must not turn into an exception for the caller.

Class Method Summary collapse

Class Method Details

.message_for(details) ⇒ Object



66
67
68
69
70
71
72
73
74
75
# File 'lib/magick/adapter_failure.rb', line 66

def self.message_for(details)
  target = details[:feature_name] ? " for '#{details[:feature_name]}'" : ''
  cause = if details[:error_class]
            "#{details[:error_class]}: #{details[:error_message]}"
          else
            details[:reason] || 'unknown error'
          end

  "Magick: #{details[:backend]} #{details[:operation]} failed#{target}: #{cause}"
end

.report(backend:, operation:, feature_name: nil, error: nil, reason: nil) ⇒ Object

backend — :redis or :active_record operation — the registry operation that failed (:set, :set_all_data, :delete, :publish_cache_invalidation, :async_write, :subscribe, :refresh) error — the exception that was rescued, when there was one reason — why the write did not happen, when there was no exception (e.g. the circuit breaker was open, so the write was dropped)



29
30
31
32
33
34
35
36
37
38
39
# File 'lib/magick/adapter_failure.rb', line 29

def self.report(backend:, operation:, feature_name: nil, error: nil, reason: nil)
  details = describe(backend: backend, operation: operation, feature_name: feature_name,
                     error: error, reason: reason)
  log(details)
  notify(details)
  nil
rescue StandardError
  # Observability must never break the caller, and never turn a partial
  # write into an exception.
  nil
end

.report_recovery(backend:, operation:) ⇒ Object

The counterpart to .report for a failure that has ended: the Pub/Sub subscriber that could not subscribe is listening again. Info severity, log only — the event channel carries failures, and a host that alerted on the failure sees the recovery in the same log. Never raises.



45
46
47
48
49
50
51
52
# File 'lib/magick/adapter_failure.rb', line 45

def self.report_recovery(backend:, operation:)
  message = "Magick: #{backend} #{operation} recovered"
  logger = rails_logger
  logger ? logger.info(message) : warn(message)
  nil
rescue StandardError
  nil
end