redis-benchmarks-specification 0.1.72__py3-none-any.whl → 0.1.74__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of redis-benchmarks-specification might be problematic. Click here for more details.
- redis_benchmarks_specification/__builder__/builder.py +2 -0
- redis_benchmarks_specification/__cli__/args.py +6 -0
- redis_benchmarks_specification/__cli__/cli.py +5 -1
- redis_benchmarks_specification/__compare__/__init__.py +5 -0
- redis_benchmarks_specification/__compare__/args.py +135 -0
- redis_benchmarks_specification/__compare__/compare.py +1153 -0
- redis_benchmarks_specification/__runner__/runner.py +120 -59
- redis_benchmarks_specification/__self_contained_coordinator__/self_contained_coordinator.py +21 -10
- redis_benchmarks_specification/test-suites/create-re-string.py +286 -0
- redis_benchmarks_specification/test-suites/generate.py +108 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-hash-hincrby.yml +1 -1
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-load-string-with-200KiB-values.yml +37 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-load-string-with-20KiB-values.yml +37 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-load-string-with-2MB-values.yml +37 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-string-get-200KiB.yml +33 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-string-get-20KiB.yml +33 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1Mkeys-string-get-2MB.yml +33 -0
- redis_benchmarks_specification/test-suites/memtier_benchmark-1key-zset-100-elements-zrangebyscore-all-elements-long-scores.yml +2 -2
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-load-string200c-with-20KiB-values-pipeline-10.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-load-string200c-with-20KiB-values.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-load-string50c-with-20KiB-values-pipeline-10.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-load-string50c-with-20KiB-values.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-string-setget200c-20KiB-pipeline-10.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-string-setget200c-20KiB.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-string-setget50c-20KiB-pipeline-10.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-100Kkeys-string-setget50c-20KiB.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string200c-with-100B-values-pipeline-10.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string200c-with-100B-values.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string200c-with-1KiB-values-pipeline-10.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string200c-with-1KiB-values.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string50c-with-100B-values-pipeline-10.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string50c-with-100B-values.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string50c-with-1KiB-values-pipeline-10.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-load-string50c-with-1KiB-values.yml +20 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-mget-1KiB.yml +27 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget200c-100B-pipeline-10.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget200c-100B.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget200c-1KiB-pipeline-10.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget200c-1KiB.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget50c-100B-pipeline-10.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget50c-100B.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget50c-1KiB-pipeline-10.yml +26 -0
- redis_benchmarks_specification/test-suites/string/memtier_benchmark-1Mkeys-string-setget50c-1KiB.yml +26 -0
- redis_benchmarks_specification/test-suites/template.txt +16 -0
- {redis_benchmarks_specification-0.1.72.dist-info → redis_benchmarks_specification-0.1.74.dist-info}/METADATA +2 -1
- {redis_benchmarks_specification-0.1.72.dist-info → redis_benchmarks_specification-0.1.74.dist-info}/RECORD +49 -12
- {redis_benchmarks_specification-0.1.72.dist-info → redis_benchmarks_specification-0.1.74.dist-info}/WHEEL +1 -1
- {redis_benchmarks_specification-0.1.72.dist-info → redis_benchmarks_specification-0.1.74.dist-info}/entry_points.txt +1 -0
- {redis_benchmarks_specification-0.1.72.dist-info → redis_benchmarks_specification-0.1.74.dist-info}/LICENSE +0 -0
|
@@ -0,0 +1,1153 @@
|
|
|
1
|
+
# BSD 3-Clause License
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2021., Redis Labs Modules
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
import datetime
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
import pandas as pd
|
|
10
|
+
import redis
|
|
11
|
+
import yaml
|
|
12
|
+
from pytablewriter import MarkdownTableWriter
|
|
13
|
+
import humanize
|
|
14
|
+
import datetime as dt
|
|
15
|
+
import os
|
|
16
|
+
from tqdm import tqdm
|
|
17
|
+
from github import Github
|
|
18
|
+
from slack_sdk.webhook import WebhookClient
|
|
19
|
+
import argparse
|
|
20
|
+
from redis_benchmarks_specification.__compare__.args import create_compare_arguments
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
from redis_benchmarks_specification.__common__.package import (
|
|
24
|
+
get_version_string,
|
|
25
|
+
populate_with_poetry_data,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
WH_TOKEN = os.getenv("PERFORMANCE_WH_TOKEN", None)
|
|
30
|
+
|
|
31
|
+
LOG_LEVEL = logging.DEBUG
|
|
32
|
+
if os.getenv("VERBOSE", "0") == "0":
|
|
33
|
+
LOG_LEVEL = logging.INFO
|
|
34
|
+
LOG_FORMAT = "%(asctime)s %(levelname)-4s %(message)s"
|
|
35
|
+
LOG_DATEFMT = "%Y-%m-%d %H:%M:%S"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_overall_dashboard_keynames(
|
|
39
|
+
tf_github_org,
|
|
40
|
+
tf_github_repo,
|
|
41
|
+
tf_triggering_env,
|
|
42
|
+
build_variant_name=None,
|
|
43
|
+
running_platform=None,
|
|
44
|
+
test_name=None,
|
|
45
|
+
):
|
|
46
|
+
build_variant_str = ""
|
|
47
|
+
if build_variant_name is not None:
|
|
48
|
+
build_variant_str = "/{}".format(build_variant_name)
|
|
49
|
+
running_platform_str = ""
|
|
50
|
+
if running_platform is not None:
|
|
51
|
+
running_platform_str = "/{}".format(running_platform)
|
|
52
|
+
sprefix = (
|
|
53
|
+
"ci.benchmarks.redislabs/"
|
|
54
|
+
+ "{triggering_env}/{github_org}/{github_repo}".format(
|
|
55
|
+
triggering_env=tf_triggering_env,
|
|
56
|
+
github_org=tf_github_org,
|
|
57
|
+
github_repo=tf_github_repo,
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
testcases_setname = "{}:testcases".format(sprefix)
|
|
61
|
+
deployment_name_setname = "{}:deployment_names".format(sprefix)
|
|
62
|
+
project_archs_setname = "{}:archs".format(sprefix)
|
|
63
|
+
project_oss_setname = "{}:oss".format(sprefix)
|
|
64
|
+
project_branches_setname = "{}:branches".format(sprefix)
|
|
65
|
+
project_versions_setname = "{}:versions".format(sprefix)
|
|
66
|
+
project_compilers_setname = "{}:compilers".format(sprefix)
|
|
67
|
+
running_platforms_setname = "{}:platforms".format(sprefix)
|
|
68
|
+
build_variant_setname = "{}:build_variants".format(sprefix)
|
|
69
|
+
build_variant_prefix = "{sprefix}{build_variant_str}".format(
|
|
70
|
+
sprefix=sprefix,
|
|
71
|
+
build_variant_str=build_variant_str,
|
|
72
|
+
)
|
|
73
|
+
prefix = "{build_variant_prefix}{running_platform_str}".format(
|
|
74
|
+
build_variant_prefix=build_variant_prefix,
|
|
75
|
+
running_platform_str=running_platform_str,
|
|
76
|
+
)
|
|
77
|
+
tsname_project_total_success = "{}:total_success".format(
|
|
78
|
+
prefix,
|
|
79
|
+
)
|
|
80
|
+
tsname_project_total_failures = "{}:total_failures".format(
|
|
81
|
+
prefix,
|
|
82
|
+
)
|
|
83
|
+
testcases_metric_context_path_setname = ""
|
|
84
|
+
if test_name is not None:
|
|
85
|
+
testcases_metric_context_path_setname = (
|
|
86
|
+
"{testcases_setname}:metric_context_path:{test_name}".format(
|
|
87
|
+
testcases_setname=testcases_setname, test_name=test_name
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
testcases_and_metric_context_path_setname = (
|
|
91
|
+
"{testcases_setname}_AND_metric_context_path".format(
|
|
92
|
+
testcases_setname=testcases_setname
|
|
93
|
+
)
|
|
94
|
+
)
|
|
95
|
+
return (
|
|
96
|
+
prefix,
|
|
97
|
+
testcases_setname,
|
|
98
|
+
deployment_name_setname,
|
|
99
|
+
tsname_project_total_failures,
|
|
100
|
+
tsname_project_total_success,
|
|
101
|
+
running_platforms_setname,
|
|
102
|
+
build_variant_setname,
|
|
103
|
+
testcases_metric_context_path_setname,
|
|
104
|
+
testcases_and_metric_context_path_setname,
|
|
105
|
+
project_archs_setname,
|
|
106
|
+
project_oss_setname,
|
|
107
|
+
project_branches_setname,
|
|
108
|
+
project_versions_setname,
|
|
109
|
+
project_compilers_setname,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_start_time_vars(start_time=None):
|
|
114
|
+
if start_time is None:
|
|
115
|
+
start_time = dt.datetime.utcnow()
|
|
116
|
+
start_time_ms = int((start_time - dt.datetime(1970, 1, 1)).total_seconds() * 1000)
|
|
117
|
+
start_time_str = start_time.strftime("%Y-%m-%d-%H-%M-%S")
|
|
118
|
+
return start_time, start_time_ms, start_time_str
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_project_compare_zsets(triggering_env, org, repo):
|
|
122
|
+
return "ci.benchmarks.redislabs/{}/{}/{}:compare:pull_requests:zset".format(
|
|
123
|
+
triggering_env, org, repo
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def compare_command_logic(args, project_name, project_version):
|
|
128
|
+
|
|
129
|
+
logger = logging.getLogger()
|
|
130
|
+
logger.setLevel(LOG_LEVEL)
|
|
131
|
+
|
|
132
|
+
# create console handler and set level to debug
|
|
133
|
+
ch = logging.StreamHandler()
|
|
134
|
+
ch.setLevel(LOG_LEVEL)
|
|
135
|
+
|
|
136
|
+
# create formatter
|
|
137
|
+
formatter = logging.Formatter(LOG_FORMAT)
|
|
138
|
+
|
|
139
|
+
# add formatter to ch
|
|
140
|
+
ch.setFormatter(formatter)
|
|
141
|
+
|
|
142
|
+
# add ch to logger
|
|
143
|
+
logger.addHandler(ch)
|
|
144
|
+
|
|
145
|
+
logging.info(
|
|
146
|
+
"Using: {project_name} {project_version}".format(
|
|
147
|
+
project_name=project_name, project_version=project_version
|
|
148
|
+
)
|
|
149
|
+
)
|
|
150
|
+
logging.info(
|
|
151
|
+
"Checking connection to RedisTimeSeries with user: {}, host: {}, port: {}".format(
|
|
152
|
+
args.redistimeseries_user,
|
|
153
|
+
args.redistimeseries_host,
|
|
154
|
+
args.redistimeseries_port,
|
|
155
|
+
)
|
|
156
|
+
)
|
|
157
|
+
rts = redis.Redis(
|
|
158
|
+
host=args.redistimeseries_host,
|
|
159
|
+
port=args.redistimeseries_port,
|
|
160
|
+
password=args.redistimeseries_pass,
|
|
161
|
+
username=args.redistimeseries_user,
|
|
162
|
+
)
|
|
163
|
+
rts.ping()
|
|
164
|
+
default_baseline_branch = None
|
|
165
|
+
default_metrics_str = ""
|
|
166
|
+
if args.defaults_filename != "" and os.path.exists(args.defaults_filename):
|
|
167
|
+
logging.info(
|
|
168
|
+
"Loading configuration from defaults file: {}".format(
|
|
169
|
+
args.defaults_filename
|
|
170
|
+
)
|
|
171
|
+
)
|
|
172
|
+
with open(args.defaults_filename) as yaml_fd:
|
|
173
|
+
defaults_dict = yaml.safe_load(yaml_fd)
|
|
174
|
+
if "exporter" in defaults_dict:
|
|
175
|
+
exporter_dict = defaults_dict["exporter"]
|
|
176
|
+
if "comparison" in exporter_dict:
|
|
177
|
+
comparison_dict = exporter_dict["comparison"]
|
|
178
|
+
if "metrics" in comparison_dict:
|
|
179
|
+
metrics = comparison_dict["metrics"]
|
|
180
|
+
logging.info("Detected defaults metrics info. reading metrics")
|
|
181
|
+
default_metrics = []
|
|
182
|
+
|
|
183
|
+
for metric in metrics:
|
|
184
|
+
if metric.startswith("$."):
|
|
185
|
+
metric = metric[2:]
|
|
186
|
+
logging.info("Will use metric: {}".format(metric))
|
|
187
|
+
default_metrics.append(metric)
|
|
188
|
+
if len(default_metrics) == 1:
|
|
189
|
+
default_metrics_str = default_metrics[0]
|
|
190
|
+
if len(default_metrics) > 1:
|
|
191
|
+
default_metrics_str = "({})".format(
|
|
192
|
+
",".join(default_metrics)
|
|
193
|
+
)
|
|
194
|
+
logging.info("Default metrics: {}".format(default_metrics_str))
|
|
195
|
+
|
|
196
|
+
if "baseline-branch" in comparison_dict:
|
|
197
|
+
default_baseline_branch = comparison_dict["baseline-branch"]
|
|
198
|
+
logging.info(
|
|
199
|
+
"Detected baseline branch in defaults file. {}".format(
|
|
200
|
+
default_baseline_branch
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
tf_github_org = args.github_org
|
|
205
|
+
tf_github_repo = args.github_repo
|
|
206
|
+
tf_triggering_env = args.triggering_env
|
|
207
|
+
if args.baseline_deployment_name != "":
|
|
208
|
+
baseline_deployment_name = args.baseline_deployment_name
|
|
209
|
+
else:
|
|
210
|
+
baseline_deployment_name = args.deployment_name
|
|
211
|
+
if args.comparison_deployment_name != "":
|
|
212
|
+
comparison_deployment_name = args.comparison_deployment_name
|
|
213
|
+
else:
|
|
214
|
+
comparison_deployment_name = args.deployment_name
|
|
215
|
+
|
|
216
|
+
logging.info(
|
|
217
|
+
"Using baseline deployment_name={} and comparison deployment_name={} for the analysis".format(
|
|
218
|
+
baseline_deployment_name,
|
|
219
|
+
comparison_deployment_name,
|
|
220
|
+
)
|
|
221
|
+
)
|
|
222
|
+
from_ts_ms = args.from_timestamp
|
|
223
|
+
to_ts_ms = args.to_timestamp
|
|
224
|
+
from_date = args.from_date
|
|
225
|
+
to_date = args.to_date
|
|
226
|
+
baseline_branch = args.baseline_branch
|
|
227
|
+
if baseline_branch is None and default_baseline_branch is not None:
|
|
228
|
+
logging.info(
|
|
229
|
+
"Given --baseline-branch was null using the default baseline branch {}".format(
|
|
230
|
+
default_baseline_branch
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
baseline_branch = default_baseline_branch
|
|
234
|
+
comparison_branch = args.comparison_branch
|
|
235
|
+
simplify_table = args.simple_table
|
|
236
|
+
print_regressions_only = args.print_regressions_only
|
|
237
|
+
print_improvements_only = args.print_improvements_only
|
|
238
|
+
skip_unstable = args.skip_unstable
|
|
239
|
+
baseline_tag = args.baseline_tag
|
|
240
|
+
comparison_tag = args.comparison_tag
|
|
241
|
+
last_n_baseline = args.last_n
|
|
242
|
+
last_n_comparison = args.last_n
|
|
243
|
+
if last_n_baseline < 0:
|
|
244
|
+
last_n_baseline = args.last_n_baseline
|
|
245
|
+
if last_n_comparison < 0:
|
|
246
|
+
last_n_comparison = args.last_n_comparison
|
|
247
|
+
logging.info("Using last {} samples for baseline analysis".format(last_n_baseline))
|
|
248
|
+
logging.info(
|
|
249
|
+
"Using last {} samples for comparison analysis".format(last_n_comparison)
|
|
250
|
+
)
|
|
251
|
+
verbose = args.verbose
|
|
252
|
+
regressions_percent_lower_limit = args.regressions_percent_lower_limit
|
|
253
|
+
metric_name = args.metric_name
|
|
254
|
+
if (metric_name is None or metric_name == "") and default_metrics_str != "":
|
|
255
|
+
logging.info(
|
|
256
|
+
"Given --metric_name was null using the default metric names {}".format(
|
|
257
|
+
default_metrics_str
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
metric_name = default_metrics_str
|
|
261
|
+
|
|
262
|
+
if metric_name is None:
|
|
263
|
+
logging.error(
|
|
264
|
+
"You need to provider either "
|
|
265
|
+
+ " --metric_name or provide a defaults file via --defaults_filename that contains exporter.redistimeseries.comparison.metrics array. Exiting..."
|
|
266
|
+
)
|
|
267
|
+
exit(1)
|
|
268
|
+
else:
|
|
269
|
+
logging.info("Using metric {}".format(metric_name))
|
|
270
|
+
|
|
271
|
+
metric_mode = args.metric_mode
|
|
272
|
+
test = args.test
|
|
273
|
+
use_metric_context_path = args.use_metric_context_path
|
|
274
|
+
github_token = args.github_token
|
|
275
|
+
pull_request = args.pull_request
|
|
276
|
+
testname_regex = args.testname_regex
|
|
277
|
+
auto_approve = args.auto_approve
|
|
278
|
+
running_platform = args.running_platform
|
|
279
|
+
grafana_base_dashboard = args.grafana_base_dashboard
|
|
280
|
+
# using an access token
|
|
281
|
+
is_actionable_pr = False
|
|
282
|
+
contains_regression_comment = False
|
|
283
|
+
regression_comment = None
|
|
284
|
+
github_pr = None
|
|
285
|
+
# slack related
|
|
286
|
+
webhook_notifications_active = False
|
|
287
|
+
webhook_client_slack = None
|
|
288
|
+
if running_platform is not None:
|
|
289
|
+
logging.info(
|
|
290
|
+
"Using platform named: {} to do the comparison.\n\n".format(
|
|
291
|
+
running_platform
|
|
292
|
+
)
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
old_regression_comment_body = ""
|
|
296
|
+
if github_token is not None:
|
|
297
|
+
logging.info("Detected github token")
|
|
298
|
+
g = Github(github_token)
|
|
299
|
+
if pull_request is not None and pull_request != "":
|
|
300
|
+
pull_request_n = int(pull_request)
|
|
301
|
+
github_pr = (
|
|
302
|
+
g.get_user(tf_github_org)
|
|
303
|
+
.get_repo(tf_github_repo)
|
|
304
|
+
.get_issue(pull_request_n)
|
|
305
|
+
)
|
|
306
|
+
comments = github_pr.get_comments()
|
|
307
|
+
pr_link = github_pr.html_url
|
|
308
|
+
logging.info("Working on github PR already: {}".format(pr_link))
|
|
309
|
+
is_actionable_pr = True
|
|
310
|
+
contains_regression_comment, pos = check_regression_comment(comments)
|
|
311
|
+
if contains_regression_comment:
|
|
312
|
+
regression_comment = comments[pos]
|
|
313
|
+
old_regression_comment_body = regression_comment.body
|
|
314
|
+
logging.info(
|
|
315
|
+
"Already contains regression comment. Link: {}".format(
|
|
316
|
+
regression_comment.html_url
|
|
317
|
+
)
|
|
318
|
+
)
|
|
319
|
+
if verbose:
|
|
320
|
+
logging.info("Printing old regression comment:")
|
|
321
|
+
print("".join(["-" for x in range(1, 80)]))
|
|
322
|
+
print(regression_comment.body)
|
|
323
|
+
print("".join(["-" for x in range(1, 80)]))
|
|
324
|
+
else:
|
|
325
|
+
logging.info("Does not contain regression comment")
|
|
326
|
+
|
|
327
|
+
grafana_dashboards_uids = {
|
|
328
|
+
"redisgraph": "SH9_rQYGz",
|
|
329
|
+
"redisbloom": "q4-5sRR7k",
|
|
330
|
+
"redisearch": "3Ejv2wZnk",
|
|
331
|
+
"redisjson": "UErSC0jGk",
|
|
332
|
+
"redistimeseries": "2WMw61UGz",
|
|
333
|
+
}
|
|
334
|
+
uid = None
|
|
335
|
+
if tf_github_repo.lower() in grafana_dashboards_uids:
|
|
336
|
+
uid = grafana_dashboards_uids[tf_github_repo.lower()]
|
|
337
|
+
grafana_link_base = None
|
|
338
|
+
if uid is not None:
|
|
339
|
+
grafana_link_base = "{}/{}".format(grafana_base_dashboard, uid)
|
|
340
|
+
logging.info(
|
|
341
|
+
"There is a grafana dashboard for this repo. Base link: {}".format(
|
|
342
|
+
grafana_link_base
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
(
|
|
347
|
+
detected_regressions,
|
|
348
|
+
table_output,
|
|
349
|
+
total_improvements,
|
|
350
|
+
total_regressions,
|
|
351
|
+
total_stable,
|
|
352
|
+
total_unstable,
|
|
353
|
+
total_comparison_points,
|
|
354
|
+
) = compute_regression_table(
|
|
355
|
+
rts,
|
|
356
|
+
tf_github_org,
|
|
357
|
+
tf_github_repo,
|
|
358
|
+
tf_triggering_env,
|
|
359
|
+
metric_name,
|
|
360
|
+
comparison_branch,
|
|
361
|
+
baseline_branch,
|
|
362
|
+
baseline_tag,
|
|
363
|
+
comparison_tag,
|
|
364
|
+
baseline_deployment_name,
|
|
365
|
+
comparison_deployment_name,
|
|
366
|
+
print_improvements_only,
|
|
367
|
+
print_regressions_only,
|
|
368
|
+
skip_unstable,
|
|
369
|
+
regressions_percent_lower_limit,
|
|
370
|
+
simplify_table,
|
|
371
|
+
test,
|
|
372
|
+
testname_regex,
|
|
373
|
+
verbose,
|
|
374
|
+
last_n_baseline,
|
|
375
|
+
last_n_comparison,
|
|
376
|
+
metric_mode,
|
|
377
|
+
from_date,
|
|
378
|
+
from_ts_ms,
|
|
379
|
+
to_date,
|
|
380
|
+
to_ts_ms,
|
|
381
|
+
use_metric_context_path,
|
|
382
|
+
running_platform,
|
|
383
|
+
)
|
|
384
|
+
comment_body = ""
|
|
385
|
+
if total_comparison_points > 0:
|
|
386
|
+
comment_body = "### Automated performance analysis summary\n\n"
|
|
387
|
+
comment_body += "This comment was automatically generated given there is performance data available.\n\n"
|
|
388
|
+
if running_platform is not None:
|
|
389
|
+
comment_body += "Using platform named: {} to do the comparison.\n\n".format(
|
|
390
|
+
running_platform
|
|
391
|
+
)
|
|
392
|
+
comparison_summary = "In summary:\n"
|
|
393
|
+
if total_stable > 0:
|
|
394
|
+
comparison_summary += (
|
|
395
|
+
"- Detected a total of {} stable tests between versions.\n".format(
|
|
396
|
+
total_stable,
|
|
397
|
+
)
|
|
398
|
+
)
|
|
399
|
+
|
|
400
|
+
if total_unstable > 0:
|
|
401
|
+
comparison_summary += (
|
|
402
|
+
"- Detected a total of {} highly unstable benchmarks.\n".format(
|
|
403
|
+
total_unstable
|
|
404
|
+
)
|
|
405
|
+
)
|
|
406
|
+
if total_improvements > 0:
|
|
407
|
+
comparison_summary += "- Detected a total of {} improvements above the improvement water line.\n".format(
|
|
408
|
+
total_improvements
|
|
409
|
+
)
|
|
410
|
+
if total_regressions > 0:
|
|
411
|
+
comparison_summary += "- Detected a total of {} regressions bellow the regression water line {}.\n".format(
|
|
412
|
+
total_regressions, args.regressions_percent_lower_limit
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
comment_body += comparison_summary
|
|
416
|
+
comment_body += "\n"
|
|
417
|
+
|
|
418
|
+
if grafana_link_base is not None:
|
|
419
|
+
grafana_link = "{}/".format(grafana_link_base)
|
|
420
|
+
if baseline_tag is not None and comparison_tag is not None:
|
|
421
|
+
grafana_link += "?var-version={}&var-version={}".format(
|
|
422
|
+
baseline_tag, comparison_tag
|
|
423
|
+
)
|
|
424
|
+
if baseline_branch is not None and comparison_branch is not None:
|
|
425
|
+
grafana_link += "?var-branch={}&var-branch={}".format(
|
|
426
|
+
baseline_branch, comparison_branch
|
|
427
|
+
)
|
|
428
|
+
comment_body += "You can check a comparison in detail via the [grafana link]({})".format(
|
|
429
|
+
grafana_link
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
comment_body += "\n\n##" + table_output
|
|
433
|
+
print(comment_body)
|
|
434
|
+
|
|
435
|
+
if is_actionable_pr:
|
|
436
|
+
zset_project_pull_request = get_project_compare_zsets(
|
|
437
|
+
tf_triggering_env,
|
|
438
|
+
tf_github_org,
|
|
439
|
+
tf_github_repo,
|
|
440
|
+
)
|
|
441
|
+
logging.info(
|
|
442
|
+
"Populating the pull request performance ZSETs: {} with branch {}".format(
|
|
443
|
+
zset_project_pull_request, comparison_branch
|
|
444
|
+
)
|
|
445
|
+
)
|
|
446
|
+
_, start_time_ms, _ = get_start_time_vars()
|
|
447
|
+
res = rts.zadd(
|
|
448
|
+
zset_project_pull_request,
|
|
449
|
+
{comparison_branch: start_time_ms},
|
|
450
|
+
)
|
|
451
|
+
logging.info(
|
|
452
|
+
"Result of Populating the pull request performance ZSETs: {} with branch {}: {}".format(
|
|
453
|
+
zset_project_pull_request, comparison_branch, res
|
|
454
|
+
)
|
|
455
|
+
)
|
|
456
|
+
user_input = "n"
|
|
457
|
+
html_url = "n/a"
|
|
458
|
+
regression_count = len(detected_regressions)
|
|
459
|
+
(
|
|
460
|
+
baseline_str,
|
|
461
|
+
by_str_baseline,
|
|
462
|
+
comparison_str,
|
|
463
|
+
by_str_comparison,
|
|
464
|
+
) = get_by_strings(
|
|
465
|
+
baseline_branch,
|
|
466
|
+
comparison_branch,
|
|
467
|
+
baseline_tag,
|
|
468
|
+
comparison_tag,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
if contains_regression_comment:
|
|
472
|
+
same_comment = False
|
|
473
|
+
if comment_body == old_regression_comment_body:
|
|
474
|
+
logging.info(
|
|
475
|
+
"The old regression comment is the same as the new comment. skipping..."
|
|
476
|
+
)
|
|
477
|
+
same_comment = True
|
|
478
|
+
else:
|
|
479
|
+
logging.info(
|
|
480
|
+
"The old regression comment is different from the new comment. updating it..."
|
|
481
|
+
)
|
|
482
|
+
comment_body_arr = comment_body.split("\n")
|
|
483
|
+
old_regression_comment_body_arr = old_regression_comment_body.split(
|
|
484
|
+
"\n"
|
|
485
|
+
)
|
|
486
|
+
if verbose:
|
|
487
|
+
DF = [
|
|
488
|
+
x
|
|
489
|
+
for x in comment_body_arr
|
|
490
|
+
if x not in old_regression_comment_body_arr
|
|
491
|
+
]
|
|
492
|
+
print("---------------------")
|
|
493
|
+
print(DF)
|
|
494
|
+
print("---------------------")
|
|
495
|
+
if same_comment is False:
|
|
496
|
+
if auto_approve:
|
|
497
|
+
print("auto approving...")
|
|
498
|
+
else:
|
|
499
|
+
user_input = input(
|
|
500
|
+
"Do you wish to update the comment {} (y/n): ".format(
|
|
501
|
+
regression_comment.html_url
|
|
502
|
+
)
|
|
503
|
+
)
|
|
504
|
+
if user_input.lower() == "y" or auto_approve:
|
|
505
|
+
print("Updating comment {}".format(regression_comment.html_url))
|
|
506
|
+
regression_comment.edit(comment_body)
|
|
507
|
+
html_url = regression_comment.html_url
|
|
508
|
+
print(
|
|
509
|
+
"Updated comment. Access it via {}".format(
|
|
510
|
+
regression_comment.html_url
|
|
511
|
+
)
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
else:
|
|
515
|
+
if auto_approve:
|
|
516
|
+
print("auto approving...")
|
|
517
|
+
else:
|
|
518
|
+
user_input = input(
|
|
519
|
+
"Do you wish to add a comment in {} (y/n): ".format(pr_link)
|
|
520
|
+
)
|
|
521
|
+
if user_input.lower() == "y" or auto_approve:
|
|
522
|
+
print("creating an comment in PR {}".format(pr_link))
|
|
523
|
+
regression_comment = github_pr.create_comment(comment_body)
|
|
524
|
+
html_url = regression_comment.html_url
|
|
525
|
+
print("created comment. Access it via {}".format(html_url))
|
|
526
|
+
|
|
527
|
+
else:
|
|
528
|
+
logging.error("There was no comparison points to produce a table...")
|
|
529
|
+
return (
|
|
530
|
+
detected_regressions,
|
|
531
|
+
comment_body,
|
|
532
|
+
total_improvements,
|
|
533
|
+
total_regressions,
|
|
534
|
+
total_stable,
|
|
535
|
+
total_unstable,
|
|
536
|
+
total_comparison_points,
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def check_regression_comment(comments):
|
|
541
|
+
res = False
|
|
542
|
+
pos = -1
|
|
543
|
+
for n, comment in enumerate(comments):
|
|
544
|
+
body = comment.body
|
|
545
|
+
if "Comparison between" in body and "Time Period from" in body:
|
|
546
|
+
res = True
|
|
547
|
+
pos = n
|
|
548
|
+
return res, pos
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def compute_regression_table(
|
|
552
|
+
rts,
|
|
553
|
+
tf_github_org,
|
|
554
|
+
tf_github_repo,
|
|
555
|
+
tf_triggering_env,
|
|
556
|
+
metric_name,
|
|
557
|
+
comparison_branch,
|
|
558
|
+
baseline_branch="master",
|
|
559
|
+
baseline_tag=None,
|
|
560
|
+
comparison_tag=None,
|
|
561
|
+
baseline_deployment_name="oss-standalone",
|
|
562
|
+
comparison_deployment_name="oss-standalone",
|
|
563
|
+
print_improvements_only=False,
|
|
564
|
+
print_regressions_only=False,
|
|
565
|
+
skip_unstable=False,
|
|
566
|
+
regressions_percent_lower_limit=5.0,
|
|
567
|
+
simplify_table=False,
|
|
568
|
+
test="",
|
|
569
|
+
testname_regex=".*",
|
|
570
|
+
verbose=False,
|
|
571
|
+
last_n_baseline=-1,
|
|
572
|
+
last_n_comparison=-1,
|
|
573
|
+
metric_mode="higher-better",
|
|
574
|
+
from_date=None,
|
|
575
|
+
from_ts_ms=None,
|
|
576
|
+
to_date=None,
|
|
577
|
+
to_ts_ms=None,
|
|
578
|
+
use_metric_context_path=None,
|
|
579
|
+
running_platform=None,
|
|
580
|
+
):
|
|
581
|
+
START_TIME_NOW_UTC, _, _ = get_start_time_vars()
|
|
582
|
+
START_TIME_LAST_MONTH_UTC = START_TIME_NOW_UTC - datetime.timedelta(days=31)
|
|
583
|
+
if from_date is None:
|
|
584
|
+
from_date = START_TIME_LAST_MONTH_UTC
|
|
585
|
+
if to_date is None:
|
|
586
|
+
to_date = START_TIME_NOW_UTC
|
|
587
|
+
if from_ts_ms is None:
|
|
588
|
+
from_ts_ms = int(from_date.timestamp() * 1000)
|
|
589
|
+
if to_ts_ms is None:
|
|
590
|
+
to_ts_ms = int(to_date.timestamp() * 1000)
|
|
591
|
+
from_human_str = humanize.naturaltime(
|
|
592
|
+
dt.datetime.utcfromtimestamp(from_ts_ms / 1000)
|
|
593
|
+
)
|
|
594
|
+
to_human_str = humanize.naturaltime(dt.datetime.utcfromtimestamp(to_ts_ms / 1000))
|
|
595
|
+
logging.info(
|
|
596
|
+
"Using a time-delta from {} to {}".format(from_human_str, to_human_str)
|
|
597
|
+
)
|
|
598
|
+
baseline_str, by_str_baseline, comparison_str, by_str_comparison = get_by_strings(
|
|
599
|
+
baseline_branch,
|
|
600
|
+
comparison_branch,
|
|
601
|
+
baseline_tag,
|
|
602
|
+
comparison_tag,
|
|
603
|
+
)
|
|
604
|
+
(
|
|
605
|
+
prefix,
|
|
606
|
+
testcases_setname,
|
|
607
|
+
_,
|
|
608
|
+
tsname_project_total_failures,
|
|
609
|
+
tsname_project_total_success,
|
|
610
|
+
_,
|
|
611
|
+
_,
|
|
612
|
+
_,
|
|
613
|
+
testcases_metric_context_path_setname,
|
|
614
|
+
_,
|
|
615
|
+
_,
|
|
616
|
+
_,
|
|
617
|
+
_,
|
|
618
|
+
_,
|
|
619
|
+
) = get_overall_dashboard_keynames(tf_github_org, tf_github_repo, tf_triggering_env)
|
|
620
|
+
test_names = []
|
|
621
|
+
used_key = testcases_setname
|
|
622
|
+
test_filter = "test_name"
|
|
623
|
+
if use_metric_context_path:
|
|
624
|
+
test_filter = "test_name:metric_context_path"
|
|
625
|
+
used_key = testcases_metric_context_path_setname
|
|
626
|
+
tags_regex_string = re.compile(testname_regex)
|
|
627
|
+
if test != "":
|
|
628
|
+
test_names = test.split(",")
|
|
629
|
+
logging.info("Using test name {}".format(test_names))
|
|
630
|
+
else:
|
|
631
|
+
test_names = get_test_names_from_db(
|
|
632
|
+
rts, tags_regex_string, test_names, used_key
|
|
633
|
+
)
|
|
634
|
+
(
|
|
635
|
+
detected_regressions,
|
|
636
|
+
table,
|
|
637
|
+
total_improvements,
|
|
638
|
+
total_regressions,
|
|
639
|
+
total_stable,
|
|
640
|
+
total_unstable,
|
|
641
|
+
total_comparison_points,
|
|
642
|
+
) = from_rts_to_regression_table(
|
|
643
|
+
baseline_deployment_name,
|
|
644
|
+
comparison_deployment_name,
|
|
645
|
+
baseline_str,
|
|
646
|
+
comparison_str,
|
|
647
|
+
by_str_baseline,
|
|
648
|
+
by_str_comparison,
|
|
649
|
+
from_ts_ms,
|
|
650
|
+
to_ts_ms,
|
|
651
|
+
last_n_baseline,
|
|
652
|
+
last_n_comparison,
|
|
653
|
+
metric_mode,
|
|
654
|
+
metric_name,
|
|
655
|
+
print_improvements_only,
|
|
656
|
+
print_regressions_only,
|
|
657
|
+
skip_unstable,
|
|
658
|
+
regressions_percent_lower_limit,
|
|
659
|
+
rts,
|
|
660
|
+
simplify_table,
|
|
661
|
+
test_filter,
|
|
662
|
+
test_names,
|
|
663
|
+
tf_triggering_env,
|
|
664
|
+
verbose,
|
|
665
|
+
running_platform,
|
|
666
|
+
)
|
|
667
|
+
logging.info(
|
|
668
|
+
"Printing differential analysis between {} and {}".format(
|
|
669
|
+
baseline_str, comparison_str
|
|
670
|
+
)
|
|
671
|
+
)
|
|
672
|
+
writer = MarkdownTableWriter(
|
|
673
|
+
table_name="Comparison between {} and {}.\n\nTime Period from {}. (environment used: {})\n".format(
|
|
674
|
+
baseline_str,
|
|
675
|
+
comparison_str,
|
|
676
|
+
from_human_str,
|
|
677
|
+
baseline_deployment_name,
|
|
678
|
+
),
|
|
679
|
+
headers=[
|
|
680
|
+
"Test Case",
|
|
681
|
+
"Baseline {} (median obs. +- std.dev)".format(baseline_str),
|
|
682
|
+
"Comparison {} (median obs. +- std.dev)".format(comparison_str),
|
|
683
|
+
"% change ({})".format(metric_mode),
|
|
684
|
+
"Note",
|
|
685
|
+
],
|
|
686
|
+
value_matrix=table,
|
|
687
|
+
)
|
|
688
|
+
table_output = ""
|
|
689
|
+
|
|
690
|
+
from io import StringIO
|
|
691
|
+
import sys
|
|
692
|
+
|
|
693
|
+
old_stdout = sys.stdout
|
|
694
|
+
sys.stdout = mystdout = StringIO()
|
|
695
|
+
|
|
696
|
+
writer.dump(mystdout, False)
|
|
697
|
+
|
|
698
|
+
sys.stdout = old_stdout
|
|
699
|
+
|
|
700
|
+
table_output = mystdout.getvalue()
|
|
701
|
+
|
|
702
|
+
return (
|
|
703
|
+
detected_regressions,
|
|
704
|
+
table_output,
|
|
705
|
+
total_improvements,
|
|
706
|
+
total_regressions,
|
|
707
|
+
total_stable,
|
|
708
|
+
total_unstable,
|
|
709
|
+
total_comparison_points,
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def get_by_strings(
|
|
714
|
+
baseline_branch,
|
|
715
|
+
comparison_branch,
|
|
716
|
+
baseline_tag,
|
|
717
|
+
comparison_tag,
|
|
718
|
+
):
|
|
719
|
+
baseline_covered = False
|
|
720
|
+
comparison_covered = False
|
|
721
|
+
by_str_baseline = ""
|
|
722
|
+
by_str_comparison = ""
|
|
723
|
+
baseline_str = ""
|
|
724
|
+
comparison_str = ""
|
|
725
|
+
if baseline_branch is not None:
|
|
726
|
+
baseline_covered = True
|
|
727
|
+
by_str_baseline = "branch"
|
|
728
|
+
baseline_str = baseline_branch
|
|
729
|
+
if comparison_branch is not None:
|
|
730
|
+
comparison_covered = True
|
|
731
|
+
by_str_comparison = "branch"
|
|
732
|
+
comparison_str = comparison_branch
|
|
733
|
+
|
|
734
|
+
if baseline_tag is not None:
|
|
735
|
+
if comparison_covered:
|
|
736
|
+
logging.error(
|
|
737
|
+
"--baseline-branch and --baseline-tag are mutually exclusive. Pick one..."
|
|
738
|
+
)
|
|
739
|
+
exit(1)
|
|
740
|
+
baseline_covered = True
|
|
741
|
+
by_str_baseline = "version"
|
|
742
|
+
baseline_str = baseline_tag
|
|
743
|
+
|
|
744
|
+
if comparison_tag is not None:
|
|
745
|
+
# check if we had already covered comparison
|
|
746
|
+
if comparison_covered:
|
|
747
|
+
logging.error(
|
|
748
|
+
"--comparison-branch and --comparison-tag are mutually exclusive. Pick one..."
|
|
749
|
+
)
|
|
750
|
+
exit(1)
|
|
751
|
+
comparison_covered = True
|
|
752
|
+
by_str_comparison = "version"
|
|
753
|
+
comparison_str = comparison_tag
|
|
754
|
+
|
|
755
|
+
if baseline_covered is False:
|
|
756
|
+
logging.error(
|
|
757
|
+
"You need to provider either " + "( --baseline-branch or --baseline-tag ) "
|
|
758
|
+
)
|
|
759
|
+
exit(1)
|
|
760
|
+
if comparison_covered is False:
|
|
761
|
+
logging.error(
|
|
762
|
+
"You need to provider either "
|
|
763
|
+
+ "( --comparison-branch or --comparison-tag ) "
|
|
764
|
+
)
|
|
765
|
+
exit(1)
|
|
766
|
+
return baseline_str, by_str_baseline, comparison_str, by_str_comparison
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
def from_rts_to_regression_table(
|
|
770
|
+
baseline_deployment_name,
|
|
771
|
+
comparison_deployment_name,
|
|
772
|
+
baseline_str,
|
|
773
|
+
comparison_str,
|
|
774
|
+
by_str_baseline,
|
|
775
|
+
by_str_comparison,
|
|
776
|
+
from_ts_ms,
|
|
777
|
+
to_ts_ms,
|
|
778
|
+
last_n_baseline,
|
|
779
|
+
last_n_comparison,
|
|
780
|
+
metric_mode,
|
|
781
|
+
metric_name,
|
|
782
|
+
print_improvements_only,
|
|
783
|
+
print_regressions_only,
|
|
784
|
+
skip_unstable,
|
|
785
|
+
regressions_percent_lower_limit,
|
|
786
|
+
rts,
|
|
787
|
+
simplify_table,
|
|
788
|
+
test_filter,
|
|
789
|
+
test_names,
|
|
790
|
+
tf_triggering_env,
|
|
791
|
+
verbose,
|
|
792
|
+
running_platform=None,
|
|
793
|
+
):
|
|
794
|
+
print_all = print_regressions_only is False and print_improvements_only is False
|
|
795
|
+
table = []
|
|
796
|
+
detected_regressions = []
|
|
797
|
+
total_improvements = 0
|
|
798
|
+
total_stable = 0
|
|
799
|
+
total_unstable = 0
|
|
800
|
+
total_regressions = 0
|
|
801
|
+
total_comparison_points = 0
|
|
802
|
+
noise_waterline = 3
|
|
803
|
+
progress = tqdm(unit="benchmark time-series", total=len(test_names))
|
|
804
|
+
at_comparison = 0
|
|
805
|
+
for test_name in test_names:
|
|
806
|
+
multi_value_baseline = check_multi_value_filter(baseline_str)
|
|
807
|
+
multi_value_comparison = check_multi_value_filter(comparison_str)
|
|
808
|
+
|
|
809
|
+
filters_baseline = [
|
|
810
|
+
"{}={}".format(by_str_baseline, baseline_str),
|
|
811
|
+
"metric={}".format(metric_name),
|
|
812
|
+
"{}={}".format(test_filter, test_name),
|
|
813
|
+
"deployment_name={}".format(baseline_deployment_name),
|
|
814
|
+
"triggering_env={}".format(tf_triggering_env),
|
|
815
|
+
]
|
|
816
|
+
if running_platform is not None:
|
|
817
|
+
filters_baseline.append("running_platform={}".format(running_platform))
|
|
818
|
+
filters_comparison = [
|
|
819
|
+
"{}={}".format(by_str_comparison, comparison_str),
|
|
820
|
+
"metric={}".format(metric_name),
|
|
821
|
+
"{}={}".format(test_filter, test_name),
|
|
822
|
+
"deployment_name={}".format(comparison_deployment_name),
|
|
823
|
+
"triggering_env={}".format(tf_triggering_env),
|
|
824
|
+
]
|
|
825
|
+
if running_platform is not None:
|
|
826
|
+
filters_comparison.append("running_platform={}".format(running_platform))
|
|
827
|
+
baseline_timeseries = rts.ts().queryindex(filters_baseline)
|
|
828
|
+
comparison_timeseries = rts.ts().queryindex(filters_comparison)
|
|
829
|
+
|
|
830
|
+
# avoiding target time-series
|
|
831
|
+
comparison_timeseries = [x for x in comparison_timeseries if "target" not in x]
|
|
832
|
+
baseline_timeseries = [x for x in baseline_timeseries if "target" not in x]
|
|
833
|
+
progress.update()
|
|
834
|
+
if verbose:
|
|
835
|
+
logging.info(
|
|
836
|
+
"Baseline timeseries for {}: {}. test={}".format(
|
|
837
|
+
baseline_str, len(baseline_timeseries), test_name
|
|
838
|
+
)
|
|
839
|
+
)
|
|
840
|
+
logging.info(
|
|
841
|
+
"Comparison timeseries for {}: {}. test={}".format(
|
|
842
|
+
comparison_str, len(comparison_timeseries), test_name
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
if len(baseline_timeseries) > 1 and multi_value_baseline is False:
|
|
846
|
+
baseline_timeseries = get_only_Totals(baseline_timeseries)
|
|
847
|
+
|
|
848
|
+
if len(baseline_timeseries) != 1 and multi_value_baseline is False:
|
|
849
|
+
if verbose:
|
|
850
|
+
logging.warning(
|
|
851
|
+
"Skipping this test given the value of timeseries !=1. Baseline timeseries {}".format(
|
|
852
|
+
len(baseline_timeseries)
|
|
853
|
+
)
|
|
854
|
+
)
|
|
855
|
+
if len(baseline_timeseries) > 1:
|
|
856
|
+
logging.warning(
|
|
857
|
+
"\t\tTime-series: {}".format(", ".join(baseline_timeseries))
|
|
858
|
+
)
|
|
859
|
+
continue
|
|
860
|
+
|
|
861
|
+
if len(comparison_timeseries) > 1 and multi_value_comparison is False:
|
|
862
|
+
comparison_timeseries = get_only_Totals(comparison_timeseries)
|
|
863
|
+
if len(comparison_timeseries) != 1 and multi_value_comparison is False:
|
|
864
|
+
if verbose:
|
|
865
|
+
logging.warning(
|
|
866
|
+
"Comparison timeseries {}".format(len(comparison_timeseries))
|
|
867
|
+
)
|
|
868
|
+
continue
|
|
869
|
+
|
|
870
|
+
baseline_v = "N/A"
|
|
871
|
+
comparison_v = "N/A"
|
|
872
|
+
baseline_values = []
|
|
873
|
+
baseline_datapoints = []
|
|
874
|
+
comparison_values = []
|
|
875
|
+
comparison_datapoints = []
|
|
876
|
+
percentage_change = 0.0
|
|
877
|
+
baseline_v_str = "N/A"
|
|
878
|
+
comparison_v_str = "N/A"
|
|
879
|
+
largest_variance = 0
|
|
880
|
+
baseline_pct_change = "N/A"
|
|
881
|
+
comparison_pct_change = "N/A"
|
|
882
|
+
|
|
883
|
+
note = ""
|
|
884
|
+
try:
|
|
885
|
+
for ts_name_baseline in baseline_timeseries:
|
|
886
|
+
datapoints_inner = rts.ts().revrange(
|
|
887
|
+
ts_name_baseline, from_ts_ms, to_ts_ms
|
|
888
|
+
)
|
|
889
|
+
baseline_datapoints.extend(datapoints_inner)
|
|
890
|
+
(
|
|
891
|
+
baseline_pct_change,
|
|
892
|
+
baseline_v,
|
|
893
|
+
largest_variance,
|
|
894
|
+
) = get_v_pct_change_and_largest_var(
|
|
895
|
+
baseline_datapoints,
|
|
896
|
+
baseline_pct_change,
|
|
897
|
+
baseline_v,
|
|
898
|
+
baseline_values,
|
|
899
|
+
largest_variance,
|
|
900
|
+
last_n_baseline,
|
|
901
|
+
verbose,
|
|
902
|
+
)
|
|
903
|
+
for ts_name_comparison in comparison_timeseries:
|
|
904
|
+
datapoints_inner = rts.ts().revrange(
|
|
905
|
+
ts_name_comparison, from_ts_ms, to_ts_ms
|
|
906
|
+
)
|
|
907
|
+
comparison_datapoints.extend(datapoints_inner)
|
|
908
|
+
|
|
909
|
+
(
|
|
910
|
+
comparison_pct_change,
|
|
911
|
+
comparison_v,
|
|
912
|
+
largest_variance,
|
|
913
|
+
) = get_v_pct_change_and_largest_var(
|
|
914
|
+
comparison_datapoints,
|
|
915
|
+
comparison_pct_change,
|
|
916
|
+
comparison_v,
|
|
917
|
+
comparison_values,
|
|
918
|
+
largest_variance,
|
|
919
|
+
last_n_comparison,
|
|
920
|
+
verbose,
|
|
921
|
+
)
|
|
922
|
+
|
|
923
|
+
waterline = regressions_percent_lower_limit
|
|
924
|
+
if regressions_percent_lower_limit < largest_variance:
|
|
925
|
+
note = "waterline={:.1f}%.".format(largest_variance)
|
|
926
|
+
waterline = largest_variance
|
|
927
|
+
|
|
928
|
+
except redis.exceptions.ResponseError:
|
|
929
|
+
pass
|
|
930
|
+
except ZeroDivisionError as e:
|
|
931
|
+
logging.error("Detected a ZeroDivisionError. {}".format(e.__str__()))
|
|
932
|
+
pass
|
|
933
|
+
unstable = False
|
|
934
|
+
if baseline_v != "N/A" and comparison_v != "N/A":
|
|
935
|
+
if comparison_pct_change > 10.0 or baseline_pct_change > 10.0:
|
|
936
|
+
note = "UNSTABLE (very high variance)"
|
|
937
|
+
unstable = True
|
|
938
|
+
|
|
939
|
+
baseline_v_str = prepare_value_str(
|
|
940
|
+
baseline_pct_change, baseline_v, baseline_values, simplify_table
|
|
941
|
+
)
|
|
942
|
+
comparison_v_str = prepare_value_str(
|
|
943
|
+
comparison_pct_change, comparison_v, comparison_values, simplify_table
|
|
944
|
+
)
|
|
945
|
+
|
|
946
|
+
if metric_mode == "higher-better":
|
|
947
|
+
percentage_change = (
|
|
948
|
+
float(comparison_v) / float(baseline_v) - 1
|
|
949
|
+
) * 100.0
|
|
950
|
+
else:
|
|
951
|
+
# lower-better
|
|
952
|
+
percentage_change = (
|
|
953
|
+
float(baseline_v) / float(comparison_v) - 1
|
|
954
|
+
) * 100.0
|
|
955
|
+
if baseline_v != "N/A" or comparison_v != "N/A":
|
|
956
|
+
detected_regression = False
|
|
957
|
+
detected_improvement = False
|
|
958
|
+
if percentage_change < 0.0 and not unstable:
|
|
959
|
+
if -waterline >= percentage_change:
|
|
960
|
+
detected_regression = True
|
|
961
|
+
total_regressions = total_regressions + 1
|
|
962
|
+
note = note + " REGRESSION"
|
|
963
|
+
detected_regressions.append(test_name)
|
|
964
|
+
elif percentage_change < -noise_waterline:
|
|
965
|
+
if simplify_table is False:
|
|
966
|
+
note = note + " potential REGRESSION"
|
|
967
|
+
else:
|
|
968
|
+
if simplify_table is False:
|
|
969
|
+
note = note + " No Change"
|
|
970
|
+
|
|
971
|
+
if percentage_change > 0.0 and not unstable:
|
|
972
|
+
if percentage_change > waterline:
|
|
973
|
+
detected_improvement = True
|
|
974
|
+
total_improvements = total_improvements + 1
|
|
975
|
+
note = note + " IMPROVEMENT"
|
|
976
|
+
elif percentage_change > noise_waterline:
|
|
977
|
+
if simplify_table is False:
|
|
978
|
+
note = note + " potential IMPROVEMENT"
|
|
979
|
+
else:
|
|
980
|
+
if simplify_table is False:
|
|
981
|
+
note = note + " No Change"
|
|
982
|
+
|
|
983
|
+
if (
|
|
984
|
+
detected_improvement is False
|
|
985
|
+
and detected_regression is False
|
|
986
|
+
and not unstable
|
|
987
|
+
):
|
|
988
|
+
total_stable = total_stable + 1
|
|
989
|
+
|
|
990
|
+
if unstable:
|
|
991
|
+
total_unstable += 1
|
|
992
|
+
|
|
993
|
+
should_add_line = False
|
|
994
|
+
if print_regressions_only and detected_regression:
|
|
995
|
+
should_add_line = True
|
|
996
|
+
if print_improvements_only and detected_improvement:
|
|
997
|
+
should_add_line = True
|
|
998
|
+
if print_all:
|
|
999
|
+
should_add_line = True
|
|
1000
|
+
if unstable and skip_unstable:
|
|
1001
|
+
should_add_line = False
|
|
1002
|
+
|
|
1003
|
+
if should_add_line:
|
|
1004
|
+
total_comparison_points = total_comparison_points + 1
|
|
1005
|
+
add_line(
|
|
1006
|
+
baseline_v_str,
|
|
1007
|
+
comparison_v_str,
|
|
1008
|
+
note,
|
|
1009
|
+
percentage_change,
|
|
1010
|
+
table,
|
|
1011
|
+
test_name,
|
|
1012
|
+
)
|
|
1013
|
+
return (
|
|
1014
|
+
detected_regressions,
|
|
1015
|
+
table,
|
|
1016
|
+
total_improvements,
|
|
1017
|
+
total_regressions,
|
|
1018
|
+
total_stable,
|
|
1019
|
+
total_unstable,
|
|
1020
|
+
total_comparison_points,
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
def get_only_Totals(baseline_timeseries):
|
|
1025
|
+
logging.warning("\t\tTime-series: {}".format(", ".join(baseline_timeseries)))
|
|
1026
|
+
logging.info("Checking if Totals will reduce timeseries.")
|
|
1027
|
+
new_base = []
|
|
1028
|
+
for ts_name in baseline_timeseries:
|
|
1029
|
+
if "Totals" in ts_name:
|
|
1030
|
+
new_base.append(ts_name)
|
|
1031
|
+
baseline_timeseries = new_base
|
|
1032
|
+
return baseline_timeseries
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
def check_multi_value_filter(baseline_str):
|
|
1036
|
+
multi_value_baseline = False
|
|
1037
|
+
if "(" in baseline_str and "," in baseline_str and ")" in baseline_str:
|
|
1038
|
+
multi_value_baseline = True
|
|
1039
|
+
return multi_value_baseline
|
|
1040
|
+
|
|
1041
|
+
|
|
1042
|
+
def prepare_value_str(baseline_pct_change, baseline_v, baseline_values, simplify_table):
|
|
1043
|
+
if baseline_v < 1.0:
|
|
1044
|
+
baseline_v_str = " {:.2f}".format(baseline_v)
|
|
1045
|
+
elif baseline_v < 10.0:
|
|
1046
|
+
baseline_v_str = " {:.1f}".format(baseline_v)
|
|
1047
|
+
else:
|
|
1048
|
+
baseline_v_str = " {:.0f}".format(baseline_v)
|
|
1049
|
+
stamp_b = ""
|
|
1050
|
+
if baseline_pct_change > 10.0:
|
|
1051
|
+
stamp_b = "UNSTABLE "
|
|
1052
|
+
if len(baseline_values) > 1:
|
|
1053
|
+
baseline_v_str += " +- {:.1f}% {}".format(
|
|
1054
|
+
baseline_pct_change,
|
|
1055
|
+
stamp_b,
|
|
1056
|
+
)
|
|
1057
|
+
if simplify_table is False and len(baseline_values) > 1:
|
|
1058
|
+
baseline_v_str += "({} datapoints)".format(len(baseline_values))
|
|
1059
|
+
return baseline_v_str
|
|
1060
|
+
|
|
1061
|
+
|
|
1062
|
+
def get_test_names_from_db(rts, tags_regex_string, test_names, used_key):
|
|
1063
|
+
try:
|
|
1064
|
+
test_names = rts.smembers(used_key)
|
|
1065
|
+
test_names = list(test_names)
|
|
1066
|
+
test_names.sort()
|
|
1067
|
+
final_test_names = []
|
|
1068
|
+
for test_name in test_names:
|
|
1069
|
+
test_name = test_name.decode()
|
|
1070
|
+
match_obj = re.search(tags_regex_string, test_name)
|
|
1071
|
+
if match_obj is not None:
|
|
1072
|
+
final_test_names.append(test_name)
|
|
1073
|
+
test_names = final_test_names
|
|
1074
|
+
|
|
1075
|
+
except redis.exceptions.ResponseError as e:
|
|
1076
|
+
logging.warning(
|
|
1077
|
+
"Error while trying to fetch test cases set (key={}) {}. ".format(
|
|
1078
|
+
used_key, e.__str__()
|
|
1079
|
+
)
|
|
1080
|
+
)
|
|
1081
|
+
pass
|
|
1082
|
+
logging.warning(
|
|
1083
|
+
"Based on test-cases set (key={}) we have {} comparison points. ".format(
|
|
1084
|
+
used_key, len(test_names)
|
|
1085
|
+
)
|
|
1086
|
+
)
|
|
1087
|
+
return test_names
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
def add_line(
|
|
1091
|
+
baseline_v_str,
|
|
1092
|
+
comparison_v_str,
|
|
1093
|
+
note,
|
|
1094
|
+
percentage_change,
|
|
1095
|
+
table,
|
|
1096
|
+
test_name,
|
|
1097
|
+
):
|
|
1098
|
+
percentage_change_str = "{:.1f}% ".format(percentage_change)
|
|
1099
|
+
table.append(
|
|
1100
|
+
[
|
|
1101
|
+
test_name,
|
|
1102
|
+
baseline_v_str,
|
|
1103
|
+
comparison_v_str,
|
|
1104
|
+
percentage_change_str,
|
|
1105
|
+
note.strip(),
|
|
1106
|
+
]
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
def get_v_pct_change_and_largest_var(
|
|
1111
|
+
comparison_datapoints,
|
|
1112
|
+
comparison_pct_change,
|
|
1113
|
+
comparison_v,
|
|
1114
|
+
comparison_values,
|
|
1115
|
+
largest_variance,
|
|
1116
|
+
last_n=-1,
|
|
1117
|
+
verbose=False,
|
|
1118
|
+
):
|
|
1119
|
+
comparison_nsamples = len(comparison_datapoints)
|
|
1120
|
+
if comparison_nsamples > 0:
|
|
1121
|
+
_, comparison_v = comparison_datapoints[0]
|
|
1122
|
+
for tuple in comparison_datapoints:
|
|
1123
|
+
if last_n < 0 or (last_n > 0 and len(comparison_values) < last_n):
|
|
1124
|
+
comparison_values.append(tuple[1])
|
|
1125
|
+
comparison_df = pd.DataFrame(comparison_values)
|
|
1126
|
+
comparison_median = float(comparison_df.median())
|
|
1127
|
+
comparison_v = comparison_median
|
|
1128
|
+
comparison_std = float(comparison_df.std())
|
|
1129
|
+
if verbose:
|
|
1130
|
+
logging.info(
|
|
1131
|
+
"comparison_datapoints: {} value: {}; std-dev: {}; median: {}".format(
|
|
1132
|
+
comparison_datapoints,
|
|
1133
|
+
comparison_v,
|
|
1134
|
+
comparison_std,
|
|
1135
|
+
comparison_median,
|
|
1136
|
+
)
|
|
1137
|
+
)
|
|
1138
|
+
comparison_pct_change = (comparison_std / comparison_median) * 100.0
|
|
1139
|
+
if comparison_pct_change > largest_variance:
|
|
1140
|
+
largest_variance = comparison_pct_change
|
|
1141
|
+
return comparison_pct_change, comparison_v, largest_variance
|
|
1142
|
+
|
|
1143
|
+
|
|
1144
|
+
def main():
|
|
1145
|
+
_, _, project_version = populate_with_poetry_data()
|
|
1146
|
+
project_name = "redis-benchmarks-spec-cli"
|
|
1147
|
+
parser = argparse.ArgumentParser(
|
|
1148
|
+
description=get_version_string(project_name, project_version),
|
|
1149
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
1150
|
+
)
|
|
1151
|
+
parser = create_compare_arguments(parser)
|
|
1152
|
+
args = parser.parse_args()
|
|
1153
|
+
compare_command_logic(args, project_name, project_version)
|