Module: EventHub
- Defined in:
- lib/eventhub/helper.rb,
lib/eventhub/base.rb,
lib/eventhub/logger.rb,
lib/eventhub/message.rb,
lib/eventhub/sleeper.rb,
lib/eventhub/version.rb,
lib/eventhub/constant.rb,
lib/eventhub/consumer.rb,
lib/eventhub/processor2.rb,
lib/eventhub/execution_id.rb,
lib/eventhub/configuration.rb,
lib/eventhub/docs_renderer.rb,
lib/eventhub/actor_watchdog.rb,
lib/eventhub/base_exception.rb,
lib/eventhub/correlation_id.rb,
lib/eventhub/actor_heartbeat.rb,
lib/eventhub/actor_publisher.rb,
lib/eventhub/actor_listener_amqp.rb,
lib/eventhub/actor_listener_http.rb,
lib/eventhub/patches/celluloid_logger.rb
Overview
EventHub patches for upstream gems.
Celluloid 0.18 (the last released version, ~2016) is incompatible with Ruby 3.x frozen-string-literal defaults. Its ‘Internals::Logger.crash` mutates string literals like:
def crash(string, exception)
if Celluloid.log_actor_crashes
string << "\n" << format_exception(exception) # FrozenError under Ruby 3.x
error string
end
@exception_handlers.each { |h| h.call(exception) }
end
The ‘string << …` raises FrozenError BEFORE the registered exception handlers fire. The actor thread then dies silently:
* no exit event is sent to the supervisor (no restart),
* no exit event is sent to linked sub-actors (they stay alive as zombies),
* no error is logged anywhere.
Externally the symptom is: an actor whose method raises (e.g. our ‘ActorListenerAmqp#restart` raising “Listener amqp is restarting…”) appears to be entering the raise but never actually dies, and the listener never gets restarted. We hit this in 1.28.0 testing: SIGHUP looked like it worked (Configuration reloaded, async.restart enqueued, restart entered) but the listener silently became a zombie.
Upstream fix is unlikely - Celluloid is unmaintained. We prepend a corrected ‘crash` that defrosts the input string before mutating it. Behavior is otherwise identical to the original.
Defined Under Namespace
Modules: Configuration, CorrelationId, ExecutionId, Helper, Patches Classes: ActorHeartbeat, ActorListenerAmqp, ActorListenerHttp, ActorPublisher, ActorWatchdog, BaseException, Consumer, DocsRenderer, LoggerProxy, Message, Processor2, Sleeper, Statistics
Constant Summary collapse
- VERSION =
"1.28.2".freeze
- EH_X_INBOUND =
"event_hub.inbound"- STATUS_INITIAL =
To be set when dispatcher needs to dispatch to first process step.
0- STATUS_SUCCESS =
To be set to indicate successful processed message. Dispatcher will routes message to the next step.
200- STATUS_RETRY =
To be set to trigger retry cycle controlled by the dispatcher
300- STATUS_RETRY_PENDING =
Set and used by the dispatcher only.
301- STATUS_INVALID =
Set before putting the message into a retry queue. Once message has been retried it will sent do the same step with status.code = STATUS_SUCCESS
400- STATUS_DEADLETTER =
Dispatcher will publish message to the invalid queue.
500- STATUS_SCHEDULE =
that message needs to be dead-lettered. Rejected messages could miss the status.code = STATUS_DEADLETTER due to the RabbitMQ deadletter exchange mechanism.
600- STATUS_SCHEDULE_RETRY =
To be set to trigger scheduler based on schedule block, proceses next process step
601- STATUS_SCHEDULE_PENDING =
To be set to trigger scheduler based on schedule block, retry actual process step
602- STATUS_CODE_TRANSLATION =
Set and used by the dispatcher only. Set before putting the scheduled message to the schedule queue.
{ STATUS_INITIAL => "STATUS_INITIAL", STATUS_SUCCESS => "STATUS_SUCCESS", STATUS_RETRY => "STATUS_RETRY", STATUS_RETRY_PENDING => "STATUS_RETRY_PENDING", STATUS_INVALID => "STATUS_INVALID", STATUS_DEADLETTER => "STATUS_DEADLETTER", STATUS_SCHEDULE => "STATUS_SCHEDULE", STATUS_SCHEDULE_RETRY => "STATUS_SCHEDULE_RETRY", STATUS_SCHEDULE_PENDING => "STATUS_SCHEDULE_PENDING" }
Class Method Summary collapse
-
.format_celluloid_exception(ex) ⇒ Object
Format a Celluloid actor exception with the dying actor’s class name so post-mortem analysis can identify which actor died.
- .logger ⇒ Object
Class Method Details
.format_celluloid_exception(ex) ⇒ Object
Format a Celluloid actor exception with the dying actor’s class name so post-mortem analysis can identify which actor died.
Important: inside an actor’s crash flow, ‘Celluloid.current_actor` returns the Proxy::Cell, whose `.class` goes through method_missing / the mailbox - which can hang on a dying actor. Read the raw actor object out of the thread-local instead and walk to the subject class via instance variables (no proxy round-trips).
42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/eventhub/base.rb', line 42 def self.format_celluloid_exception(ex) actor_name = begin actor = Thread.current[:celluloid_actor] if actor behavior = actor.instance_variable_get(:@behavior) subject = behavior&.instance_variable_get(:@subject) subject&.class&.name end rescue nil end prefix = actor_name ? "[#{actor_name}] " : "" "#{prefix}Exception occured: #{ex.class}: #{ex.}" end |
.logger ⇒ Object
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/eventhub/logger.rb', line 43 def self.logger unless defined?(@logger) base_logger = ::EventHub::Components::MultiLogger.new if Configuration.console_log_only base_logger.add_device( EventHub::Components::Logger.logstash_cloud(Configuration.name, Configuration.environment) ) else base_logger.add_device(Logger.new($stdout)) base_logger.add_device( EventHub::Components::Logger.logstash(Configuration.name, Configuration.environment) ) end @logger = LoggerProxy.new(base_logger) end @logger end |