Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "hammerdb-scale"
version = "2.0.1"
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"
Expand Down
2 changes: 1 addition & 1 deletion src/hammerdb_scale/chart/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/hammerdb_scale/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
19 changes: 15 additions & 4 deletions src/hammerdb_scale/k8s/jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down
15 changes: 13 additions & 2 deletions src/hammerdb_scale/k8s/naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
10 changes: 10 additions & 0 deletions tests/test_naming.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down