Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clarification or documentation about channel vs connection events #780

Open
soryy708 opened this issue Jan 6, 2025 · 1 comment
Open

Comments

@soryy708
Copy link

soryy708 commented Jan 6, 2025

From experimenting with [email protected] I noticed some undocumented behavior observing channel vs connection events. I don't know if I can rely on this behavior remaining consistent in future patch versions of amqplib, because it's currently undocumented.

It appears that observing channel.on('error', ...) changes the behavior of connection.on('error', ...), which also interacts with channel.on('close', ...)/connection.on('close', ...).
If this is an implementation detail, I'd like clarification in this issue. If it's intended as stable behavior, I believe this should be documented in the amqplib docs.


Observations:

  1. Given
    connection.on('error', ...) and connection.on('close', ...)
    (and no observers on channel)

    Making a "soft" error (as defined by RabbitMQ docs) triggers both connection.on('error', ...) and connection.on('close', ...)

  2. Given
    connection.on('error', ...), connection.on('close', ...), and channel.on('error', ...)

    Making a "soft" error triggers channel.on('error', ...), but neither connection.on('error', ...) or connection.on('close', ...)

  3. Given
    connection.on('error', ...), connection.on('close', ...), and channel.on('close', ...)

    Making a "soft" error triggers connection.on('error', ...), channel.on('close', ...), and connection.on('close', ...)

  4. Given
    connection.on('error', ...), connection.on('close', ...), channel.on('error', ...), and channel.on('close', ...)

    Making a "soft" error triggers channel.on('error', ...), channel.on('close', ...), but neither connection.on('error', ...) or connection.on('close', ...)

In tabular form:

image


This undocumented nuance is a problem in the following example situation:

Suppose the user has:

  • connection.on('error', logConnectionError)
  • connection.on('close', reconnectAndAcquireChannel)

So when a "soft" error occurs, it's logged, the connection is closed, and re-acquired. The system self-heals.

Later the user wants more granular logging, so the user adds also:

  • channel.on('error', logChannelError)

Now when a "soft" error occurs, it's logged, the channel closes, but the connection remains open. Re-acquire isn't run, so the system is in a pathologic state.

@cressie176
Copy link
Collaborator

Hi @soryy708,

Thank you for putting this all together so clearly. I agree the the behaviour should be documented. I'll take some time to reproduce what you've done and confirm the behaviour.

This undocumented nuance is a problem in the following example situation:

The approach I've taken with Rascal is to

  1. Listen for channel both error & close events, then attempt recovery using the existing connection.
  2. Listen for connection error & close events, then attempt recovery using a new connection.

There are some complications though - it was at one point possible to get both an error and close event, and even multiple error events (I'm not 100% this is still the case), so the code should only recover once. However, you can't simply use or unbind the listeners because an unhandled error event will crash the node application.

My preferred approach since writing rascal is to add error and close event handlers which publish a third lost event. I bind the recovery handler to the lost event using emitter.once so it is only triggered once, no matter how many close or error events are triggered. I never unbind the error and close event handlers so a repeat error event won't crash the application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants