Skip to content

Commit 1256e3c

Browse files
Add an e2e perf/load test with blocking commits (#7780)
Co-authored-by: Amaury Chamayou <amchamay@microsoft.com>
1 parent c5da48c commit 1256e3c

3 files changed

Lines changed: 121 additions & 30 deletions

File tree

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,14 @@ if(BUILD_TESTS)
12311231
"1,write,3000000,primary"
12321232
)
12331233

1234+
add_piccolo_test(
1235+
NAME pi_basic_blocking
1236+
PYTHON_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/tests/infra/basicperf.py
1237+
CLIENT_BIN ./submit PERF_LABEL "Basic Blocking"
1238+
ADDITIONAL_ARGS --package "samples/apps/basic/basic" --client-def
1239+
"128,blocking_write,100,primary" --max-writes-ahead 0
1240+
)
1241+
12341242
add_piccolo_test(
12351243
NAME pi_basic_js
12361244
PYTHON_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/tests/infra/basicperf.py

samples/apps/basic/basic.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ namespace basicapp
5959
.set_forwarding_required(ccf::endpoints::ForwardingRequired::Never)
6060
.install();
6161

62+
make_endpoint(
63+
"/records/blocking/{key}", HTTP_PUT, put, {ccf::user_cert_auth_policy})
64+
.set_forwarding_required(ccf::endpoints::ForwardingRequired::Never)
65+
.set_consensus_committed_function(
66+
ccf::endpoints::default_respond_on_commit_func)
67+
.install();
68+
6269
auto get = [this](ccf::endpoints::ReadOnlyEndpointContext& ctx) {
6370
std::string key;
6471
std::string error;
@@ -95,6 +102,13 @@ namespace basicapp
95102
.set_forwarding_required(ccf::endpoints::ForwardingRequired::Never)
96103
.install();
97104

105+
make_read_only_endpoint(
106+
"/records/blocking/{key}", HTTP_GET, get, {ccf::user_cert_auth_policy})
107+
.set_forwarding_required(ccf::endpoints::ForwardingRequired::Never)
108+
.set_consensus_committed_function(
109+
ccf::endpoints::default_respond_on_commit_func)
110+
.install();
111+
98112
auto post = [](ccf::endpoints::EndpointContext& ctx) {
99113
const nlohmann::json body =
100114
nlohmann::json::parse(ctx.rpc_ctx->get_request_body());

tests/infra/basicperf.py

Lines changed: 99 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,62 @@ def read_from_key_space(
8989
)
9090

9191

92+
def blocking_write_to_key_space(
93+
key_space: List[str],
94+
iterations: int,
95+
msgs: generator.Messages,
96+
additional_headers: Dict[str, str],
97+
):
98+
LOG.info(
99+
f"Workload: {iterations} blocking writes to a range of {len(key_space)} keys"
100+
)
101+
indices = list(range(iterations))
102+
random.shuffle(indices)
103+
for index in indices:
104+
key = key_space[index % len(key_space)]
105+
msgs.append(
106+
f"/records/blocking/{key}",
107+
"PUT",
108+
additional_headers=additional_headers,
109+
body=f"{hashlib.sha256(key.encode()).hexdigest()}",
110+
content_type="text/plain",
111+
)
112+
113+
114+
def blocking_read_from_key_space(
115+
key_space: List[str],
116+
iterations: int,
117+
msgs: generator.Messages,
118+
additional_headers: Dict[str, str],
119+
):
120+
LOG.info(
121+
f"Workload: {iterations} blocking reads from a range of {len(key_space)} keys"
122+
)
123+
indices = list(range(iterations))
124+
random.shuffle(indices)
125+
for index in indices:
126+
key = key_space[index % len(key_space)]
127+
msgs.append(
128+
f"/records/blocking/{key}",
129+
"GET",
130+
additional_headers=additional_headers,
131+
content_type="text/plain",
132+
)
133+
134+
92135
def append_to_msgs(definition, key_space, iterations, msgs, additional_headers):
93136
if definition == "write":
94137
return write_to_key_space(key_space, iterations, msgs, additional_headers)
95138
elif definition == "read":
96139
return read_from_key_space(key_space, iterations, msgs, additional_headers)
140+
elif definition == "blocking_write":
141+
return blocking_write_to_key_space(
142+
key_space, iterations, msgs, additional_headers
143+
)
144+
elif definition == "blocking_read":
145+
return blocking_read_from_key_space(
146+
key_space, iterations, msgs, additional_headers
147+
)
97148
elif definition.startswith("rwmix:"):
98149
_, ratio = definition.split(":")
99150
assert iterations % 1000 == 0
@@ -576,13 +627,13 @@ def _shades(n, start, end):
576627
]
577628

578629
def blue_shades(n):
579-
return _shades(n, (59, 142, 234), (100, 100, 255))
630+
return _shades(n, (59, 142, 234), (180, 180, 255))
580631

581632
def green_shades(n):
582-
return _shades(n, (13, 188, 121), (100, 255, 100))
633+
return _shades(n, (13, 188, 121), (180, 255, 180))
583634

584635
def red_shades(n):
585-
return _shades(n, (205, 49, 49), (255, 100, 100))
636+
return _shades(n, (205, 49, 49), (255, 180, 180))
586637

587638
def format_title(title, width):
588639
"""Centered title bar in the style of plotext's simple_bar charts."""
@@ -610,11 +661,6 @@ def color_legend(names, *color_groups):
610661
)
611662
print(format_title(" ".join(parts), chart_width), end="")
612663

613-
# Generate per-client color palettes
614-
sent_colors = blue_shades(n_clients)
615-
rcvd_colors = green_shades(n_clients)
616-
error_colors = red_shades(n_clients)
617-
618664
# Compute per-client, per-second counts in a single pass
619665
agg_with_seconds = agg.with_columns(
620666
((pl.col("sendTime") - start_send) / 1000000)
@@ -668,15 +714,43 @@ def color_legend(names, *color_groups):
668714

669715
seconds = sorted(all_seconds)
670716

671-
use_builtin_legend = n_clients <= max_builtin_legend_clients
717+
# With many clients, bin them into groups so each group gets
718+
# enough bar width to be visible. Target ~chart_width/4 groups max.
719+
max_stacked_layers = chart_width // 4
720+
if n_clients > max_stacked_layers:
721+
group_size = (
722+
n_clients + max_stacked_layers - 1
723+
) // max_stacked_layers
724+
groups = [
725+
client_names[i : i + group_size]
726+
for i in range(0, n_clients, group_size)
727+
]
728+
group_names = [
729+
f"{g[0]}-{g[-1]}" if len(g) > 1 else g[0] for g in groups
730+
]
731+
else:
732+
groups = [[cn] for cn in client_names]
733+
group_names = client_names
672734

673-
# Stacked bar: sent per second, blue shades per client
674-
sent_stacks = [
675-
[client_sent[cn].get(s, 0) for s in seconds] for cn in client_names
676-
]
735+
n_groups = len(groups)
736+
sent_colors = blue_shades(n_groups)
737+
rcvd_colors = green_shades(n_groups)
738+
error_colors = red_shades(n_groups)
739+
740+
def group_stacks(per_client_dict):
741+
return [
742+
[
743+
sum(per_client_dict[cn].get(s, 0) for cn in group)
744+
for s in seconds
745+
]
746+
for group in groups
747+
]
748+
749+
# Stacked bar: sent per second
750+
sent_stacks = group_stacks(client_sent)
677751
sent_kwargs = {"colors": sent_colors}
678-
if use_builtin_legend:
679-
sent_kwargs["labels"] = client_names
752+
if n_groups <= max_builtin_legend_clients:
753+
sent_kwargs["labels"] = group_names
680754
plt.simple_stacked_bar(
681755
seconds,
682756
sent_stacks,
@@ -685,21 +759,16 @@ def color_legend(names, *color_groups):
685759
**sent_kwargs,
686760
)
687761
plt.show()
688-
if not use_builtin_legend:
689-
color_legend(client_names, (sent_colors, ""))
762+
if n_groups > max_builtin_legend_clients:
763+
color_legend(group_names, (sent_colors, ""))
690764

691-
# Stacked bar: received per second, green shades per client + red for errors
692-
rcvd_stacks = [
693-
[client_rcvd[cn].get(s, 0) for s in seconds] for cn in client_names
694-
]
695-
error_stacks = [
696-
[client_errors[cn].get(s, 0) for s in seconds]
697-
for cn in client_names
698-
]
765+
# Stacked bar: received per second + errors
766+
rcvd_stacks = group_stacks(client_rcvd)
767+
error_stacks = group_stacks(client_errors)
699768
rcvd_kwargs = {"colors": rcvd_colors + error_colors}
700-
if use_builtin_legend:
701-
rcvd_kwargs["labels"] = client_names + [
702-
f"{cn} errors" for cn in client_names
769+
if n_groups <= max_builtin_legend_clients:
770+
rcvd_kwargs["labels"] = group_names + [
771+
f"{gn} errors" for gn in group_names
703772
]
704773
plt.simple_stacked_bar(
705774
seconds,
@@ -709,9 +778,9 @@ def color_legend(names, *color_groups):
709778
**rcvd_kwargs,
710779
)
711780
plt.show()
712-
if not use_builtin_legend:
781+
if n_groups > max_builtin_legend_clients:
713782
color_legend(
714-
client_names,
783+
group_names,
715784
(rcvd_colors, ""),
716785
(error_colors, " errors"),
717786
)

0 commit comments

Comments
 (0)