async IQ handlers need to be gracefully terminated on session destruction
Here's a rather hairy issue:
- a short network outage happens
- poezio notices and reconnects
- poezio stops working reliably, always losing connection, until I restart it manually
I always was assuming there is a race condition between multiple ongoing connection attempts, but maybe this is just an unlucky handler combination.
In ping.py, await is used to schedule a ping timeout on the connection. The only abort condition is IqTimeout
. I've seen this handler, initiated by an old session, fire after poezio successfully reconnected and established a new session. This implies that the pending IQ timeout handler wasn't cancelled when the old session was terminated.
Therefore, we need some way to cancel all pending IQs when they are not sensible any more, e.g. by delivering a different type of exception (SessionClosed something) that can be ignored by the IQ sender. Something like this:
try:
rtt = await self.ping(self.xmpp.boundjid.host, timeout=self.timeout)
except SessionClosed:
log.debug("Old session is gone. No further action required.")
except IqTimeout:
log.debug("Did not receive ping back in time. " + \
"Requesting Reconnect.")
self.xmpp.reconnect(0.0, "Ping timeout after %ds" % self.timeout)
else:
log.debug('Keepalive RTT: %s' % rtt)