def get_message(self, correlation_id): try: while correlation_id not in self.replies: self.consumer.channel.connection.client.drain_events( timeout=self.timeout) body, message = self.replies.pop(correlation_id) self.provider.handle_message(body, message) except socket.timeout: timeout_error = RpcTimeout(self.timeout) event = self.provider._reply_events.pop(correlation_id) event.send_exception(timeout_error) # timeout is implemented using socket timeout, so when it # fires the connection is closed, causing the reply queue # to be deleted self._setup_consumer() except (IOError, ConnectionError) as exc: for event in self.provider._reply_events.values(): rpc_connection_error = RpcConnectionError( 'Disconnected while waiting for reply: %s', exc) event.send_exception(rpc_connection_error) self.provider._reply_events.clear() # In case this was a temporary error, attempt to reconnect. If # we fail, the connection error will bubble. self._setup_consumer() except KeyboardInterrupt as exc: event = self.provider._reply_events.pop(correlation_id) event.send_exception(exc) # exception may have killed the connection self._setup_consumer()
def on_consume_ready(self): # This is called on re-connection, and is the best hook for detecting # disconnections. If we have any pending reply events, we were # disconnected, and may have lost replies (since reply queues auto # delete). for event in self._reply_events.values(): event.send_exception( RpcConnectionError('Disconnected while waiting for reply')) self._reply_events.clear()
def test_wait_already_disconnected(self, queue_consumer): correlation_id = 1 event = ConsumeEvent(queue_consumer, correlation_id) exc = RpcConnectionError() event.send_exception(exc) with pytest.raises(RpcConnectionError): event.wait() assert not queue_consumer.get_message.called
def _poll_messages(self): replies = {} correlation_id = yield while True: try: for body, msg in queue_iterator(self.queue, timeout=self.timeout): msg_correlation_id = msg.properties.get('correlation_id') if msg_correlation_id not in self.provider._reply_events: _logger.debug("Unknown correlation id: %s", msg_correlation_id) continue replies[msg_correlation_id] = (body, msg) # Here, and every time we re-enter this coroutine (at the # `yield` statement below) we check if we already have the # data for the new correlation_id before polling for new # messages. while correlation_id in replies: body, msg = replies.pop(correlation_id) self.provider.handle_message(body, msg) correlation_id = yield except RpcTimeout as exc: event = self.provider._reply_events.pop(correlation_id) event.send_exception(exc) # timeout is implemented using socket timeout, so when it # fires the connection is closed, causing the reply queue # to be deleted self._setup_queue() correlation_id = yield except ConnectionError as exc: for event in self.provider._reply_events.values(): rpc_connection_error = RpcConnectionError( 'Disconnected while waiting for reply: %s', exc) event.send_exception(rpc_connection_error) self.provider._reply_events.clear() # In case this was a temporary error, attempt to reconnect. If # we fail, the connection error will bubble. self._setup_queue() correlation_id = yield except KeyboardInterrupt as exc: event = self.provider._reply_events.pop(correlation_id) event.send_exception(exc) # exception may have killed the connection self._setup_queue() correlation_id = yield
def test_wait_disconnected_while_waiting(self, queue_consumer): correlation_id = 1 event = ConsumeEvent(queue_consumer, correlation_id) exc = RpcConnectionError() def get_message(correlation_id): event.send_exception(exc) queue_consumer.get_message.side_effect = get_message with pytest.raises(RpcConnectionError): event.wait() assert queue_consumer.get_message.call_args == call(correlation_id)