Skip to content

Commit f424438

Browse files
Include p99 latency tracking (#274)
* Added 142 new benchmarks focused on latency tracking (rate-limited) * Bumping version from 0.1.240 to 0.1.241 * Added 142 new benchmarks focused on latency tracking (rate-limited) * Using docker image redislabs/memtier_benchmark:2.1.0 on latency related benchmarks * Removed benchmarks above 25gb * Removed 2TB test * Deleted wrongly designed test * Added 3M keys 512 and 1000B GET/SET use-cases * include p99 latency tracking
1 parent 4f37432 commit f424438

File tree

5 files changed

+251
-9
lines changed

5 files changed

+251
-9
lines changed

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "redis-benchmarks-specification"
3-
version = "0.1.245"
3+
version = "0.1.247"
44
description = "The Redis benchmarks specification describes the cross-language/tools requirements and expectations to foster performance and observability standards around redis related technologies. Members from both industry and academia, including organizations and individuals are encouraged to contribute."
55
authors = ["filipecosta90 <filipecosta.90@gmail.com>","Redis Performance Group <performance@redis.com>"]
66
readme = "Readme.md"

redis_benchmarks_specification/__cli__/cli.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,18 @@ def get_commits_by_tags(args, repo):
243243
def get_repo(args):
244244
redisDirPath = args.redis_repo
245245
cleanUp = False
246+
last_n = args.last_n
246247
if redisDirPath is None:
247248
cleanUp = True
248249
redisDirPath = tempfile.mkdtemp()
249250
remote_url = f"https://github.com/{args.gh_org}/{args.gh_repo}"
250251
logging.info(
251252
f"Retrieving redis repo from remote {remote_url} into {redisDirPath}. Using branch {args.branch}."
252253
)
253-
cmd = f"git clone {remote_url} {redisDirPath} --branch {args.branch}\n"
254+
depth_str = ""
255+
if last_n > 0:
256+
depth_str = f" --depth {last_n}"
257+
cmd = f"git clone {remote_url} {redisDirPath} --branch {args.branch} {depth_str}\n"
254258
process = subprocess.Popen(
255259
"/bin/bash", stdin=subprocess.PIPE, stdout=subprocess.PIPE
256260
)

redis_benchmarks_specification/__compare__/compare.py

+41-6
Original file line numberDiff line numberDiff line change
@@ -1019,8 +1019,13 @@ def from_rts_to_regression_table(
10191019
baseline_only_list = []
10201020
comparison_only_list = []
10211021
no_datapoints_list = []
1022+
no_datapoints_baseline_list = []
1023+
no_datapoints_comparison_list = []
1024+
original_metric_mode = metric_mode
10221025
for test_name in test_names:
1026+
metric_mode = original_metric_mode
10231027
compare_version = "main"
1028+
# GE
10241029
github_link = "https://github.com/redis/redis-benchmarks-specification/blob"
10251030
test_path = f"redis_benchmarks_specification/test-suites/{test_name}.yml"
10261031
test_link = f"[{test_name}]({github_link}/{compare_version}/{test_path})"
@@ -1076,6 +1081,22 @@ def from_rts_to_regression_table(
10761081
if len(baseline_timeseries) > 1 and multi_value_baseline is False:
10771082
baseline_timeseries = get_only_Totals(baseline_timeseries)
10781083

1084+
if len(baseline_timeseries) == 0:
1085+
logging.warning(
1086+
f"No datapoints for test={test_name} for baseline timeseries {baseline_timeseries}"
1087+
)
1088+
no_datapoints_baseline_list.append(test_name)
1089+
if test_name not in no_datapoints_list:
1090+
no_datapoints_list.append(test_name)
1091+
1092+
if len(comparison_timeseries) == 0:
1093+
logging.warning(
1094+
f"No datapoints for test={test_name} for comparison timeseries {comparison_timeseries}"
1095+
)
1096+
no_datapoints_comparison_list.append(test_name)
1097+
if test_name not in no_datapoints_list:
1098+
no_datapoints_list.append(test_name)
1099+
10791100
if len(baseline_timeseries) != 1 and multi_value_baseline is False:
10801101
if verbose:
10811102
logging.warning(
@@ -1152,11 +1173,14 @@ def from_rts_to_regression_table(
11521173
)
11531174

11541175
waterline = regressions_percent_lower_limit
1155-
if regressions_percent_lower_limit < largest_variance:
1156-
note = "waterline={:.1f}%.".format(largest_variance)
1157-
waterline = largest_variance
1176+
# if regressions_percent_lower_limit < largest_variance:
1177+
# note = "waterline={:.1f}%.".format(largest_variance)
1178+
# waterline = largest_variance
11581179

1159-
except redis.exceptions.ResponseError:
1180+
except redis.exceptions.ResponseError as e:
1181+
logging.error(
1182+
"Detected a redis.exceptions.ResponseError. {}".format(e.__str__())
1183+
)
11601184
pass
11611185
except ZeroDivisionError as e:
11621186
logging.error("Detected a ZeroDivisionError. {}".format(e.__str__()))
@@ -1198,7 +1222,7 @@ def from_rts_to_regression_table(
11981222
else:
11991223
# lower-better
12001224
percentage_change = (
1201-
float(baseline_v) / float(comparison_v) - 1
1225+
-(float(baseline_v) - float(comparison_v)) / float(baseline_v)
12021226
) * 100.0
12031227
else:
12041228
logging.warn(
@@ -1280,16 +1304,27 @@ def from_rts_to_regression_table(
12801304
logging.warning(
12811305
"There were no datapoints both for baseline and comparison for test: {test_name}"
12821306
)
1283-
no_datapoints_list.append(test_name)
1307+
if test_name not in no_datapoints_list:
1308+
no_datapoints_list.append(test_name)
12841309
logging.warning(
12851310
f"There is a total of {len(no_datapoints_list)} tests without datapoints for baseline AND comparison"
12861311
)
12871312
logging.info(
12881313
f"There is a total of {len(comparison_only_list)} tests without datapoints for baseline"
12891314
)
1315+
print(
1316+
"No datapoint baseline regex={test_names_str}".format(
1317+
test_names_str="|".join(no_datapoints_baseline_list)
1318+
)
1319+
)
12901320
logging.info(
12911321
f"There is a total of {len(baseline_only_list)} tests without datapoints for comparison"
12921322
)
1323+
print(
1324+
"No datapoint comparison regex={test_names_str}".format(
1325+
test_names_str="|".join(no_datapoints_comparison_list)
1326+
)
1327+
)
12931328
logging.info(f"There is a total of {len(unstable_list)} UNSTABLE tests")
12941329
return (
12951330
detected_regressions,

redis_benchmarks_specification/test-suites/defaults.yml

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ exporter:
1010
- $."BEST RUN RESULTS".Totals."Latency"
1111
- $."BEST RUN RESULTS".Totals."Misses/sec"
1212
- $."BEST RUN RESULTS".Totals."Percentile Latencies"."p50.00"
13+
- $."BEST RUN RESULTS".Totals."Percentile Latencies"."p99.00"
1314
- $."WORST RUN RESULTS".Totals."Ops/sec"
1415
- $."WORST RUN RESULTS".Totals."Latency"
1516
- $."WORST RUN RESULTS".Totals."Misses/sec"
1617
- $."WORST RUN RESULTS".Totals."Percentile Latencies"."p50.00"
18+
- $."WORST RUN RESULTS".Totals."Percentile Latencies"."p99.00"
1719
- $."AGGREGATED AVERAGE RESULTS (3 runs)".Totals."Ops/sec"
1820
- $."AGGREGATED AVERAGE RESULTS (3 runs)".Totals."Latency"
1921
- $."AGGREGATED AVERAGE RESULTS (3 runs)".Totals."Misses/sec"
20-
- $."AGGREGATED AVERAGE RESULTS (5 runs)".Totals."Percentile Latencies"."p50.00"
22+
- $."AGGREGATED AVERAGE RESULTS (3 runs)".Totals."Percentile Latencies"."p50.00"
23+
- $."AGGREGATED AVERAGE RESULTS (3 runs)".Totals."Percentile Latencies"."p99.00"
2124
- $."ALL STATS".Totals."Ops/sec"
2225
- $."ALL STATS".Totals."Latency"
2326
- $."ALL STATS".Totals."Misses/sec"
2427
- $."ALL STATS".Totals."Percentile Latencies"."p50.00"
28+
- $."ALL STATS".Totals."Percentile Latencies"."p99.00"

utils/summary.py

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import os
2+
import argparse
3+
from ruamel.yaml import YAML
4+
import collections
5+
6+
# Command groups mapping
7+
COMMAND_GROUPS = {
8+
"string": ["set", "get", "append", "getbit", "setrange", "bitcount", "mget"],
9+
"hash": [
10+
"hset",
11+
"hget",
12+
"hincrby",
13+
"hmset",
14+
"hdel",
15+
"hscan",
16+
"hexists",
17+
"hkeys",
18+
"hvals",
19+
"hmget",
20+
"hsetnx",
21+
"hgetall",
22+
],
23+
"list": ["lpush", "rpop", "lpop", "lrem", "lrange", "lindex", "lpos", "linsert"],
24+
"set": [
25+
"sadd",
26+
"smembers",
27+
"sismember",
28+
"sunion",
29+
"sdiff",
30+
"sinter",
31+
"smismember",
32+
"sscan",
33+
],
34+
"sorted_set": [
35+
"zadd",
36+
"zrange",
37+
"zrevrange",
38+
"zrangebyscore",
39+
"zrevrangebyscore",
40+
"zincrby",
41+
"zrem",
42+
"zscore",
43+
"zrank",
44+
"zunion",
45+
"zunionstore",
46+
"zrevrank",
47+
"zscan",
48+
"zcard",
49+
],
50+
"stream": ["xadd", "xread"],
51+
"geospatial": ["geosearch", "geopos", "geohash", "geodist"],
52+
"key_management": [
53+
"expire",
54+
"pexpire",
55+
"ttl",
56+
"expireat",
57+
"touch",
58+
"del",
59+
"exists",
60+
],
61+
"pubsub": ["ping", "hello"],
62+
"scripting": ["eval", "evalsha"],
63+
"transaction": ["multi", "exec"],
64+
"hyperloglog": ["pfadd"],
65+
"server_management": ["hello"],
66+
}
67+
68+
69+
def parse_arguments(arguments):
70+
"""
71+
Parses the memtier benchmark arguments to extract relevant parameters.
72+
Specifically extracts the --command argument.
73+
74+
Args:
75+
arguments (str): The arguments string from the YAML file.
76+
77+
Returns:
78+
dict: A dictionary containing extracted parameters.
79+
"""
80+
params = {}
81+
command = None
82+
83+
for arg in arguments.split():
84+
if arg.startswith("--command="):
85+
command = arg.split("=", 1)[1]
86+
elif arg == "--command":
87+
command = arguments.split()[arguments.split().index(arg) + 1]
88+
89+
return command
90+
91+
92+
def categorize_command(command):
93+
"""
94+
Categorize a Redis command into a command group.
95+
96+
Args:
97+
command (str): The Redis command.
98+
99+
Returns:
100+
str: The command group.
101+
"""
102+
for group, commands in COMMAND_GROUPS.items():
103+
if command in commands:
104+
return group
105+
return "unknown"
106+
107+
108+
def summarize_yaml_file(yaml_file_path, command_summary, command_group_summary):
109+
"""
110+
Processes a single YAML file to extract the tested commands and groups.
111+
112+
Args:
113+
yaml_file_path (str): Path to the YAML file.
114+
command_summary (dict): Dictionary to store the command summary.
115+
command_group_summary (dict): Dictionary to store the command group summary.
116+
"""
117+
yaml = YAML()
118+
yaml.preserve_quotes = True
119+
120+
try:
121+
with open(yaml_file_path, "r") as file:
122+
config = yaml.load(file)
123+
except Exception as e:
124+
print(f"Error reading {yaml_file_path}: {e}")
125+
return
126+
127+
# Extract tested commands from 'tested-commands'
128+
tested_commands = config.get("tested-commands", [])
129+
for command in tested_commands:
130+
command_summary["tested_commands"][command] += 1
131+
command_group = categorize_command(command)
132+
command_group_summary[command_group] += 1
133+
134+
# Extract command from 'clientconfig.arguments'
135+
arguments = config.get("clientconfig", {}).get("arguments", "")
136+
if arguments:
137+
command = parse_arguments(arguments)
138+
if command:
139+
command_summary["client_arguments_commands"][command] += 1
140+
command_group = categorize_command(command)
141+
command_group_summary[command_group] += 1
142+
143+
144+
def summarize_directory(directory):
145+
"""
146+
Summarizes the commands and command groups across all YAML files in a directory.
147+
148+
Args:
149+
directory (str): Path to the directory containing YAML files.
150+
"""
151+
command_summary = {
152+
"tested_commands": collections.Counter(),
153+
"client_arguments_commands": collections.Counter(),
154+
}
155+
command_group_summary = collections.Counter()
156+
157+
# Iterate over all YAML files in the directory
158+
for filename in os.listdir(directory):
159+
if filename.endswith(".yml") or filename.endswith(".yaml"):
160+
yaml_file_path = os.path.join(directory, filename)
161+
summarize_yaml_file(yaml_file_path, command_summary, command_group_summary)
162+
163+
# Print summary
164+
print("\nTested Commands Summary:")
165+
for command, count in command_summary["tested_commands"].items():
166+
print(f"{command}: {count} occurrences")
167+
168+
print("\nClient Arguments Commands Summary:")
169+
for command, count in command_summary["client_arguments_commands"].items():
170+
print(f"{command}: {count} occurrences")
171+
172+
print("\nCommand Group Summary:")
173+
for group, count in command_group_summary.items():
174+
print(f"{group.capitalize()}: {count} occurrences")
175+
176+
177+
def main():
178+
parser = argparse.ArgumentParser(
179+
description="Summarize commands and command groups from YAML benchmark files."
180+
)
181+
parser.add_argument(
182+
"--directory",
183+
type=str,
184+
default="../redis_benchmarks_specification/test-suites/",
185+
help="Path to the directory containing YAML test files.",
186+
)
187+
188+
args = parser.parse_args()
189+
directory = args.directory
190+
191+
if not os.path.isdir(directory):
192+
print(f"Directory {directory} does not exist.")
193+
return
194+
195+
summarize_directory(directory)
196+
197+
198+
if __name__ == "__main__":
199+
main()

0 commit comments

Comments
 (0)