Skip to content

Commit e8a5032

Browse files
committed
ING-1419: Generate graphs from the spectroperf metrics
1 parent 96e4779 commit e8a5032

2 files changed

Lines changed: 112 additions & 0 deletions

File tree

.github/workflows/load_test.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,41 @@ jobs:
9999
run: |
100100
cat ./gateway/metrics.json
101101
102+
- name: Set up Python
103+
uses: actions/setup-python@v5
104+
with:
105+
python-version: '3.11' # Specify your version
106+
107+
- name: Install dependencies
108+
run: |
109+
python -m pip install --upgrade pip
110+
pip install numpy matplotlib
111+
112+
- name: Generate graphs from metrics
113+
run: |
114+
mv ./direct/metrics.json ./direct.json
115+
mv ./gateway/metrics.json ./gateway.json
116+
python plot_metrics.py 50
117+
python plot_metrics.py 95
118+
119+
- name: Upload Graphs as Artifacts
120+
uses: actions/upload-artifact@v4
121+
with:
122+
name: performance-graphs
123+
path: |
124+
metrics_p50.png
125+
metrics_p95.png
126+
127+
- name: Post Graphs to PR
128+
uses: thollander/actions-comment-pull-request@v2
129+
if: github.event_name == 'pull_request'
130+
with:
131+
message: |
132+
### 📊 Performance Metrics Report
133+
| P50 Latency | P95 Latency |
134+
| :---: | :---: |
135+
| ![P50](https://github.com/${{ github.repository }}/blob/${{ github.sha }}/metrics_p50.png?raw=true) | ![P95](https://github.com/${{ github.repository }}/blob/${{ github.sha }}/metrics_p95.png?raw=true) |
136+
102137
- name: Collect couchbase logs
103138
timeout-minutes: 10
104139
if: failure()

plot_metrics.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import sys
2+
import json
3+
import numpy as np
4+
import matplotlib
5+
matplotlib.use("Agg")
6+
import matplotlib.pyplot as plt
7+
8+
PERCENTILE_MAP = {
9+
50: "fiftieth",
10+
95: "ninetyFifth",
11+
98: "ninetyEighth",
12+
99: "ninetyNinth",
13+
}
14+
15+
if len(sys.argv) != 2 or not sys.argv[1].isdigit() or int(sys.argv[1]) not in PERCENTILE_MAP:
16+
print(f"Usage: python plot_metrics.py <percentile>")
17+
print(f"Valid percentiles: {sorted(PERCENTILE_MAP)}")
18+
sys.exit(1)
19+
20+
percentile = int(sys.argv[1])
21+
percentile_key = PERCENTILE_MAP[percentile]
22+
23+
with open("direct.json") as f:
24+
direct = json.load(f)
25+
26+
with open("gateway.json") as f:
27+
gateway = json.load(f)
28+
29+
def extract_data(data, key):
30+
ops = {}
31+
for entry in data:
32+
num_users = entry["numUsers"]
33+
for op in entry["operations"]:
34+
name = op["name"]
35+
if name not in ops:
36+
ops[name] = {"numUsers": [], "latency": []}
37+
ops[name]["numUsers"].append(num_users)
38+
ops[name]["latency"].append(op["latencyPercentiles"][key])
39+
return ops
40+
41+
def smooth(x, y):
42+
x = np.array(x)
43+
y = np.array(y)
44+
coeffs = np.polyfit(x, y, deg=min(2, len(x) - 1))
45+
x_smooth = np.linspace(x.min(), x.max(), 300)
46+
y_smooth = np.polyval(coeffs, x_smooth)
47+
return x_smooth, y_smooth
48+
49+
direct_ops = extract_data(direct, percentile_key)
50+
gateway_ops = extract_data(gateway, percentile_key)
51+
52+
operation_names = list(direct_ops.keys())
53+
54+
fig, axes = plt.subplots(1, len(operation_names), figsize=(5 * len(operation_names), 5))
55+
fig.suptitle(f"{percentile}th Percentile Latency by Operation", fontsize=14, fontweight="bold", y=1.02)
56+
57+
for ax, name in zip(axes, operation_names):
58+
for ops, label, marker, color in [
59+
(direct_ops, "Direct", "o", "tab:blue"),
60+
(gateway_ops, "Gateway", "s", "tab:orange"),
61+
]:
62+
x = ops[name]["numUsers"]
63+
y = ops[name]["latency"]
64+
x_smooth, y_smooth = smooth(x, y)
65+
ax.plot(x_smooth, y_smooth, color=color, linewidth=1.5)
66+
ax.plot(x, y, marker=marker, linestyle="None", color=color, label=label)
67+
68+
ax.set_title(name)
69+
ax.set_xlabel("Number of Users")
70+
ax.set_ylabel(f"Latency p{percentile} (ms)")
71+
ax.legend()
72+
ax.grid(True)
73+
74+
plt.tight_layout()
75+
output_file = f"metrics_p{percentile}.png"
76+
plt.savefig(output_file, dpi=150, bbox_inches="tight")
77+
print(f"Saved {output_file}")

0 commit comments

Comments
 (0)