From a7d68ae75f1091ca5633b7714d1f726a10e0fc7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20Chodo=C5=82a?= Date: Tue, 8 Sep 2026 13:13:51 +0200 Subject: [PATCH] fix(executor): scrape the execution client through a short network alias Alloy's prometheus.scrape target was the client's container name, which the scenario name can push past the 63-character DNS label limit. Docker's embedded DNS then never resolves it, the client is silently never scraped, and the run's cgroup CPU comes out ~4% lower than for a client with a shorter name: the exposition work Alloy would have triggered is missing, while block processing is untouched. Swapping tags between two bit-identical images moved the "saving" with the tag. Give the client an `execution-client` alias on the scenario network and point Alloy at that; warn when the container name itself exceeds the limit. --- CLAUDE.md | 4 +++- src/expb/_version.py | 4 ++-- src/expb/payloads/executor/executor.py | 21 ++++++++++++++++++- src/expb/payloads/executor/executor_config.py | 13 ++++++++++-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 099ea6c..6024ac0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -314,7 +314,9 @@ Each scenario creates an isolated Docker bridge network: - Execution client container joins the network - K6 and Alloy containers join the same network -- Enables service discovery by container name +- Enables service discovery by container name; the execution client is also reachable as + `execution-client` (Alloy scrapes that alias, because container names longer than 63 + characters do not resolve) - Network is removed during cleanup ### Resource Limiting diff --git a/src/expb/_version.py b/src/expb/_version.py index 0594ff5..2040ea9 100644 --- a/src/expb/_version.py +++ b/src/expb/_version.py @@ -1,2 +1,2 @@ -__version__ = "0.1.0" -__commit__ = "7621458" +__version__ = "0.1.0" +__commit__ = "9609a1b" diff --git a/src/expb/payloads/executor/executor.py b/src/expb/payloads/executor/executor.py index 0b4eab5..bb176c5 100644 --- a/src/expb/payloads/executor/executor.py +++ b/src/expb/payloads/executor/executor.py @@ -681,7 +681,26 @@ def start_execution_client( run_kwargs["mem_swappiness"] = self.config.resources.mem_swappiness if self.config.execution_client_security_opt: run_kwargs["security_opt"] = self.config.execution_client_security_opt - container = self.config.docker_client.containers.run(**run_kwargs) + if container_network is None: + return self.config.docker_client.containers.run(**run_kwargs) + # Attach the client under its short aliases as well: the container name exceeds the + # 63-character DNS label limit for long scenario names, and a name that does not resolve + # silently leaves the client unscraped, which shows up as a spurious CPU saving. + name = run_kwargs["name"] + if len(name) > 63: + self.log.warning( + "Execution client container name exceeds the DNS label limit; use its network alias", + container=name, + aliases=self.config.get_execution_client_aliases(), + ) + run_kwargs.pop("detach") + container = self.config.docker_client.containers.create(**run_kwargs) + container_network.disconnect(container) + container_network.connect( + container, aliases=self.config.get_execution_client_aliases() + ) + container.start() + container.reload() return container def _client_host_pid(self, container: Container, timeout: int = 60) -> int | None: diff --git a/src/expb/payloads/executor/executor_config.py b/src/expb/payloads/executor/executor_config.py index 43b6e30..d62579a 100644 --- a/src/expb/payloads/executor/executor_config.py +++ b/src/expb/payloads/executor/executor_config.py @@ -32,6 +32,9 @@ from expb.payloads.executor.services.snapshots import SnapshotService +EXECUTION_CLIENT_ALIAS = "execution-client" + + # ExecutorConfig class is a collection of helper functions and configuration options for the Executor class class ExecutorConfig: def __init__( @@ -232,9 +235,15 @@ def get_execution_client_ports(self) -> dict[str, tuple[str, str]]: # f"{CLIENT_P2P_PORT}/udp": ("127.0.0.1", f"{CLIENT_P2P_PORT}"), } + def get_execution_client_aliases(self) -> list[str]: + # Docker's embedded DNS resolves a name only if it fits a 63-character DNS label. Scenario + # names push the client's container name past that, so the client also gets a short alias + # on the scenario network and everything that addresses it by name uses the alias. + return [EXECUTION_CLIENT_ALIAS] + def get_execution_metrics_address(self) -> str: - # Metrics endpoint is required before the actual execution container is started - return f"{self.get_execution_client_container_name()}:{CLIENT_METRICS_PORT}" + # Rendered into the Alloy config before the execution container exists, so it cannot be an IP. + return f"{EXECUTION_CLIENT_ALIAS}:{CLIENT_METRICS_PORT}" def get_execution_client_engine_url( self,