Repository navigation
Expand file tree
/
Copy pathworkload_manager.py
More file actions
156 lines (127 loc) · 5.93 KB
/
Copy pathworkload_manager.py
File metadata and controls
156 lines (127 loc) · 5.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import random
class WorkloadManager:
def __init__(self, workload: list[str], templates: list[int], execution_mode: str, fraction: int | None):
'''
Container class for the workload to be sent to the Router.
Note this is different from the (static) workload sent to the preprocessor,
because we still want to generate a full complement of index candidates,
but in a low-training-data environment or a workload shift environment,
we need to modify which queries are actually sent to each DBMS instance
for use in computing the reinforcement learning agent's reward function.
:param workload: a list of every query in the full generated workload
:param templates: which template # each query in the workload belongs to
:param execution_mode: how should the workload change?
:param fraction: what proportion of the full templates will be used in training
'''
self._workload = workload
self._templates = templates
self._analytical_workload = []
self._analytical_templates = []
self._update_workload = []
self._update_templates = []
self._unique_update_temps = set()
self._partial_workload = workload
self._partial_templates = templates
self._full_workload = workload
self._full_templates = templates
self._selection_weights = [2 for _ in templates]
self._num_full_queries = len(workload)
self._num_full_templates = len(list(set(templates)))
self._exe_mode = execution_mode
self._fraction = fraction
def select_queries(self):
'''
In the low data and workload drift scenarioes, we need to
select a fraction of the templates to be used in the training set.
'''
num_templates = round(self._num_full_queries * self._fraction)
selected_queries = random.choices(self._full_workload, weights=self._selection_weights, k=num_templates)
self._partial_workload = []
self._partial_templates = []
for i, query in enumerate(self._full_workload):
if query in selected_queries:
self._partial_workload.append(query)
self._partial_templates.append(self._full_templates[i])
self._workload = self._partial_workload
self._templates = self._partial_templates
def update_workload(self):
'''
If we are in a workload drift experiment, then we need to vary which templates
are present in the overall workload. If we are not in a workload drift experiment,
then this function is a no-op.
'''
if self._exe_mode != 'drift':
return
self.select_queries()
# update the selection weights for the next episode (cause the workload to drift)
queries_per_template = len(self._full_workload) // self._num_full_templates
template_to_increase = random.randint(0, self._num_full_templates - 1)
for i in range(queries_per_template * template_to_increase, queries_per_template * (template_to_increase + 1)):
self._selection_weights[i] += 1
def workload(self) -> list[str]:
'''
Returns the training set workload to be used by the router.
:returns: the queries in the training set
'''
return self._workload
def templates(self) -> list[int]:
'''
Returns which template each query in the training set is generated from.
:returns: the template assignment to each query
'''
return self._templates
def queries(self) -> tuple[list[str], list[int]]:
return self._analytical_workload, self._analytical_templates
def updates(self) -> tuple[list[str], list[int]]:
return self._update_workload, self._update_templates
def num_queries(self) -> int:
'''
Returns the number of queries in the training set.
:returns: The size of the training set
'''
return len(self._workload)
def num_templates(self) -> int:
'''
Returns the number of unique query templates used in the
training set.
:returns: the number of templates
'''
return len(list(set(self._templates)))
def num_full_templates(self) -> int:
'''
Returns the number of unique query templates used in the full workload.
:returns: the number of templates
'''
return self._num_full_templates
def set_to_partial(self):
'''
If we have set the currently active workload/template set to
the full workload (ie to generate a routing table), we can
reset it back to the partial one without reselecting templates here.
'''
self._workload = self._partial_workload
self._templates = self._partial_templates
def set_to_full(self):
'''
Changes the active workload to the full set, rather than the partial.
'''
self._workload = self._full_workload
self._templates = self._full_templates
def partial_templates(self):
'''
Get which templates were used in the training set.
:returns templates: which template numbers are used
'''
return sorted(list(set(self._partial_templates)))
def sort(self):
for i, statement in enumerate(self._workload):
if 'select' in statement.lower():
self._analytical_workload.append(statement)
self._analytical_templates.append(self._templates[i])
else:
self._update_workload.append(statement)
self._update_templates.append(self._templates[i])
self._unique_update_temps.add(self._templates[i])
print(len(self._analytical_workload), 'analytical queries;', len(self._update_workload), 'update queries')
def update_templates(self):
return self._unique_update_temps