From dd0f378dde81efa63c7b879a6525f8882edb4d0b Mon Sep 17 00:00:00 2001 From: AndrewSillifant Date: Thu, 5 Mar 2026 18:10:55 -0700 Subject: [PATCH 1/4] Fix LICENSE badge link for PyPI rendering Use absolute GitHub URL instead of relative path that breaks on PyPI. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5fe172..17d0520 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![PyPI version](https://img.shields.io/pypi/v/hammerdb-scale)](https://pypi.org/project/hammerdb-scale/) [![Python versions](https://img.shields.io/pypi/pyversions/hammerdb-scale)](https://pypi.org/project/hammerdb-scale/) -[![License](https://img.shields.io/pypi/l/hammerdb-scale)](LICENSE) +[![License](https://img.shields.io/pypi/l/hammerdb-scale)](https://github.com/PureStorage-OpenConnect/hammerdb-scale/blob/main/LICENSE) A Python CLI for orchestrating parallel HammerDB database benchmarks at scale on Kubernetes. From 36c9a672c9cd06e5b6a9d183798ffb4adc2981e8 Mon Sep 17 00:00:00 2001 From: AndrewSillifant Date: Thu, 5 Mar 2026 22:44:01 -0700 Subject: [PATCH 2/4] Fix YAML scientific notation bug in run hash generation (2.0.2) Hex hashes like '764303e9' are parsed as floating point (7.64303e+9) by YAML 1.1 parsers, causing invalid Kubernetes job names. Ensure the hash always starts with a letter by sliding the 8-char window. Bump version to 2.0.2. --- pyproject.toml | 2 +- src/hammerdb_scale/constants.py | 2 +- src/hammerdb_scale/k8s/naming.py | 15 +++++++++++++-- tests/test_naming.py | 10 ++++++++++ 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6921073..53a95db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "hammerdb-scale" -version = "2.0.1" +version = "2.0.2" description = "CLI for orchestrating parallel HammerDB database benchmarks at scale on Kubernetes" readme = {file = "README.md", content-type = "text/markdown"} license = "Apache-2.0" diff --git a/src/hammerdb_scale/constants.py b/src/hammerdb_scale/constants.py index 7849a33..b10922a 100644 --- a/src/hammerdb_scale/constants.py +++ b/src/hammerdb_scale/constants.py @@ -2,7 +2,7 @@ from pathlib import Path -VERSION = "2.0.1" +VERSION = "2.0.2" DEFAULT_CONFIG_FILENAMES = ["hammerdb-scale.yaml", "hammerdb-scale.yml"] CONFIG_ENV_VAR = "HAMMERDB_SCALE_CONFIG" diff --git a/src/hammerdb_scale/k8s/naming.py b/src/hammerdb_scale/k8s/naming.py index 391a2e0..dd46f5c 100644 --- a/src/hammerdb_scale/k8s/naming.py +++ b/src/hammerdb_scale/k8s/naming.py @@ -9,9 +9,20 @@ def generate_run_hash(deployment_name: str, test_id: str) -> str: - """Deterministic 8-char hash for job naming.""" + """Deterministic 8-char hash for job naming. + + Always starts with a letter to prevent YAML parsers from + interpreting hex strings like '764303e9' as scientific notation. + """ raw = f"{deployment_name}-{test_id}" - return hashlib.sha256(raw.encode()).hexdigest()[:8] + digest = hashlib.sha256(raw.encode()).hexdigest() + # Find first 8-char window starting with a letter + for i in range(len(digest) - 7): + candidate = digest[i : i + 8] + if candidate[0].isalpha(): + return candidate + # Fallback: prefix with 'a', take 7 hex chars + return "a" + digest[:7] def generate_job_name(phase: str, target_index: int, run_hash: str) -> str: diff --git a/tests/test_naming.py b/tests/test_naming.py index 1291c47..d998537 100644 --- a/tests/test_naming.py +++ b/tests/test_naming.py @@ -34,6 +34,16 @@ def test_run_hash_different_inputs(): assert h1 != h2 +def test_run_hash_starts_with_letter(): + """Hash must start with a letter to avoid YAML scientific notation parsing. + + e.g. '764303e9' looks like 7.64303e+9 to YAML 1.1 parsers. + """ + for i in range(100): + h = generate_run_hash(f"app-{i}", f"app-{i}-20260101-{i:04d}") + assert h[0].isalpha(), f"Hash {h!r} starts with a digit" + + def test_job_name_format(): name = generate_job_name("run", 3, "a1b2c3d4") assert name == "hdb-run-03-a1b2c3d4" From 2ea7e5a9877e83b91389e9cc0337c7dfb3d128ca Mon Sep 17 00:00:00 2001 From: AndrewSillifant Date: Fri, 6 Mar 2026 11:44:41 -0700 Subject: [PATCH 3/4] Bump Helm chart version to 2.0.2 to match CLI release --- Chart.yaml | 2 +- src/hammerdb_scale/chart/Chart.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 16a4b4e..ca9252b 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: hammerdb-scale description: Modular Helm chart for parallel HammerDB scale testing across multiple databases (SQL Server, PostgreSQL, Oracle, MySQL) type: application -version: 1.1.0 +version: 2.0.2 appVersion: "5.0" keywords: - hammerdb diff --git a/src/hammerdb_scale/chart/Chart.yaml b/src/hammerdb_scale/chart/Chart.yaml index 16a4b4e..ca9252b 100644 --- a/src/hammerdb_scale/chart/Chart.yaml +++ b/src/hammerdb_scale/chart/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: hammerdb-scale description: Modular Helm chart for parallel HammerDB scale testing across multiple databases (SQL Server, PostgreSQL, Oracle, MySQL) type: application -version: 1.1.0 +version: 2.0.2 appVersion: "5.0" keywords: - hammerdb From cfbc1805932785a0c022662838a728c82fabfc21 Mon Sep 17 00:00:00 2001 From: AndrewSillifant Date: Fri, 6 Mar 2026 11:58:22 -0700 Subject: [PATCH 4/4] Fix test ID resolution to filter by deployment name resolve_test_id now passes deployment_name to the K8s lookup, preventing a running job from a different config from shadowing the correct results. Bump version to 2.0.3. --- pyproject.toml | 2 +- src/hammerdb_scale/k8s/jobs.py | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 53a95db..55753b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "hammerdb-scale" -version = "2.0.2" +version = "2.0.3" description = "CLI for orchestrating parallel HammerDB database benchmarks at scale on Kubernetes" readme = {file = "README.md", content-type = "text/markdown"} license = "Apache-2.0" diff --git a/src/hammerdb_scale/k8s/jobs.py b/src/hammerdb_scale/k8s/jobs.py index 4e6de92..3ae2af2 100644 --- a/src/hammerdb_scale/k8s/jobs.py +++ b/src/hammerdb_scale/k8s/jobs.py @@ -135,8 +135,8 @@ def resolve_test_id( if cli_id: return cli_id - # Try K8s - k8s_id = _find_most_recent_k8s_test_id(namespace) + # Try K8s — filter by deployment_name if a config was provided + k8s_id = _find_most_recent_k8s_test_id(namespace, deployment_name) if k8s_id: return k8s_id @@ -155,8 +155,15 @@ def resolve_test_id( raise NoResultsError("No test runs found in K8s or local results directory.") -def _find_most_recent_k8s_test_id(namespace: str) -> str | None: - """Find the most recent test ID from Helm releases.""" +def _find_most_recent_k8s_test_id( + namespace: str, deployment_name: str | None = None +) -> str | None: + """Find the most recent test ID from Helm releases. + + When deployment_name is provided (i.e. a config file was given), + only return a test ID that belongs to that deployment. This prevents + a running job from a *different* config from shadowing stored results. + """ try: releases = helm_list(namespace) if not releases: @@ -187,6 +194,10 @@ def _find_most_recent_k8s_test_id(namespace: str) -> str | None: labels = items[0].get("metadata", {}).get("labels", {}) test_id = labels.get("hammerdb.io/test-id") if test_id: + if deployment_name and not test_id.startswith( + deployment_name + "-" + ): + continue return test_id except (KubectlError, json.JSONDecodeError): continue