ServerVoteSelector.master_server_downvote()   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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