Skip to content

Commit fac8d5f

Browse files
authored
Support benchmark type fallback (#3507)
PBENCH-1229 The intake code ensures that all dataset (even `server.archiveonly` datasets) will have a `server.benchmark` value representing the core benchmark used in the run. The visualization code uses this value to determine compatibility. However on the staging server, we have pre-existing datasets without the `server.benchmark` metadata, and the "internal server error" failure mode is unpleasant. To be a bit more friendly, this adds a wrapper that will first check the `server.benchmark` metadata directly, but failing that will check for the normal Pbench Agent `dataset.metalog.pbench.script` metadata so that older `pbench-uperf` runs will be recognized. Failing that, the wrapper falls back to "unknown" so that we'll never have to deal with a `None` value.
1 parent bc7e692 commit fac8d5f

File tree

3 files changed

+79
-14
lines changed

3 files changed

+79
-14
lines changed

lib/pbench/server/api/resources/datasets_compare.py

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,43 @@
2121
Schema,
2222
)
2323
from pbench.server.cache_manager import CacheManager
24-
from pbench.server.database.models.datasets import Metadata
24+
from pbench.server.database.models.datasets import Dataset, Metadata
2525

2626

2727
class DatasetsCompare(ApiBase):
2828
"""
2929
This class implements the Server API used to retrieve comparison data for visualization.
3030
"""
3131

32+
@staticmethod
33+
def get_benchmark_name(dataset: Dataset) -> str:
34+
"""Convenience function to get dataset's benchmark
35+
36+
The Pbench Server intake constructs a server.benchmark metadata key to
37+
represent the benchmark type. This helper implements a fallback for
38+
datasets processed prior to the implementation of server.benchmark to
39+
avoid null values, by using the Pbench Agent metadata.log "script"
40+
value if that exists and then a constant fallback value.
41+
42+
TODO: This is a workaround from the transition to server.benchmark in
43+
order to cleanly support earlier datasets on the staging server. This
44+
can be removed at some point, but it's not critical.
45+
46+
Args:
47+
dataset: Dataset object
48+
49+
Returns:
50+
A lowercase benchmark identifer string, including the value defined
51+
by Metadata.SERVER_BENCHMARK_UNKNOWN if we can't find a value.
52+
"""
53+
benchmark = Metadata.getvalue(dataset, Metadata.SERVER_BENCHMARK)
54+
55+
if not benchmark:
56+
benchmark = Metadata.getvalue(dataset, "dataset.metalog.pbench.script")
57+
if not benchmark:
58+
benchmark = Metadata.SERVER_BENCHMARK_UNKNOWN
59+
return benchmark
60+
3261
def __init__(self, config: PbenchServerConfig):
3362
super().__init__(
3463
config,
@@ -70,17 +99,8 @@ def _get(
7099
datasets = params.query.get("datasets")
71100
benchmark_choice = None
72101
for dataset in datasets:
73-
benchmark = Metadata.getvalue(dataset, Metadata.SERVER_BENCHMARK)
74-
# Validate if all the selected datasets is of same benchmark
75-
if not benchmark_choice:
76-
benchmark_choice = benchmark
77-
elif benchmark != benchmark_choice:
78-
raise APIAbort(
79-
HTTPStatus.BAD_REQUEST,
80-
f"Selected dataset benchmarks must match: {benchmark_choice} and {benchmark} cannot be compared.",
81-
)
82102

83-
# Validate if the user is authorized to access the selected datasets
103+
# Check that the user is authorized to read each dataset
84104
self._check_authorization(
85105
ApiAuthorization(
86106
ApiAuthorizationType.USER_ACCESS,
@@ -90,6 +110,16 @@ def _get(
90110
)
91111
)
92112

113+
# Determine the dataset benchmark and check consistency
114+
benchmark = DatasetsCompare.get_benchmark_name(dataset)
115+
if not benchmark_choice:
116+
benchmark_choice = benchmark
117+
elif benchmark != benchmark_choice:
118+
raise APIAbort(
119+
HTTPStatus.BAD_REQUEST,
120+
f"Selected dataset benchmarks must match: {benchmark_choice} and {benchmark} cannot be compared.",
121+
)
122+
93123
benchmark_type = BenchmarkName.__members__.get(benchmark.upper())
94124
if not benchmark_type:
95125
raise APIAbort(

lib/pbench/server/api/resources/datasets_visualize.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
ParamType,
2020
Schema,
2121
)
22+
from pbench.server.api.resources.datasets_compare import DatasetsCompare
2223
from pbench.server.cache_manager import CacheManager
23-
from pbench.server.database.models.datasets import Metadata
2424

2525

2626
class DatasetsVisualize(ApiBase):
@@ -59,8 +59,7 @@ def _get(
5959
"""
6060

6161
dataset = params.uri["dataset"]
62-
63-
benchmark = Metadata.getvalue(dataset, Metadata.SERVER_BENCHMARK)
62+
benchmark = DatasetsCompare.get_benchmark_name(dataset)
6463
benchmark_type = BenchmarkName.__members__.get(benchmark.upper())
6564
if not benchmark_type:
6665
raise APIAbort(

lib/pbench/test/unit/server/test_datasets_visualize.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,39 @@ def mock_get_metadata(_d: Dataset, _k: str, _u: Optional[User] = None) -> JSON:
132132
response = query_get_as("uperf_1", "test", HTTPStatus.BAD_REQUEST)
133133
assert response.json["message"] == "Unsupported Benchmark: hammerDB"
134134
assert extract_not_called
135+
136+
@pytest.mark.parametrize(
137+
"script,result", (("hammerDB", "hammerDB"), (None, "unknown"))
138+
)
139+
def test_benchmark_fallback(self, query_get_as, monkeypatch, script, result):
140+
"""Test benchmark metadata fallback.
141+
142+
If server.benchmark isn't defined, try dataset.metalog.pbench.script,
143+
then fall back to "unknown".
144+
145+
NOTE: This is deliberately not merged with test_unsupported_benchmark,
146+
which it closely resembles, because the benchmark identification
147+
tested here is intended to be a transitional workaround.
148+
"""
149+
150+
extract_not_called = True
151+
152+
def mock_extract_data(*args, **kwargs) -> JSON:
153+
nonlocal extract_not_called
154+
extract_not_called = False
155+
156+
@staticmethod
157+
def mock_get_metadata(_d: Dataset, k: str, _u: Optional[User] = None) -> JSON:
158+
if k == Metadata.SERVER_BENCHMARK:
159+
return None
160+
elif k == "dataset.metalog.pbench.script":
161+
return script
162+
else:
163+
return "Not what you expected?"
164+
165+
monkeypatch.setattr(CacheManager, "get_inventory", self.mock_get_inventory)
166+
monkeypatch.setattr(Metadata, "getvalue", mock_get_metadata)
167+
monkeypatch.setattr(QuisbyProcessing, "extract_data", mock_extract_data)
168+
response = query_get_as("uperf_1", "test", HTTPStatus.BAD_REQUEST)
169+
assert response.json["message"] == f"Unsupported Benchmark: {result}"
170+
assert extract_not_called

0 commit comments

Comments
 (0)