node.py 5.25 KiB
# This Python file uses the following encoding: utf-8
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from builtins import str
import json
import re
import time
import logging
from .exceptions import (
UnauthorizedError, RPCConnection, RPCError, NumRetriesReached, CallRetriesReached
)
log = logging.getLogger(__name__)
class Node(object):
def __init__(
self,
url
):
self.url = url
self.error_cnt = 0
self.error_cnt_call = 0
def __repr__(self):
return self.url
class Nodes(list):
"""Stores Node URLs and error counts"""
def __init__(self, urls, num_retries, num_retries_call):
if isinstance(urls, str):
url_list = re.split(r",|;", urls)
if url_list is None:
url_list = [urls]
elif isinstance(urls, Nodes):
url_list = [urls[i].url for i in range(len(urls))]
elif isinstance(urls, (list, tuple, set)):
url_list = urls
elif urls is not None:
url_list = [urls]
else:
url_list = []
super(Nodes, self).__init__([Node(x) for x in url_list])
self.num_retries = num_retries
self.num_retries_call = num_retries_call
self.current_node_index = -1
self.freeze_current_node = False
def __iter__(self):
return self
def __next__(self):
next_node_count = 0
if self.freeze_current_node:
return self.url
while next_node_count == 0 and (self.num_retries < 0 or self.node.error_cnt < self.num_retries):
self.current_node_index += 1
if self.current_node_index >= self.working_nodes_count:
self.current_node_index = 0
next_node_count += 1
if next_node_count > self.working_nodes_count + 1:
raise StopIteration
return self.url
next = __next__ # Python 2
def export_working_nodes(self):
nodes_list = []
for i in range(len(self)):
if self.num_retries < 0 or self[i].error_cnt <= self.num_retries:
nodes_list.append(self[i].url)
return nodes_list
def __repr__(self):
nodes_list = self.export_working_nodes()
return str(nodes_list)
@property
def working_nodes_count(self):
n = 0
if self.freeze_current_node:
i = self.current_node_index
if self.current_node_index < 0:
i = 0
if self.num_retries < 0 or self[i].error_cnt <= self.num_retries:
n += 1
return n
for i in range(len(self)):
if self.num_retries < 0 or self[i].error_cnt <= self.num_retries:
n += 1
return n
@property
def url(self):
if self.node is None:
return ''
return self.node.url
@property
def node(self):
if self.current_node_index < 0:
return self[0]
return self[self.current_node_index]
@property
def error_cnt(self):
if self.node is None:
return 0
return self.node.error_cnt
@property
def error_cnt_call(self):
if self.node is None:
return 0
return self.node.error_cnt_call
@property
def num_retries_call_reached(self):
return self.error_cnt_call >= self.num_retries_call
def increase_error_cnt(self):
"""Increase node error count for current node"""
if self.node is not None:
self.node.error_cnt += 1
def increase_error_cnt_call(self):
"""Increase call error count for current node"""
if self.node is not None:
self.node.error_cnt_call += 1
def reset_error_cnt_call(self):
"""Set call error count for current node to zero"""
if self.node is not None:
self.node.error_cnt_call = 0
def reset_error_cnt(self):
"""Set node error count for current node to zero"""
if self.node is not None:
self.node.error_cnt = 0
def sleep_and_check_retries(self, errorMsg=None, sleep=True, call_retry=False, showMsg=True):
"""Sleep and check if num_retries is reached"""
if errorMsg:
log.warning("Error: {}".format(errorMsg))
if call_retry:
cnt = self.error_cnt_call
if (self.num_retries_call >= 0 and self.error_cnt_call > self.num_retries_call):
raise CallRetriesReached()
else:
cnt = self.error_cnt
if (self.num_retries >= 0 and self.error_cnt > self.num_retries):
raise NumRetriesReached()
if showMsg:
if call_retry:
log.warning("Retry RPC Call on node: %s (%d/%d) \n" % (self.url, cnt, self.num_retries_call))
else:
log.warning("Lost connection or internal error on node: %s (%d/%d) \n" % (self.url, cnt, self.num_retries))
if not sleep:
return
if cnt < 1:
sleeptime = 0
elif cnt < 10:
sleeptime = (cnt - 1) * 1.5 + 0.5
else:
sleeptime = 10
if sleeptime:
log.warning("Retrying in %d seconds\n" % sleeptime)
time.sleep(sleeptime)