Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
94fc611
Drop per-pod server.address label from rpc.client.call.duration metri…
JeffLuoo Oct 14, 2026
7469ab4
Mark workers unavailable if it's pod status changes (#2055)
sfunkenhauser Oct 14, 2026
fd83041
Stamp snapshots with SandboxRuntime and schedule restores on runtime-…
shrutiyam-glitch Oct 14, 2026
e826281
ateom-gvisor: attribute runsc output for the pause container (#2330)
cloudonly Oct 14, 2026
df1331d
Block DNS if there is no egress policy configured (#2524)
bowei Oct 14, 2026
15cccfe
csi: require serverName when TLS is enabled. (#2603)
lekhoavu2909 Oct 14, 2026
2cdd21c
Benchmark automation runners can reach ate-api-server again under ate…
haiyanmeng Oct 14, 2026
db2f833
WorkerService.MintAteomActorJwt mints ateom-for-actor JWTs for atelet…
ILLUM1N0X Oct 14, 2026
34121f1
ateapi: fail /readyz while the store is unreachable (#1989)
ericcurtin Oct 14, 2026
d3f169f
Move nighthawk benchmark server out of ate-system namespace (#2656)
yanavlasov Oct 14, 2026
fa07840
atenet-router: reach atunnel through mTLS CONNECT tunnels by default …
LiorLieberman Oct 14, 2026
8188a11
Upgrade Runbook GA (#1844)
HavenXia Oct 14, 2026
22863c3
Add proto message options to attach resource metadata. (#2454)
juli4n Oct 14, 2026
9ec4975
Changed SandboxClass from enum to string to allow extensibility (#2586)
laoj2 Oct 14, 2026
bbc2c1c
benchmarking: full request history, histogram restore/checkpoint, act…
Nishanth29 Oct 14, 2026
05b69e6
fix(atelet): fall back to anonymous GCS read with objectstore-plugin …
annapendleton Oct 14, 2026
72d0272
atelet: Prefer local snapshot on Restore (#2658)
msau42 Oct 14, 2026
5af03b8
Fix macOS DNS relay tests (#2667)
swiftdiaries Oct 14, 2026
e3b707f
run ateom under minimal init (#2626)
BenTheElder Oct 14, 2026
afe2e3a
atunnel: bound the tunnel open by a deadline (#2654)
ygao-g Oct 14, 2026
45c25ef
ateapi: [Part 4] deny RPCs without an authz rule and authorize tags (…
SHRUTI6991 Oct 14, 2026
e8d40c3
Consolidate API validation for sandbox class across services (#2587)
laoj2 Oct 14, 2026
b6e6346
HostingNode and MintAteomActorJwt trust a Worker's node only while th…
aramase Oct 14, 2026
2c9a05f
ExternalVolume no longer has a status field (#2456)
ericcurtin Oct 14, 2026
cd1fb39
Merge upstream DNS enforcement and sandbox runtime updates
EItanya Oct 14, 2026
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ We provide several sample applications demonstrating Agent Substrate's capabilit
* [Egress Traffic](docs/egress-traffic.md): Which protocols an Actor may reach the outside world with and which are blocked.
* [Enabling man-in-the-middle (MITM) interception for Actor Egress policy](docs/egress-trust-bundle.md): Egress policies such as header injection depend on MITM interception of Actor traffic. This guide explains how an Actor should be configured to enable interception.
* [Request Parking](docs/request-parking.md): How the router parks requests through transient worker-pool saturation.
* [Rolling Upgrade Runbook](docs/upgrade.md): Upgrade a running substrate node by node without losing actor state.
* [Upgrade Runbook](docs/upgrade.md): Upgrade a running install to a newer supported release, and roll back if needed.
* [Security Guide](docs/security.md): Security overview, security principles, and threat modeling.
* [Roadmap](docs/roadmap.md): Current limitations and what is planned next.
* [Benchmarking Guide](benchmarking/README.md): Locust-based load tests, monitoring stack, and the orchestrated benchmark harness.
Expand Down
42 changes: 32 additions & 10 deletions benchmarking/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ Four flags control the optional post-run measurements described in
Defaults to the in-cluster service installed by
[Optional: Prometheus + Grafana](#optional-prometheus--grafana).
* `--atelet-lag-s`: how long to wait after the run before reading the
atelet's snapshot metrics. Defaults to 70.
atelet's snapshot and active actor metrics. Defaults to 70.
* `--phase-breakdown` / `--no-phase-breakdown`: read the atelet and worker pod
logs once the run ends and add the suspend/resume phase percentiles to
`stats.jsonl`. On by default; needs `pods/log` access in `ate-system` and
Expand Down Expand Up @@ -522,7 +522,9 @@ them are checked into the repository.
those two keys, because it is what CI orchestration reads to decide whether a
trial ran at all.
* `stats.csv`, `stats_history.csv`, `failures.csv`, `exceptions.csv`: Locust's
own CSV output.
own CSV output. `stats_history.csv` has a row per operation every second as
well as the `Aggregated` row, so each operation's latency can be charted
over the run; filter to `Aggregated` for run-wide numbers.
* `logs.txt`, `traces.txt`: the runner log, and the trace IDs seen during the run.
* `stats.jsonl`: one JSON object per line, one per metric. Every row carries
the same five keys: `timestamp`, `tag`, `test_name`, `metric`, and a flat
Expand Down Expand Up @@ -565,7 +567,9 @@ comparing numbers across runs:
* **Actors are derived, not counted.** Locust only sees virtual users, so the
numerator is the peak user count times `--actors-per-user`. No server-side
gauge counts resident actors: `ate.actor.stats.sampled_actors` drops any
actor without a live resource measurement, so suspended ones fall out.
actor without a live resource measurement, so suspended ones fall out. It
is recorded as `active_actors` in `server_summary.json`, a running-actor
count rather than a resident one.
* **The denominators are read once, after the run.** A cluster that autoscaled
mid-run is measured at its final size, so the ratio pairs a peak from one
moment with a capacity from another.
Expand All @@ -591,11 +595,29 @@ actually did, independent of what the load generator reported.
* `snapshots.size_p50_mb` through `size_p99_mb`: actor memory image sizes.
* `snapshots.size_avg_mb`: mean memory image size.
* `snapshots.restore_p50_s` through `restore_p99_s`, `restore_mean_s`, and the
same for `checkpoint_*`: atelet restore and checkpoint latency.
same for `checkpoint_*`: how long the atelet took to resume or suspend an
actor (`ate_actor_{restore,checkpoint}_duration`, `total` phase). Every
snapshot kind counts, as in the sizes: the actor's own snapshot, a
template's first start from its golden snapshot, and a pause. The buckets
reach 60s, so a slower operation reads as 60s.
* `snapshots.checkpoints_in_window`, `checkpoints_cumulative`: checkpoint
volume over the steady-state window, and since the atelet started.
* `snapshots.checkpoint_mb_s`: bytes written per second spent checkpointing,
not per second of wall clock.
* `active_actors`: running actors (`ate_actor_stats_sampled_actors`), as a
percentile `summary` of the cluster-wide count, `atelets` seen, and a
`timeseries` every 10s over the whole run (ramp-up included). Each point has
the cluster-wide count and `active_atelets` hosting an actor. As the
assumptions above note, the gauge drops actors without a live measurement,
so suspended ones fall out and the count dips while actors are suspended.
The atelet stops exporting when a node has no running actor, so at a step
where at least one atelet reported, a missing one counts as 0. A step where
none reported is skipped, not 0, since a failed scrape looks the same; this
mostly hits 1-user runs. If the metric never appeared during the run, every
field is `null`. `atelets` counts atelet processes seen in the run, not
nodes: the collector keeps an exited atelet's last value for about 5
minutes, so a restart can briefly count twice, and an atelet scraped both
directly and through a collector counts twice.

Every distribution reports p50, p90, p95 and p99 over the steady-state window.
The steady-state window runs from the first to the last Locust sample at 90% or
Expand All @@ -608,13 +630,13 @@ every 10s.

The atelet exports before Prometheus scrapes it, so the harvest waits
`--atelet-lag-s` seconds (default 70, enough for the OTel SDK's 60s default
export and a 10s scrape)
and reads the snapshot window half that late. The window ends at the last
full-load sample, so teardown suspends are left out.
export and a 10s scrape) and reads the snapshot and `active_actors` windows
half that late, stamping `active_actors` points back by the same amount. The
window ends at the last full-load sample, so teardown suspends are left out.

`metadata.start_ts` and `end_ts` bound the whole run, which the packing
`timeseries` covers. `steady_start_ts` and `steady_end_ts` bound the
steady-state window. A flat subset of the numbers also goes into a
`metadata.start_ts` and `end_ts` bound the whole run, which the packing and
`active_actors` timeseries cover. `steady_start_ts` and `steady_end_ts` bound
the steady-state window. A flat subset of the numbers also goes into a
`server_summary` row in `stats.jsonl`.

Neither the Kubernetes API nor Prometheus is required. If either is unreachable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ apiVersion: v1
kind: Namespace
metadata:
name: benchmarking
labels:
# ate-system admits API clients only from namespaces with this label.
ate.dev/api-access: "true"
---
apiVersion: v1
kind: ServiceAccount
Expand Down
3 changes: 3 additions & 0 deletions benchmarking/automation/manifests/runner-job.yaml.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ apiVersion: v1
kind: Namespace
metadata:
name: benchmarking
labels:
# ate-system admits API clients only from namespaces with this label.
ate.dev/api-access: "true"
---
apiVersion: v1
kind: ServiceAccount
Expand Down
33 changes: 33 additions & 0 deletions benchmarking/automation/test_orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,39 @@ def test_no_placeholder_survives(self):
self.assertNotIn("${", orchestrator.render_template(self.TMPL, subs))


class RunnerNamespaceTest(unittest.TestCase):
"""Every runner Job's namespace carries the label ate-system's
NetworkPolicy admits API clients by; without it the runner cannot reach
ate-api-server."""

HERE = os.path.dirname(__file__)
POLICIES = os.path.join(HERE, "..", "..", "manifests", "ate-install", "ate-system-networkpolicies.yaml")

def api_client_labels(self):
for doc in yaml.safe_load_all(open(self.POLICIES)):
if not doc or doc["metadata"]["name"] != "ate-api-server":
continue
for rule in doc["spec"]["ingress"]:
for peer in rule.get("from", []):
if "namespaceSelector" in peer:
return peer["namespaceSelector"]["matchLabels"]
self.fail("ate-api-server NetworkPolicy admits no namespace by label")

def test_namespace_has_api_access_label(self):
want = self.api_client_labels()
for name in ("runner-job.yaml.tmpl", "nighthawk-ingress-runner-job.yaml.tmpl"):
with self.subTest(template=name):
text = open(os.path.join(self.HERE, "manifests", name)).read()
ns = next(
yaml.safe_load(d)
for d in text.split("\n---\n")
if "kind: Namespace" in d
)
self.assertEqual(ns["metadata"]["name"], "benchmarking")
for k, v in want.items():
self.assertEqual(ns["metadata"].get("labels", {}).get(k), v)


class JobNameTest(unittest.TestCase):
COMMIT = "ac41c06deadbeef"

Expand Down
13 changes: 9 additions & 4 deletions benchmarking/locust/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,11 @@ def parse_args() -> argparse.Namespace:
type=int,
default=70,
help=(
"Seconds to wait after the run, and to shift the snapshot window "
"by, so the atelet's last export is scraped. Must cover the "
"atelet's OTEL_METRIC_EXPORT_INTERVAL plus one Prometheus scrape; "
"the default fits the OTel SDK's 60s default export and a 10s scrape"
"Seconds to wait after the run, so the atelet's last export is "
"scraped; its snapshot and active actor windows are read half this "
"late. Must cover the atelet's OTEL_METRIC_EXPORT_INTERVAL plus one "
"Prometheus scrape; the default fits the OTel SDK's 60s default export "
"and a 10s scrape"
),
)
p.add_argument(
Expand Down Expand Up @@ -344,6 +345,10 @@ def run_test(args: argparse.Namespace, csv_prefix: Path, logs: TextIO, traces: T
"-t", args.duration,
"-u", str(args.users),
"--csv", str(csv_prefix),
# Every operation's row in stats_history.csv, not just Aggregated, so
# each one's latency can be charted over the run. Readers of the file
# here keep to the Aggregated rows.
"--csv-full-history",
]
if with_boomer:
# Master mode so boomer can connect as a worker on localhost:5557.
Expand Down
Loading
Loading