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

Channel.process_frames crashes when channel-close listener raises an exception #13

Closed
vitaly-krugl opened this issue Mar 16, 2012 · 1 comment

Comments

@vitaly-krugl
Copy link

[This is related to https://github.com//issues/11 and https://github.com//issues/12]

  1. Create a channel-close listener function that raises Exception() and register it via Channel.add_close_listener(); the exception doesn't have to come explicitly from the channel-closer listener: if the listener calls a Gevent API that results in context switch and then another greenlet kills the greenlet that was executing the channel-close listener, then a GreenletExit (or another exception such as LinkedFailed, etc.) would be injected into that greenlet, causing the exception to be raised in channel-close listener function.
  2. Then do something that causes the broker to close your channel; this happened to me with RabbitMQ when I set qos as ch.basic.qos(prefetch_count=1), but forgot to pass no_ack=False to ch.basic.consume(), and then sent some messages to an exchange that was bound to the consumer's queue; when my code ACK'ed the incoming message, the broker no longer had a matching message in the queue (since no_ack was True by default) and closed the channel to signal an error condition.

If the channel-close listener callback is called from the scope of self.dispatch(), then by the time the exception that was raised in my channel-close listener is caught by process_frames(), both self._connection and self._logger of the Channel instance are None (cleared by Channel._closed_cb()). Since self.logger maps to self._connection.logger and self.close() maps to self.channel.close(), both of these calls would result in AttributeError exception. E.g.,


  File "/Users/vkruglikov/Packages/haigha/agoragames-haigha-6540e82/build/lib/haigha/connection.py", line 318, in read_frames
    self._transport.process_channels( p_channels )
  File "/Users/vkruglikov/Packages/haigha/agoragames-haigha-6540e82/build/lib/haigha/transports/transport.py", line 32, in process_channels
    channel.process_frames()
  File "/Users/vkruglikov/Packages/haigha/agoragames-haigha-6540e82/build/lib/haigha/channel.py", line 148, in process_frames
    self.logger.error(
  File "/Users/vkruglikov/Packages/haigha/agoragames-haigha-6540e82/build/lib/haigha/channel.py", line 60, in logger
    return self._connection.logger
AttributeError: 'NoneType' object has no attribute 'logger'

def process_frames(self):
  """
  Process the input buffer.
  """
  while len(self._frame_buffer):
    try:
      # It would make sense to call next_frame, but it's technically faster
      # to repeat the code here.
      frame = self._frame_buffer.popleft()
      self.dispatch( frame )
    except ProtocolClass.FrameUnderflow:
      return
    except:
      self.logger.error( 
        "Failed to dispatch %s", frame, exc_info=True )
      self.close( 500, "Failed to dispatch %s"%(str(frame)) )
      return
@awestendorf
Copy link
Member

I think that this is resolved in master due to :

  • Channel state is now stored in the Channel object and is left alone after it is closed
  • No longer log in Channel.process_frames; exception is raised and user code can handle it

This is a complicated case that I can't readily repeat so please re-open if this is still broken. Fix will go out with today's release.

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