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
24 changes: 16 additions & 8 deletions .github/actions/detect-changed-impls/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,23 @@ runs:
git show "origin/$BASE_REF:$IMPLS_FILE" > /tmp/impls-old.yaml 2>/dev/null || echo "" > /tmp/impls-old.yaml
cp "$IMPLS_FILE" /tmp/impls-new.yaml

# Normalize objects before comparing so YAML comments/formatting
# don't make unchanged entries look modified.
normalize_object() {
local file="$1"
local query="$2"
yq eval -o=json -I=0 "$query" "$file" 2>/dev/null || echo ""
}

# Find changed/new implementations
OLD_IMPLS=$(yq eval '.implementations[].id' /tmp/impls-old.yaml 2>/dev/null | sort || echo "")
NEW_IMPLS=$(yq eval '.implementations[].id' /tmp/impls-new.yaml 2>/dev/null | sort || echo "")

for impl_id in $NEW_IMPLS; do
if echo "$OLD_IMPLS" | grep -q "^${impl_id}$"; then
# Exists in old, check if content changed
OLD_IMPL=$(yq eval ".implementations[] | select(.id == \"$impl_id\")" /tmp/impls-old.yaml 2>/dev/null || echo "")
NEW_IMPL=$(yq eval ".implementations[] | select(.id == \"$impl_id\")" /tmp/impls-new.yaml 2>/dev/null || echo "")
OLD_IMPL=$(normalize_object /tmp/impls-old.yaml ".implementations[] | select(.id == \"$impl_id\")")
NEW_IMPL=$(normalize_object /tmp/impls-new.yaml ".implementations[] | select(.id == \"$impl_id\")")

if [ "$OLD_IMPL" != "$NEW_IMPL" ]; then
CHANGED_IMPLS="${CHANGED_IMPLS:+$CHANGED_IMPLS|}$impl_id"
Expand All @@ -113,8 +121,8 @@ runs:

for relay_id in $NEW_RELAYS; do
if echo "$OLD_RELAYS" | grep -q "^${relay_id}$"; then
OLD_RELAY=$(yq eval ".relays[] | select(.id == \"$relay_id\")" /tmp/impls-old.yaml 2>/dev/null || echo "")
NEW_RELAY=$(yq eval ".relays[] | select(.id == \"$relay_id\")" /tmp/impls-new.yaml 2>/dev/null || echo "")
OLD_RELAY=$(normalize_object /tmp/impls-old.yaml ".relays[] | select(.id == \"$relay_id\")")
NEW_RELAY=$(normalize_object /tmp/impls-new.yaml ".relays[] | select(.id == \"$relay_id\")")
if [ "$OLD_RELAY" != "$NEW_RELAY" ]; then
CHANGED_RELAYS="${CHANGED_RELAYS:+$CHANGED_RELAYS|}$relay_id"
echo " → Changed relay: $relay_id"
Expand All @@ -131,8 +139,8 @@ runs:

for router_id in $NEW_ROUTERS; do
if echo "$OLD_ROUTERS" | grep -q "^${router_id}$"; then
OLD_ROUTER=$(yq eval ".routers[] | select(.id == \"$router_id\")" /tmp/impls-old.yaml 2>/dev/null || echo "")
NEW_ROUTER=$(yq eval ".routers[] | select(.id == \"$router_id\")" /tmp/impls-new.yaml 2>/dev/null || echo "")
OLD_ROUTER=$(normalize_object /tmp/impls-old.yaml ".routers[] | select(.id == \"$router_id\")")
NEW_ROUTER=$(normalize_object /tmp/impls-new.yaml ".routers[] | select(.id == \"$router_id\")")
if [ "$OLD_ROUTER" != "$NEW_ROUTER" ]; then
CHANGED_ROUTERS="${CHANGED_ROUTERS:+$CHANGED_ROUTERS|}$router_id"
echo " → Changed router: $router_id"
Expand All @@ -153,8 +161,8 @@ runs:
for baseline_id in $NEW_BASELINES; do
if echo "$OLD_BASELINES" | grep -q "^${baseline_id}$"; then
# Exists in old, check if content changed
OLD_BASELINE=$(yq eval ".baselines[] | select(.id == \"$baseline_id\")" /tmp/impls-old.yaml 2>/dev/null || echo "")
NEW_BASELINE=$(yq eval ".baselines[] | select(.id == \"$baseline_id\")" /tmp/impls-new.yaml 2>/dev/null || echo "")
OLD_BASELINE=$(normalize_object /tmp/impls-old.yaml ".baselines[] | select(.id == \"$baseline_id\")")
NEW_BASELINE=$(normalize_object /tmp/impls-new.yaml ".baselines[] | select(.id == \"$baseline_id\")")
if [ "$OLD_BASELINE" != "$NEW_BASELINE" ]; then
CHANGED_BASELINES="${CHANGED_BASELINES:+$CHANGED_BASELINES|}$baseline_id"
echo " → Changed baseline: $baseline_id"
Expand Down
15 changes: 15 additions & 0 deletions .github/actions/run-bash-perf-test/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ inputs:
description: "Enable debug mode (sets DEBUG=true in test containers)"
required: false
default: "false"
timeout:
description: "Max seconds per test before harness abort"
required: false
default: ""

runs:
using: "composite"
Expand Down Expand Up @@ -129,6 +133,16 @@ runs:
LATENCY_ITERATIONS_FLAG="--latency-iterations ${{ inputs.latency-iterations }}"
fi

TIMEOUT_FLAG=""
if [ -n "${{ inputs.timeout }}" ]; then
TIMEOUT_FLAG="--timeout ${{ inputs.timeout }}"
fi

if echo '${{ inputs.test-select }}' | grep -q 'python'; then
export PY_YAMUX_DISABLE_HYSTERESIS=1
export PY_YAMUX_ASSUME_RTT_MS=1
fi

./run.sh \
--impl-select '${{ inputs.test-select }}' \
--test-ignore '${{ inputs.test-ignore }}' \
Expand All @@ -140,6 +154,7 @@ runs:
$DOWNLOAD_BYTES_FLAG \
$DURATION_FLAG \
$LATENCY_ITERATIONS_FLAG \
$TIMEOUT_FLAG \
$SNAPSHOT_FLAG \
$EXPORT_DOCKER_FLAG \
$FORCE_MATRIX_FLAG \
Expand Down
23 changes: 21 additions & 2 deletions .github/workflows/perf-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ on:
required: false
default: false
type: boolean
timeout:
description: 'Max seconds per test before harness abort'
required: false
default: 300
type: number

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -78,6 +83,7 @@ jobs:
debug: ${{ steps.resolve.outputs.debug }}
snapshot: ${{ steps.resolve.outputs.snapshot }}
export-docker-images: ${{ steps.resolve.outputs.export-docker-images }}
timeout: ${{ steps.resolve.outputs.timeout }}
should-run-tests: ${{ steps.resolve.outputs.should-run-tests }}
steps:
- name: Checkout code
Expand Down Expand Up @@ -108,6 +114,7 @@ jobs:
echo "debug=${{ github.event.inputs.debug }}" >> $GITHUB_OUTPUT
echo "snapshot=${{ github.event.inputs.snapshot }}" >> $GITHUB_OUTPUT
echo "export-docker-images=${{ github.event.inputs.export-docker-images }}" >> $GITHUB_OUTPUT
echo "timeout=${{ github.event.inputs.timeout }}" >> $GITHUB_OUTPUT
echo "should-run-tests=true" >> $GITHUB_OUTPUT
echo "→ Manual trigger: using workflow inputs"
else
Expand All @@ -120,14 +127,25 @@ jobs:
echo "test-ignore=~failing" >> $GITHUB_OUTPUT

CHANGED_BASELINES="${{ steps.detect.outputs.changed-baselines }}"
CHANGED_IMPLS="${{ steps.detect.outputs.changed-impls }}"
if [ -z "$CHANGED_BASELINES" ]; then
echo "baseline-select=~default_baselines" >> $GITHUB_OUTPUT
else
echo "baseline-select=$CHANGED_BASELINES" >> $GITHUB_OUTPUT
fi

# Same rule for baselines: avoid negating selected values.
echo "baseline-ignore=~failing" >> $GITHUB_OUTPUT
# Skip baseline builds/tests on impl-only PRs to save runner disk/time.
if [ -z "$CHANGED_BASELINES" ] && [ -n "$CHANGED_IMPLS" ]; then
echo "baseline-ignore=~baselines" >> $GITHUB_OUTPUT
else
echo "baseline-ignore=~failing" >> $GITHUB_OUTPUT
fi

if echo "$CHANGED_IMPLS" | grep -q 'python'; then
echo "timeout=900" >> $GITHUB_OUTPUT
else
echo "timeout=300" >> $GITHUB_OUTPUT
fi

echo "iterations=10" >> $GITHUB_OUTPUT
echo "force-matrix-rebuild=${{ steps.detect.outputs.impls-yaml-changed }}" >> $GITHUB_OUTPUT
Expand Down Expand Up @@ -167,3 +185,4 @@ jobs:
force-matrix-rebuild: ${{ needs.resolve-parameters.outputs.force-matrix-rebuild }}
force-image-rebuild: ${{ needs.resolve-parameters.outputs.force-image-rebuild }}
debug: ${{ needs.resolve-parameters.outputs.debug }}
timeout: '${{ needs.resolve-parameters.outputs.timeout }}'
1 change: 1 addition & 0 deletions lib/lib-inputs-yaml.sh
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ EOF
DOWNLOAD_BYTES: "${DOWNLOAD_BYTES:-}"
DURATION: "${DURATION:-}"
LATENCY_ITERATIONS: "${LATENCY_ITERATIONS:-}"
PERF_TEST_TIMEOUT_SECS: "${PERF_TEST_TIMEOUT_SECS:-}"
EOF
;;
hole-punch)
Expand Down
16 changes: 11 additions & 5 deletions perf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ Control test behavior with these options:
# Number of iterations for upload/download tests (default: 10)
./run.sh --iterations 10

# Duration per iteration for throughput tests in seconds (default: 20)
./run.sh --duration 20
# Max seconds per test before the harness aborts (default: 300)
./run.sh --timeout 900

# Number of iterations for latency tests (default: 100)
./run.sh --latency-iterations 100
Expand All @@ -83,7 +83,11 @@ Control test behavior with these options:
./run.sh --cache-dir /srv/cache
```

**Note**: The `--iterations` flag controls both upload and download iterations. The framework measures throughput by transferring data over a time period and measuring bytes/second.
**Note**: The `--iterations` flag controls both upload and download iterations. Modern dialers measure throughput by transferring a fixed amount of data (`UPLOAD_BYTES` / `DOWNLOAD_BYTES`) per iteration and reporting Gbps.

**Note**: `--timeout` sets `PERF_TEST_TIMEOUT_SECS` (harness limit for the whole docker-compose test). This is separate from `TEST_TIMEOUT_SECS` inside dialer/listener containers (peer/redis wait limits).

**Legacy**: `--duration` (default 20s) is passed to dialers as `DURATION` but is unused by current implementations; prefer `--upload-bytes`, `--iterations`, and `--timeout`.

## Test Filtering

Expand Down Expand Up @@ -586,8 +590,10 @@ For now, all tests run locally using Docker networking. Multi-machine testing su

**Tests failing with timeout**
- Check container logs: `$TEST_PASS_DIR/logs/<test-name>.log`
- Increase test duration: `--duration 30`
- Enable debug mode: `--debug`
- Each perf test is capped at **300 seconds** by default (`--timeout`). For slow stacks: `./run.sh --timeout 900 --test-select "..." --yes`
- Do not use `--debug` for throughput runs (adds log I/O and often lowers Gbps)
- `--duration` does not extend the harness limit and is unused by modern dialers (they transfer fixed `UPLOAD_BYTES`/`DOWNLOAD_BYTES` per iteration, not “run for N seconds”)
- Python yamux tuning uses `PY_YAMUX_*` env vars (e.g. `PY_YAMUX_DISABLE_HYSTERESIS=1`); they are injected into **python-v0.x** containers only, not Go/Rust

**Cache not working / Matrix regenerates every time**
- Check TEST_RUN_KEY in output (should be consistent for same configuration)
Expand Down
26 changes: 24 additions & 2 deletions perf/images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ test-aliases:
- alias: "none"
value: "!~all"
- alias: "images"
value: "dotnet-v1.0|go-v0.45|js-v3.x|rust-v0.56|lua-v0.1.0|eth-p2p-z-v0.0.2|nim-v1.15"
value: "dotnet-v1.0|go-v0.45|js-v3.x|python-v0.x|rust-v0.56|lua-v0.1.0|eth-p2p-z-v0.0.2|nim-v1.15"
- alias: "python"
value: "python-v0.x"
- alias: "baselines"
value: "https|quic-go|iperf|quinn|tquic|tokio-quiche|tokio-tquic|quiche"
- alias: "default_baselines"
Expand All @@ -28,7 +30,7 @@ test-aliases:
value: "eth-p2p-z-v0.0.2"
- alias: "nim"
value: "nim-v1.15"
- alias: "failing" # As of 26 Feb 2026
- alias: "failing" # As of 7 Jul 2026
value: "go-v0.45 x js-v3.x (webtransport)|js-v3.x x go-v0.45 (webtransport)|js-v3.x x js-v3.x (webtransport)|js-v3.x x dotnet-v1.0 (tcp, tls, yamux)|rust-v0.56 x dotnet-v1.0 (tcp, tls, yamux)|dotnet-v1.0 x go-v0.45 (tcp, noise, yamux)|dotnet-v1.0 x go-v0.45 (tcp, tls, yamux)|dotnet-v1.0 x go-v0.45 (quic-v1)|dotnet-v1.0 x rust-v0.56 (tcp, noise, yamux)|dotnet-v1.0 x rust-v0.56 (tcp, tls, yamux)|dotnet-v1.0 x rust-v0.56 (quic-v1)|dotnet-v1.0 x js-v3.x (tcp, noise, yamux)|dotnet-v1.0 x js-v3.x (tcp, tls, yamux)|dotnet-v1.0 x dotnet-v1.0 (quic-v1)"

# Baseline implementations - Separate from main tests
Expand Down Expand Up @@ -156,6 +158,26 @@ implementations:
secureChannels: [noise, tls]
muxers: [yamux]

- id: python-v0.x
source:
type: github
repo: libp2p/py-libp2p
commit: 34d7bcda5e616d05af88f4a787d9193564ecc018
dockerfile: interop/perf/Dockerfile
transports: [tcp, ws]
secureChannels: [noise, tls]
muxers: [yamux, mplex]

# Local python config example (same Dockerfile; context = repo root = images/python/0.x/py-libp2p)
# - id: python-v0.x
# source:
# type: local
# path: images/python/0.x/py-libp2p
# dockerfile: interop/perf/Dockerfile
# transports: [tcp, ws]
# secureChannels: [noise, tls]
# muxers: [yamux, mplex]

- id: lua-v0.1.0
source:
type: github
Expand Down
Loading
Loading