From a9ca13f7d3a7d82c76acec268eb499c5e82e7a58 Mon Sep 17 00:00:00 2001 From: Adam Gibson Date: Sun, 30 Jul 2017 16:42:07 +0300 Subject: [PATCH] Implement reconnections correctly in IRC Uses protocol.ReconnectingClientFactory as superclass, which uses exponential backoff in retrying. Also change on_welcome_trigger callback to reset the status of message channels which have reconnected, so that they become operable once reconnected. --- jmdaemon/jmdaemon/irc.py | 11 ++++++++--- jmdaemon/jmdaemon/message_channel.py | 6 +++--- jmdaemon/test/test_message_channel.py | 9 +++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/jmdaemon/jmdaemon/irc.py b/jmdaemon/jmdaemon/irc.py index 9956fef..5670f77 100644 --- a/jmdaemon/jmdaemon/irc.py +++ b/jmdaemon/jmdaemon/irc.py @@ -44,7 +44,7 @@ def get_config_irc_channel(chan_name, btcnet): channel += "-test" return channel -class TxIRCFactory(protocol.ClientFactory): +class TxIRCFactory(protocol.ReconnectingClientFactory): def __init__(self, wrapper): self.wrapper = wrapper self.channel = self.wrapper.channel @@ -60,11 +60,16 @@ class TxIRCFactory(protocol.ClientFactory): if not self.wrapper.give_up: if reactor.running: log.info('Attempting to reconnect...') - reactor.callLater(self.wrapper.reconnect_interval, - connector.connect()) + protocol.ReconnectingClientFactory.clientConnectionLost(self, + connector, reason) def clientConnectionFailed(self, connector, reason): log.info('IRC connection failed') + if not self.wrapper.give_up: + if reactor.running: + log.info('Attempting to reconnect...') + protocol.ReconnectingClientFactory.clientConnectionFailed(self, + connector, reason) class IRCMessageChannel(MessageChannel): diff --git a/jmdaemon/jmdaemon/message_channel.py b/jmdaemon/jmdaemon/message_channel.py index 11725aa..e830e22 100644 --- a/jmdaemon/jmdaemon/message_channel.py +++ b/jmdaemon/jmdaemon/message_channel.py @@ -403,11 +403,11 @@ class MessageChannelCollection(object): message channel child threads. """ with self.mc_lock: - if self.welcomed: - return #This trigger indicates successful login - #so we update status. + #so we update status; this also triggers on reconnection. self.mc_status[mc] = 1 + if self.welcomed: + return #This way broadcasts orders or requests ONCE to ALL mchans #which are actually available. if not any([x == 0 for x in self.mc_status.values()]): diff --git a/jmdaemon/test/test_message_channel.py b/jmdaemon/test/test_message_channel.py index 163c36c..7c4f3ff 100644 --- a/jmdaemon/test/test_message_channel.py +++ b/jmdaemon/test/test_message_channel.py @@ -224,13 +224,14 @@ def test_setup_mc(): #try to send the transaction to a wrong cp: mcc.send_tx(["notrealcp"], "deadbeef") - #At this stage, dmcs0,2 should be "up" and 1 should be "down" + #At this stage, dmcs0,2 should be "up" and 1 should have been reset to 1 assert mcc.mc_status[dmcs[0]] == 1 - assert mcc.mc_status[dmcs[1]] == 2 + assert mcc.mc_status[dmcs[1]] == 1 assert mcc.mc_status[dmcs[2]] == 1 + #Not currently used: #simulate re-connection of dmcs[1] ; note that this code isn't used atm - mcc.on_connect_trigger(dmcs[1]) - assert mcc.mc_status[dmcs[1]] == 1 + #mcc.on_connect_trigger(dmcs[1]) + #assert mcc.mc_status[dmcs[1]] == 1 #Now trigger disconnection code; each mc one by one; the last should trigger #on_disconnect callback for m in dmcs: