def __init__(self, outgoing_publishes): self.max_inflight = 1 assert isinstance(outgoing_publishes, OutgoingPublishesBase) self.publishes = outgoing_publishes self.packets = deque() self.retrial_handles = dict() self.future = DummyFuture()
def clear(self): """ Clears the in-memory state of the outgoing queue. The data in persistence is left as is. If there is any pending `Future` waiting for result, it is cancelled. """ for packet_id in self.retrial_handles.keys(): self.cancel_retrial(packet_id) self.packets.clear() self.retrial_handles.clear() if not self.future.done(): msg = 'Outgoing queue was cleansed' self.future.set_exception(CancelledException(msg)) self.future = DummyFuture()
def get(self): """ Gets the next packet to be sent to the remote client. :return: a `Future` """ if not self.future.done(): self.future.set_exception(CancelledException('Only one future supported')) self.future = Future() # try to start next publish flow first, # otherwise the outgoing packets would have to deplete before # any publish flows could start started = self._start_next_flow() if not started and self.packets: self.future.set_result(self.packets.popleft()) return self.future
def get(self): """ Gets the next packet to be sent to the remote client. :return: a `Future` """ if not self.future.done(): self.future.set_exception( CancelledException('Only one future supported')) self.future = Future() # try to start next publish flow first, # otherwise the outgoing packets would have to deplete before # any publish flows could start started = self._start_next_flow() if not started and self.packets: self.future.set_result(self.packets.popleft()) return self.future
class OutgoingQueue(): """ This class controls packets to be delivered to the remote client. It encapsulates the logic to send packets and start new publish flows. """ def __init__(self, outgoing_publishes): self.max_inflight = 1 assert isinstance(outgoing_publishes, OutgoingPublishesBase) self.publishes = outgoing_publishes self.packets = deque() self.retrial_handles = dict() self.future = DummyFuture() def retry_pending(self): for packet in self.publishes.get_all_inflight(): self.retrial_handles[packet.id] = None self.put(packet) def put(self, packet): """ Puts a packet to the outgoing queue. No checks are done on the provided packets. """ assert isinstance(packet, BaseMQTTMessage) if not self.future.done(): self.future.set_result(packet) else: self.packets.append(packet) def put_publish(self, packet): """ Puts a publish packet to the outgoing queue. If the QoS level is 0 it is only placed on the outgoing queue. Otherwise the packet is persisted and scheduled for publishing. """ assert isinstance(packet, Publish) if packet.qos == 0: self.put(packet) else: self.publishes.insert(packet) self._start_next_flow() def set_sent(self, packet_id): if self.publishes.is_inflight(packet_id): self.publishes.set_sent(packet_id) def is_sent(self, packet_id): return self.publishes.is_inflight(packet_id) and \ self.publishes.is_sent(packet_id) def is_pubconf(self, packet_id): return self.publishes.is_inflight(packet_id) and \ self.publishes.is_pubconf(packet_id) def set_pubconf(self, packet_id): if self.publishes.is_inflight(packet_id): self.publishes.set_pubconf(packet_id) def get(self): """ Gets the next packet to be sent to the remote client. :return: a `Future` """ if not self.future.done(): self.future.set_exception(CancelledException('Only one future supported')) self.future = Future() # try to start next publish flow first, # otherwise the outgoing packets would have to deplete before # any publish flows could start started = self._start_next_flow() if not started and self.packets: self.future.set_result(self.packets.popleft()) return self.future def clear(self): """ Clears the in-memory state of the outgoing queue. The data in persistence is left as is. If there is any pending `Future` waiting for result, it is cancelled. """ for packet_id in self.retrial_handles.keys(): self.cancel_retrial(packet_id) self.packets.clear() self.retrial_handles.clear() if not self.future.done(): msg = 'Outgoing queue was cleansed' self.future.set_exception(CancelledException(msg)) self.future = DummyFuture() def flow_completed(self, packet_id): """ Removes the publish corresponding to the `packet-id` from the inflight set and from persistence. """ if packet_id: self.publishes.remove(packet_id) if packet_id in self.retrial_handles: self.cancel_retrial(packet_id) del self.retrial_handles[packet_id] self._start_next_flow() def _start_next_flow(self): if self.publishes.inflight_len < self.max_inflight: packet = self.publishes.get_next() if packet: self.retrial_handles[packet.id] = None self.put(packet) return True return False def _retry_flow(self, packet_id): if self.publishes.is_inflight(packet_id): self.retrial_handles[packet_id] = None self.put(self.publishes.get_inflight(packet_id)) def set_retrial(self, packet_id, deadline): handle = self.retrial_handles.get(packet_id) if handle: self.cancel_retrial(packet_id) callback = lambda: self._retry(packet_id) handle = ioloop.IOLoop.current().add_timeout(deadline, callback) self.retrial_handles[packet_id] = handle def cancel_retrial(self, packet_id): handler = self.retrial_handles.get(packet_id) if handler is not None: ioloop.IOLoop.current().remove_timeout(handler) self.retrial_handles[packet_id] = None def _retry(self, packet_id): if packet_id in self.retrial_handles: self._retry_flow(packet_id)
class OutgoingQueue(): """ This class controls packets to be delivered to the remote client. It encapsulates the logic to send packets and start new publish flows. """ def __init__(self, outgoing_publishes): self.max_inflight = 1 assert isinstance(outgoing_publishes, OutgoingPublishesBase) self.publishes = outgoing_publishes self.packets = deque() self.retrial_handles = dict() self.future = DummyFuture() def retry_pending(self): for packet in self.publishes.get_all_inflight(): self.retrial_handles[packet.id] = None self.put(packet) def put(self, packet): """ Puts a packet to the outgoing queue. No checks are done on the provided packets. """ assert isinstance(packet, BaseMQTTMessage) if not self.future.done(): self.future.set_result(packet) else: self.packets.append(packet) def put_publish(self, packet): """ Puts a publish packet to the outgoing queue. If the QoS level is 0 it is only placed on the outgoing queue. Otherwise the packet is persisted and scheduled for publishing. """ assert isinstance(packet, Publish) if packet.qos == 0: self.put(packet) else: self.publishes.insert(packet) self._start_next_flow() def set_sent(self, packet_id): if self.publishes.is_inflight(packet_id): self.publishes.set_sent(packet_id) def is_sent(self, packet_id): return self.publishes.is_inflight(packet_id) and \ self.publishes.is_sent(packet_id) def is_pubconf(self, packet_id): return self.publishes.is_inflight(packet_id) and \ self.publishes.is_pubconf(packet_id) def set_pubconf(self, packet_id): if self.publishes.is_inflight(packet_id): self.publishes.set_pubconf(packet_id) def get(self): """ Gets the next packet to be sent to the remote client. :return: a `Future` """ if not self.future.done(): self.future.set_exception( CancelledException('Only one future supported')) self.future = Future() # try to start next publish flow first, # otherwise the outgoing packets would have to deplete before # any publish flows could start started = self._start_next_flow() if not started and self.packets: self.future.set_result(self.packets.popleft()) return self.future def clear(self): """ Clears the in-memory state of the outgoing queue. The data in persistence is left as is. If there is any pending `Future` waiting for result, it is cancelled. """ for packet_id in self.retrial_handles.keys(): self.cancel_retrial(packet_id) self.packets.clear() self.retrial_handles.clear() if not self.future.done(): msg = 'Outgoing queue was cleansed' self.future.set_exception(CancelledException(msg)) self.future = DummyFuture() def flow_completed(self, packet_id): """ Removes the publish corresponding to the `packet-id` from the inflight set and from persistence. """ if packet_id: self.publishes.remove(packet_id) if packet_id in self.retrial_handles: self.cancel_retrial(packet_id) del self.retrial_handles[packet_id] self._start_next_flow() def _start_next_flow(self): if self.publishes.inflight_len < self.max_inflight: packet = self.publishes.get_next() if packet: self.retrial_handles[packet.id] = None self.put(packet) return True return False def _retry_flow(self, packet_id): if self.publishes.is_inflight(packet_id): self.retrial_handles[packet_id] = None self.put(self.publishes.get_inflight(packet_id)) def set_retrial(self, packet_id, deadline): handle = self.retrial_handles.get(packet_id) if handle: self.cancel_retrial(packet_id) callback = lambda: self._retry(packet_id) handle = ioloop.IOLoop.current().add_timeout(deadline, callback) self.retrial_handles[packet_id] = handle def cancel_retrial(self, packet_id): handler = self.retrial_handles.get(packet_id) if handler is not None: ioloop.IOLoop.current().remove_timeout(handler) self.retrial_handles[packet_id] = None def _retry(self, packet_id): if packet_id in self.retrial_handles: self._retry_flow(packet_id)