Passed
Push — master ( 2adcb6...75d235 )
by Markus
01:50
created

ServerVoteSelector.__init__()   A

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
nop 2
1
# -*- coding: utf-8 -*-
2
3
# pylint: disable=C0111,C0326,C0103
4
5
"""Tools to sort API servers to find the least awful one."""
6
7
import numpy
8
import time
0 ignored issues
show
introduced by
standard import "import time" should be placed before "import numpy"
Loading history...
9
10
11
MASTER_SERVERS = [
12
    "g2master-us-east.tclclouds.com",
13
    "g2master-us-west.tclclouds.com",
14
    "g2master-eu-west.tclclouds.com",
15
    "g2master-ap-south.tclclouds.com",
16
    "g2master-ap-north.tclclouds.com",
17
    "g2master-sa-east.tclclouds.com",
18
]
19
20
class ServerSelector:
0 ignored issues
show
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
21
    """Returns a random server to use."""
22
23
    def __init__(self, server_list=None):
24
        """Init stuff"""
25
        if server_list:
26
            self.server_list = server_list
27
        else:
28
            self.server_list = MASTER_SERVERS
29
        self.last_server = None
30
31
    def get_master_server(self):
32
        """Return a random server."""
33
        while True:
34
            new_server = numpy.random.choice(self.server_list)
35
            if new_server != self.last_server:
36
                break
37
        self.last_server = new_server
38
        return new_server
39
40
    def hook_prerequest(self):
41
        """Hook to be called before doing request"""
42
        pass
43
44
    def hook_postrequest(self, successful: bool):
45
        """Hook to be called after request finished"""
46
        pass
47
48
class ServerVoteSelector(ServerSelector):
0 ignored issues
show
Unused Code introduced by
The variable __class__ seems to be unused.
Loading history...
49
    """Tries to return faster servers more often."""
50
51
    def __init__(self, server_list=None):
52
        """Populate server list and weighting variables."""
53
        super().__init__(server_list)
54
        self.servers_weights = [3] * len(self.server_list)
55
        self.check_time_sum = 3
56
        self.check_time_count = 1
57
58
    def get_master_server(self):
59
        """Return weighted choice from server list."""
60
        weight_sum = 0
61
        for i in self.servers_weights:
62
            weight_sum += i
63
        numpy_weights = []
64
        for i in self.servers_weights:
65
            numpy_weights.append(i/weight_sum)
66
        self.last_server = numpy.random.choice(self.server_list, p=numpy_weights)
67
        return self.last_server
68
69
    def master_server_downvote(self):
70
        """Decrease weight of last chosen server."""
71
        idx = self.server_list.index(self.last_server)
72
        if self.servers_weights[idx] > 1:
73
            self.servers_weights[idx] -= 1
74
75
    def master_server_upvote(self):
76
        """Increase weight of last chosen server."""
77
        idx = self.server_list.index(self.last_server)
78
        if self.servers_weights[idx] < 10:
79
            self.servers_weights[idx] += 1
80
81
    def check_time_add(self, duration):
82
        """Record connection time."""
83
        self.check_time_sum += duration
84
        self.check_time_count += 1
85
86
    def check_time_avg(self):
87
        """Return average connection time."""
88
        return self.check_time_sum / self.check_time_count
89
90
    def master_server_vote_on_time(self, last_duration):
91
        """Change weight of a server based on average connection time."""
92
        avg_duration = self.check_time_avg()
93
        if last_duration < avg_duration - 0.5:
94
            self.master_server_upvote()
95
        elif last_duration > avg_duration + 0.5:
96
            self.master_server_downvote()
97
98
    def hook_prerequest(self):
99
        """Hook to be called before doing request"""
100
        self.reqtime_start = time.perf_counter()
0 ignored issues
show
Coding Style introduced by
The attribute reqtime_start was defined outside __init__.

It is generally a good practice to initialize all attributes to default values in the __init__ method:

class Foo:
    def __init__(self, x=None):
        self.x = x
Loading history...
101
102
    def hook_postrequest(self, successful: bool):
103
        """Hook to be called after request finished"""
104
        reqtime = time.perf_counter() - self.reqtime_start
105
        self.check_time_add(reqtime)
106
        if successful:
107
            self.master_server_vote_on_time(reqtime)
108
        else:
109
            self.master_server_downvote()
110