Skip to content

Commit 951ebe8

Browse files
ScottLinnncopybara-github
authored andcommitted
Add result parsing for benchbase
PiperOrigin-RevId: 839911398
1 parent 23d9533 commit 951ebe8

File tree

4 files changed

+114
-9
lines changed

4 files changed

+114
-9
lines changed

perfkitbenchmarker/linux_benchmarks/benchbase_benchmark.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,6 @@ def GetConfig(user_config: Dict[str, Any]) -> Dict[str, Any]:
8080
return config
8181

8282

83-
# TODO(shuninglin): need to implement auth logic(automatic password gen)
84-
# for DSQL
8583
def Prepare(benchmark_spec: bm_spec.BenchmarkSpec) -> None:
8684
"""Prepares the benchmark by installing BenchBase and loading data.
8785
@@ -147,10 +145,8 @@ def Run(benchmark_spec: bm_spec.BenchmarkSpec) -> List[sample.Sample]:
147145
' --execute=true"'
148146
)
149147
client_vm.RemoteCommand(run_command)
150-
# TODO(shuninglin): Parse results from the output files
151-
152-
samples: List[sample.Sample] = []
153-
return samples
148+
metadata = benchmark_spec.relational_db.GetResourceMetadata()
149+
return benchbase.ParseResults(client_vm, metadata)
154150

155151

156152
def Cleanup(benchmark_spec: bm_spec.BenchmarkSpec) -> None:

perfkitbenchmarker/linux_packages/benchbase.py

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,17 @@
1313
# limitations under the License.
1414

1515
"""Module containing Benchbase installation and cleanup functions."""
16-
# TODO(shuninglin): Add result parsing functions
1716

17+
import json
1818
import logging
1919
import os
20+
from typing import Any
2021

2122
from absl import flags
2223
import jinja2
2324
from perfkitbenchmarker import data as pkb_data
2425
from perfkitbenchmarker import errors
26+
from perfkitbenchmarker import sample
2527
from perfkitbenchmarker import sql_engine_utils
2628
from perfkitbenchmarker import virtual_machine
2729

@@ -209,6 +211,66 @@ def CreateConfigFile(vm: virtual_machine.BaseVirtualMachine) -> None:
209211
return
210212

211213

214+
def ParseResults(
215+
vm: virtual_machine.BaseVirtualMachine, metadata: dict[str, Any]
216+
) -> list[sample.Sample]:
217+
"""Parses the latest benchbase result file and returns metrics.
218+
219+
Args:
220+
vm: The virtual machine to parse results from.
221+
metadata: The metadata to attach to the samples.
222+
223+
Returns:
224+
A list of sample.Sample objects.
225+
226+
Raises:
227+
errors.Benchmarks.RunError: If the result file is not found or cannot be
228+
parsed.
229+
"""
230+
stdout, _ = vm.RemoteCommand(
231+
f'ls -t {BENCHBASE_DIR}/results/tpcc*summary.json | head -n 1'
232+
)
233+
result_file = stdout.strip()
234+
if not result_file:
235+
raise errors.Benchmarks.RunError('Benchbase result file not found.')
236+
stdout, _ = vm.RemoteCommand(f'cat {result_file}')
237+
try:
238+
results = json.loads(stdout)
239+
except json.JSONDecodeError as e:
240+
raise errors.Benchmarks.RunError(
241+
f'Error parsing benchbase result file {result_file}: {e}'
242+
) from e
243+
samples: list[sample.Sample] = []
244+
latency_metrics = results.get('Latency Distribution', {})
245+
if not latency_metrics:
246+
raise errors.Benchmarks.RunError(
247+
'Latency Distribution not found in benchbase result file.'
248+
)
249+
for key, value in latency_metrics.items():
250+
metric_name = (
251+
key.replace('(microseconds)', '').strip().replace(' ', '_').lower()
252+
)
253+
samples.append(sample.Sample(metric_name, value / 1000, 'ms', metadata))
254+
255+
if 'Throughput (requests/second)' in results:
256+
throughput = results['Throughput (requests/second)']
257+
samples.append(
258+
sample.Sample(
259+
'tps',
260+
throughput,
261+
'tps',
262+
metadata,
263+
)
264+
)
265+
tpmc = throughput * int(_BENCHBASE_TXN_WEIGHTS.value[0]) / 100.0 * 60.0
266+
samples.append(sample.Sample('tpmc', tpmc, 'tpm', metadata))
267+
else:
268+
raise errors.Benchmarks.RunError(
269+
'Throughput (requests/second) not found in benchbase result file.'
270+
)
271+
return samples
272+
273+
212274
def OverrideEndpoint(
213275
vm: virtual_machine.BaseVirtualMachine, endpoint: str
214276
) -> None:

tests/linux_benchmarks/benchbase_benchmark_test.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ def setUp(self):
3737
benchmark_spec.BenchmarkSpec, instance=True
3838
)
3939
self.mock_benchmark_spec.vms = [self.mock_vm]
40+
self.mock_benchmark_spec.relational_db = mock.Mock()
41+
self.mock_benchmark_spec.relational_db.GetResourceMetadata.return_value = {}
4042
self.mock_load_config = self.enter_context(
4143
mock.patch.object(configs, 'LoadConfig', autospec=True)
4244
)
@@ -58,10 +60,13 @@ def test_prepare(self, mock_create_config):
5860
self.mock_vm.Install.assert_called_once_with('benchbase')
5961
mock_create_config.assert_called_once_with(self.mock_vm)
6062

61-
def test_run(self):
62-
# TODO(shuninglin): Update test when Run is implemented
63+
@mock.patch.object(benchbase, 'ParseResults', autospec=True)
64+
def test_run(self, mock_parse_results):
65+
mock_parse_results.return_value = []
6366
results = benchbase_benchmark.Run(self.mock_benchmark_spec)
6467
self.assertEqual(results, [])
68+
self.mock_benchmark_spec.relational_db.GetResourceMetadata.assert_called_once()
69+
mock_parse_results.assert_called_once_with(self.mock_vm, {})
6570

6671

6772
if __name__ == '__main__':

tests/linux_packages/benchbase_test.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"""Tests for benchbase package."""
22

3+
import json
34
import unittest
45
from unittest import mock
56

@@ -110,6 +111,47 @@ def test_create_config_file_aurora_dsql(self, _):
110111
'jdbc:postgresql://localhost:5432/postgres', context['jdbc_url']
111112
)
112113

114+
def test_parse_results(self):
115+
self.vm.RemoteCommand.side_effect = [
116+
('tpcc_2025-12-03_19-45-37.summary.json', ''),
117+
(
118+
json.dumps({
119+
'Latency Distribution': {
120+
'95th Percentile Latency (microseconds)': 1555074,
121+
'Maximum Latency (microseconds)': 5588384,
122+
'Median Latency (microseconds)': 656210,
123+
'Minimum Latency (microseconds)': 7632,
124+
'25th Percentile Latency (microseconds)': 270068,
125+
'90th Percentile Latency (microseconds)': 1442239,
126+
'99th Percentile Latency (microseconds)': 2621825,
127+
'75th Percentile Latency (microseconds)': 1121111,
128+
'Average Latency (microseconds)': 732735,
129+
},
130+
'Throughput (requests/second)': 404.000,
131+
}),
132+
'',
133+
),
134+
]
135+
results = benchbase.ParseResults(self.vm, {})
136+
self.assertLen(results, 11)
137+
actual_metrics = {s.metric: s.value for s in results}
138+
expected_metrics = {
139+
'95th_percentile_latency': 1555.074,
140+
'maximum_latency': 5588.384,
141+
'median_latency': 656.210,
142+
'minimum_latency': 7.632,
143+
'25th_percentile_latency': 270.068,
144+
'90th_percentile_latency': 1442.239,
145+
'99th_percentile_latency': 2621.825,
146+
'75th_percentile_latency': 1121.111,
147+
'average_latency': 732.735,
148+
'tps': 404.000,
149+
'tpmc': 10908.0,
150+
}
151+
for metric, value in expected_metrics.items():
152+
self.assertIn(metric, actual_metrics)
153+
self.assertAlmostEqual(actual_metrics[metric], value, places=3)
154+
113155

114156
if __name__ == '__main__':
115157
unittest.main()

0 commit comments

Comments
 (0)