def send_async( self, receiver_address: Address, queue_name: bytes, message: Message, ): if not self._running: return self.log.info( 'SEND ASYNC', receiver_address=to_normalized_address(receiver_address), message=message, queue_name=queue_name, ) if not is_binary_address(receiver_address): raise ValueError('Invalid address {}'.format( pex(receiver_address))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError( 'Do not use send_async for {} messages'.format( message.__class__.__name__), ) message_id = message.message_identifier async_result = AsyncResult() if isinstance(message, Processed): async_result.set( True) # processed messages shouldn't get a Delivered reply self._send_immediate(receiver_address, json.dumps(message.to_dict())) else: self._messageids_to_asyncresult[message_id] = async_result self._send_with_retry(receiver_address, async_result, json.dumps(message.to_dict()))
def send_async( self, receiver_address: Address, queue_name: bytes, message: Message, ): if not self._running: return self.log.info( 'SEND ASYNC', receiver_address=to_normalized_address(receiver_address), message=message, queue_name=queue_name, ) if not is_binary_address(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError( 'Do not use send_async for {} messages'.format(message.__class__.__name__), ) message_id = message.message_identifier async_result = AsyncResult() if isinstance(message, Processed): async_result.set(True) # processed messages shouldn't get a Delivered reply self._send_immediate(receiver_address, json.dumps(message.to_dict())) else: self._messageids_to_asyncresult[message_id] = async_result self._send_with_retry(receiver_address, async_result, json.dumps(message.to_dict()))
def send_async( self, receiver_address: typing.Address, queue_name: bytes, message: Message, ) -> AsyncResult: if not self._running: return self.log.debug( 'SEND ASYNC', receiver_address=to_normalized_address(receiver_address), message=message, queue_name=queue_name, ) if not is_binary_address(receiver_address): raise ValueError('Invalid address {}'.format(pex(receiver_address))) # These are not protocol messages, but transport specific messages if isinstance(message, (Delivered, Ping, Pong)): raise ValueError( 'Do not use send_async for {} messages'.format(message.__class__.__name__), ) if isinstance(message, SignedMessage) and not message.sender: # FIXME: This can't be right message.sender = self._client.user_id # Ignore duplicated messages message_id = message.message_identifier if message_id not in self._messageids_to_asyncresult: async_result = self._messageids_to_asyncresult[message_id] = AsyncResult() self._send_with_retry(receiver_address, async_result, json.dumps(message.to_dict())) return self._messageids_to_asyncresult[message_id]
def _send_immediate( self, queue_identifier: QueueIdentifier, message: Message, ): data = json.dumps(message.to_dict()) receiver_address = queue_identifier.recipient self._send_raw(receiver_address, data)
def _send_with_retry( self, queue_identifier: QueueIdentifier, message: Message, ): data = json.dumps(message.to_dict()) message_id = message.message_identifier receiver_address = queue_identifier.recipient reachable = {UserPresence.ONLINE, UserPresence.UNAVAILABLE} def retry(): timeout_generator = udp_utils.timeout_exponential_backoff( self._config['retries_before_backoff'], self._config['retry_interval'], self._config['retry_interval'] * 10, ) for delay in timeout_generator: status = self._address_to_presence.get(receiver_address) if status in reachable: self._send_raw(receiver_address, data) else: self.log.debug( 'Skipping SEND to unreachable node', receiver=pex(receiver_address), status=status, message=message, queue=queue_identifier, ) # equivalent of gevent.sleep, but bails out when stopping if self._stop_event.wait(delay): break # retry while our queue is valid if queue_identifier not in self._queueids_to_queues: self.log.debug( 'Queue cleaned, stop retrying', message=message, queue=queue_identifier, queueids_to_queues=self._queueids_to_queues, ) break # retry while the message is in queue # Delivered and Processed messages should eventually remove them message_in_queue = any( message_id == event.message_identifier for event in self._queueids_to_queues[queue_identifier] ) if not message_in_queue: break self._spawn(retry)