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
- .message_for(details) ⇒ Object
-
.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).
-
.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.
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.(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:) = "Magick: #{backend} #{operation} recovered" logger = rails_logger logger ? logger.info() : warn() nil rescue StandardError nil end |