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
5 changes: 4 additions & 1 deletion .github/actionlint.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
self-hosted-runner:
# `celeris-cluster` is the label our ephemeral cluster runners
# register themselves with (see .github/actions/cluster-runner-up).
# Declaring it here silences actionlint's runner-label warning.
# `msr1` pins matrix orchestration to that specific node (it is the
# benchmark conductor; see benchmark-tier.yml runs-on). Declaring both
# here silences actionlint's runner-label warning.
labels:
- celeris-cluster
- msr1
79 changes: 61 additions & 18 deletions .github/workflows/benchmark-tier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,34 @@ name: Benchmark Tier
# never runs from an untrusted PR surface).

on:
schedule:
# Weekly full-grid SATURATION bench (the "fast" profile: rated OFF, 35s/10s,
# ~21.6h — fits the 24h cluster slot). This re-activates the weekly cadence
# that was previously manual-only. Wednesday 04:00 UTC keeps it clear of the
# weekend soak; the matrix-tier-cluster concurrency group serializes anyway.
- cron: "0 4 * * 3"
workflow_dispatch:
inputs:
profile:
description: "full | headline (default: full — every server × every scenario, capability-gated. headline is the ~3h smoke-test opt-in, never the silent default, because users asked repeatedly for \"no missing tests\" and got the curated subset instead.)"
description: "fast (DEFAULT, recommended): full grid, saturation-only (rated OFF), 35s/10s → ~21.6h, fits 24h. | full / headline: ALSO run the rated/SLO sweep — much longer; full needs a raised BENCH_BUDGET (set automatically below) and won't fit 24h."
required: false
type: string
default: full
default: fast
competitors:
description: "Adapter columns to BENCH (BENCH_COMPETITORS). 'all' (DEFAULT) runs the full grid; a CSV of registry slugs (e.g. 'httpzig,drogon,aspnet-h2') runs ONLY those columns — used to re-run the subset that DNF'd in a prior run without paying for the whole grid. Names must match servers.Registry."
required: false
type: string
default: all
deploy_competitors:
description: "Binary set to BUILD+DEPLOY (DEPLOY_COMPETITORS). 'all' (DEFAULT). For a subset re-run set this to the underlying BINARY names — h2 columns share their h1 sibling's binary, so 'aspnet-h2' needs binary 'aspnet' (e.g. 'httpzig,drogon,aspnet,axum'). Must be the deploy-side names (nativeBuildSpecs/servers dirs), not column slugs."
required: false
type: string
default: all
publish:
description: "Push results to the docs repo (BENCH_PUBLISH). 'true' (DEFAULT) publishes; 'false' produces data only (no docs push, no integrity gate) — use for a subset re-run that you compose + publish manually."
required: false
type: string
default: "true"

permissions:
contents: read
Expand All @@ -45,7 +66,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-up
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand All @@ -58,17 +79,38 @@ jobs:
matrix:
name: benchmark tier (amd64)
needs: [setup]
runs-on: [self-hosted, celeris-cluster]
# 72h ceiling: the full profile on one arch is ~19h single-pass, plus
# Orchestrate from msr1 specifically: the ansible control loop + result
# aggregation must NOT share a host with the SUT (bench_target=msa2-server)
# or the loadgen (msa2-client), or its CPU/IO steals from the measurement.
# msr1 (arm64) is unusable as a BENCH target under NIC load (firmware bug
# celeris#312) but is perfectly safe and otherwise-idle as the conductor —
# it just runs ansible over SSH. This keeps the amd64 SUT's numbers clean.
runs-on: [self-hosted, celeris-cluster, msr1]
# 72h ceiling: the full profile on one arch is ~30h single-pass (the
# grid grew with the mid-size payload rows + native h2c columns), plus
# Deploy + Cleanup. Well under GitHub's 5-day self-hosted job hard
# limit. This is only a ceiling — the headline profile finishes in
# hours and exits early. BENCH_BUDGET (below) is the real fail-loud
# projection gate.
# limit. This is only a ceiling — the headline profile (same grid,
# shorter window) projects ~21.6h. BENCH_BUDGET (below) is the real
# fail-loud projection gate.
timeout-minutes: 4320
env:
BENCH_PROFILE: ${{ github.event.inputs.profile || 'full' }}
BENCH_PROFILE: ${{ github.event.inputs.profile || 'fast' }}
# Column subset to BENCH for a targeted re-run; 'all' on schedule/default.
BENCH_COMPETITORS: ${{ github.event.inputs.competitors || 'all' }}
# Binary/module set to BUILD+DEPLOY. MUST be job-level (not just the
# Deploy step): BenchTier runs an internal auto-deploy when no manifest
# is present (always true right after the prior run's Cleanup), and that
# auto-deploy falls back to BENCH_COMPETITORS — which is column slugs
# (e.g. 'aspnet-h2') that Deploy rejects. Pinning the module names here
# (e.g. 'aspnet') makes both the explicit Deploy step AND the auto-deploy
# use them. h2 columns reuse their h1 sibling's binary, so this set
# differs from BENCH_COMPETITORS.
DEPLOY_COMPETITORS: ${{ github.event.inputs.deploy_competitors || 'all' }}
# 'false' => data-only (no docs push, no integrity gate). Maps to the
# BENCH_PUBLISH=0 the mage targets read; any other value publishes.
BENCH_PUBLISH: ${{ github.event.inputs.publish == 'false' && '0' || '1' }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-go@v6
with:
Expand Down Expand Up @@ -115,8 +157,8 @@ jobs:

- name: mage Deploy (competitors + db services for the profile)
env:
# Bench needs the competitor binaries staged.
DEPLOY_COMPETITORS: all
# DEPLOY_COMPETITORS is set at the job level (above) so the BenchTier
# auto-deploy inherits it too.
# driver-* cells in the profile need postgres/redis/memcached.
DEPLOY_NEEDS_DBSERVICES: "1"
run: mage Deploy
Expand All @@ -129,11 +171,12 @@ jobs:
# Restore "both" once the arm64 host is fixed (and #168 ArchParallel
# lands, or the budget/timeout below is re-checked for two arches).
BENCH_TARGET: msa2-server
# The full single-pass matrix runs ~19h on one arch — raise the
# fit budget above the 24h weekly default so BenchTier doesn't
# refuse a future larger registry (must stay under the job
# timeout-minutes above).
BENCH_BUDGET: "70h"
# fast/headline fit the 24h cluster slot, so the budget asserts <24h
# (FitWithin fails loudly if the grid ever grows past it). Only the
# rated "full" deep-dive needs the raised ceiling (it runs the rated
# sweep on the whole grid — many hours); both stay under the job
# timeout-minutes above.
BENCH_BUDGET: ${{ github.event.inputs.profile == 'full' && '70h' || '24h' }}
DOCS_TOKEN: ${{ secrets.DOCS_DISPATCH_TOKEN }}
run: mage BenchTier

Expand All @@ -156,7 +199,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-down
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
name: go vet + golangci-lint + gofmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
with:
go-version: "1.26.4"
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/matrix-nightly-tier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-up
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand All @@ -50,7 +50,7 @@ jobs:
runs-on: [self-hosted, celeris-cluster]
timeout-minutes: 180
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-go@v6
with:
Expand Down Expand Up @@ -157,7 +157,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-down
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/matrix-pr-tier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-up
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand All @@ -110,7 +110,7 @@ jobs:
runs-on: [self-hosted, celeris-cluster]
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-go@v6
with:
Expand Down Expand Up @@ -202,7 +202,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-down
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/matrix-weekend-tier.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-up
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand All @@ -51,7 +51,7 @@ jobs:
runs-on: [self-hosted, celeris-cluster]
timeout-minutes: 1740
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7

- uses: actions/setup-go@v6
with:
Expand Down Expand Up @@ -148,7 +148,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: ./.github/actions/cluster-runner-down
with:
tailscale-oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
name: root module
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
with:
go-version: "1.26.4"
Expand All @@ -45,7 +45,7 @@ jobs:
- iris
- stdhttp
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
- uses: actions/setup-go@v6
with:
go-version: "1.26.4"
Expand Down
22 changes: 22 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@
/servers/elysia/bun.lock
/servers/elysia/bun.lockb

# Express (Node.js) competitor adapter. node runs src/ directly (no
# bundle), so only the resolved-deps cache and npm's lockfile stay out
# of git — every cluster deploy does a fresh `npm install` against the
# upstream registry, enforcing the always-latest policy.
/servers/express/node_modules/
/servers/express/package-lock.json

# Python adapter local-dev artefacts. The cluster builds the venv under
# {{ bench_root }}/competitors/<name>/.venv via the python ansible role
# — both the in-repo .venv and the bytecode caches are dev-only debris.
Expand Down Expand Up @@ -83,3 +90,18 @@ servers/_docker/**/*.tar
/validation/refapp/driver_memcached/driver_memcached
/validation/refapp/observability/observability
/validation/refapp/static_swagger_proxy/static_swagger_proxy

# wave-6 native competitor adapters (actix/lithium/starlette/bunraw/httpzig/
# h2o/uws/fastify/express/vertx/netty + nbio). The cluster builds every
# artefact under bench_root; nothing here should be committed. Generic globs
# so a local validation build never dirties the tree.
/servers/*/target/
/servers/*/build/
/servers/*/node_modules/
/servers/*/dist/
/servers/*/zig-out/
/servers/*/.zig-cache/
/servers/**/__pycache__/
/servers/*/server
/servers/actix/target/
/servers/actix/Cargo.lock
36 changes: 36 additions & 0 deletions ansible/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,11 @@
| selectattr('key', 'in', competitor_list)
| selectattr('value.lang', 'equalto', 'cpp')
| list | length) > 0 }}
need_c: >-
{{ (competitor_sources | default({}) | dict2items
| selectattr('key', 'in', competitor_list)
| selectattr('value.lang', 'equalto', 'c')
| list | length) > 0 }}
need_dotnet: >-
{{ (competitor_sources | default({}) | dict2items
| selectattr('key', 'in', competitor_list)
Expand All @@ -362,6 +367,16 @@
| selectattr('key', 'in', competitor_list)
| selectattr('value.lang', 'equalto', 'python')
| list | length) > 0 }}
need_node: >-
{{ (competitor_sources | default({}) | dict2items
| selectattr('key', 'in', competitor_list)
| selectattr('value.lang', 'equalto', 'node')
| list | length) > 0 }}
need_java: >-
{{ (competitor_sources | default({}) | dict2items
| selectattr('key', 'in', competitor_list)
| selectattr('value.lang', 'equalto', 'java')
| list | length) > 0 }}

- name: Install rust toolchain (only when a rust competitor is in scope — incl. hyper)
ansible.builtin.include_role:
Expand All @@ -377,6 +392,13 @@
- inventory_hostname in groups['bench_targets']
- need_cpp | bool

- name: Install c toolchain + libreactor (only when a c competitor is in scope)
ansible.builtin.include_role:
name: c
when:
- inventory_hostname in groups['bench_targets']
- need_c | bool

- name: Install .NET SDK (only when a dotnet competitor is in scope)
ansible.builtin.include_role:
name: dotnet
Expand Down Expand Up @@ -405,6 +427,20 @@
- inventory_hostname in groups['bench_targets']
- need_python | bool

- name: Install node toolchain (only when a node competitor is in scope)
ansible.builtin.include_role:
name: node
when:
- inventory_hostname in groups['bench_targets']
- need_node | bool

- name: Install java toolchain (only when a java competitor is in scope)
ansible.builtin.include_role:
name: java
when:
- inventory_hostname in groups['bench_targets']
- need_java | bool

# ---------- native competitor builds ----------
# For each non-Go competitor, push the source tarball, extract under
# {{ bench_root }}/competitors-src/<slug>/, then run build_cmd inside
Expand Down
Loading