commit 68dad21fb467ddb172884048a50f87978e11d3f8
parent dfdc1e1d25a87c3283f57e8bd98028352324ca8f
Author: SomberNight <somber.night@protonmail.com>
Date: Sun, 1 Dec 2019 23:24:43 +0100
network: make best_effort_reliable smarter and a bit more lenient
related: #5815
Diffstat:
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/electrum/interface.py b/electrum/interface.py
@@ -374,7 +374,7 @@ class Interface(Logger):
self.logger.info(f'disconnecting due to: {repr(e)}')
return
- def mark_ready(self):
+ def _mark_ready(self) -> None:
if self.ready.cancelled():
raise GracefulDisconnect('conn establishment was too slow; *ready* future was cancelled')
if self.ready.done():
@@ -512,7 +512,7 @@ class Interface(Logger):
self.tip = height
if self.tip < constants.net.max_checkpoint():
raise GracefulDisconnect('server tip below max checkpoint')
- self.mark_ready()
+ self._mark_ready()
await self._process_header_at_tip()
self.network.trigger_callback('network_updated')
await self.network.switch_unwanted_fork_interface()
diff --git a/electrum/network.py b/electrum/network.py
@@ -290,6 +290,7 @@ class Network(Logger):
self.nodes_retry_time = time.time()
# the main server we are currently communicating with
self.interface = None # type: Interface
+ self.default_server_changed_event = asyncio.Event()
# set of servers we have an ongoing connection with
self.interfaces = {} # type: Dict[str, Interface]
self.auto_connect = self.config.get('auto_connect', True)
@@ -730,10 +731,13 @@ class Network(Logger):
i = self.interfaces[server]
if old_interface != i:
self.logger.info(f"switching to {server}")
+ assert i.ready.done(), "interface we are switching to is not ready yet"
blockchain_updated = i.blockchain != self.blockchain()
self.interface = i
await i.group.spawn(self._request_server_info(i))
self.trigger_callback('default_server_changed')
+ self.default_server_changed_event.set()
+ self.default_server_changed_event.clear()
self._set_status('connected')
self.trigger_callback('network_updated')
if blockchain_updated: self.trigger_callback('blockchain_updated')
@@ -840,30 +844,27 @@ class Network(Logger):
b.update_size()
def best_effort_reliable(func):
- async def make_reliable_wrapper(self, *args, **kwargs):
+ async def make_reliable_wrapper(self: 'Network', *args, **kwargs):
for i in range(10):
iface = self.interface
# retry until there is a main interface
if not iface:
- await asyncio.sleep(0.1)
- continue # try again
- # wait for it to be usable
- iface_ready = iface.ready
- iface_disconnected = iface.got_disconnected
- await asyncio.wait([iface_ready, iface_disconnected], return_when=asyncio.FIRST_COMPLETED)
- if not iface_ready.done() or iface_ready.cancelled():
- await asyncio.sleep(0.1)
+ try:
+ await asyncio.wait_for(self.default_server_changed_event.wait(), 1)
+ except asyncio.TimeoutError:
+ pass
continue # try again
+ assert iface.ready.done(), "interface not ready yet"
# try actual request
success_fut = asyncio.ensure_future(func(self, *args, **kwargs))
- await asyncio.wait([success_fut, iface_disconnected], return_when=asyncio.FIRST_COMPLETED)
+ await asyncio.wait([success_fut, iface.got_disconnected], return_when=asyncio.FIRST_COMPLETED)
if success_fut.done() and not success_fut.cancelled():
if success_fut.exception():
try:
raise success_fut.exception()
except RequestTimedOut:
await iface.close()
- await iface_disconnected
+ await iface.got_disconnected
continue # try again
return success_fut.result()
# otherwise; try again