From 93d1e0115d472d02870d5979ff479cee8857e735 Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Thu, 12 Nov 2026 01:53:56 +0000 Subject: [PATCH] Confine the remaining CI and perf downloads (#677) - kubernetes-validation.yml checks kind v0.23.0 and kubectl v1.29.9 against the projects' published SHA-256 sums and restricts the downloads to HTTPS. - The native corpus's dw.phar download is restricted to HTTPS, including redirects; its digest was already checked. - The PHP-only Octane perf experiments (FrankenPHP, Swoole, OpenSwoole) are removed with their Dockerfiles, whose PECL and extension downloads bypassed the checked CA stores, along with their overlays, probes, soak variants, the prebuilt FrankenPHP transfer and the Composer sink rows. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/kubernetes-validation.yml | 7 +- .github/workflows/server-perf-soak.yml | 2 +- docs/perf-runner.md | 9 +- .../server-parity/native-image-corpus.sh | 2 +- .../Dockerfile.octane-frankenphp-experiment | 47 ------- .../Dockerfile.octane-openswoole-experiment | 24 ---- .../perf/Dockerfile.octane-swoole-experiment | 24 ---- .../octane-concurrent-isolation.compose.yml | 17 --- .../octane-concurrent-request-isolation.py | 118 ------------------ .../octane-frankenphp-experiment.compose.yml | 13 -- .../octane-openswoole-experiment.compose.yml | 13 -- scripts/perf/octane-request-isolation.php | 112 ----------------- ...ctane-single-worker-experiment.compose.yml | 18 --- .../perf/octane-swoole-experiment.compose.yml | 13 -- .../poll-wait-octane-experiment.compose.yml | 14 --- scripts/perf/run-server-soak.sh | 50 ++------ scripts/perf/run-vultr-soak.sh | 67 +--------- .../soak-frankenphp-experiment.compose.yml | 13 -- .../perf/soak-octane-experiment.compose.yml | 17 --- scripts/perf/standard-workflow-profile.md | 73 +---------- tests/Unit/Support/composer-sinks.json | 78 ------------ tests/Unit/Support/server_soak_test.py | 7 +- tests/Unit/Support/vultr_soak_cleanup_test.py | 47 +++---- 23 files changed, 47 insertions(+), 738 deletions(-) delete mode 100644 scripts/perf/Dockerfile.octane-frankenphp-experiment delete mode 100644 scripts/perf/Dockerfile.octane-openswoole-experiment delete mode 100644 scripts/perf/Dockerfile.octane-swoole-experiment delete mode 100644 scripts/perf/octane-concurrent-isolation.compose.yml delete mode 100644 scripts/perf/octane-concurrent-request-isolation.py delete mode 100644 scripts/perf/octane-frankenphp-experiment.compose.yml delete mode 100644 scripts/perf/octane-openswoole-experiment.compose.yml delete mode 100644 scripts/perf/octane-request-isolation.php delete mode 100644 scripts/perf/octane-single-worker-experiment.compose.yml delete mode 100644 scripts/perf/octane-swoole-experiment.compose.yml delete mode 100644 scripts/perf/poll-wait-octane-experiment.compose.yml delete mode 100644 scripts/perf/soak-frankenphp-experiment.compose.yml delete mode 100644 scripts/perf/soak-octane-experiment.compose.yml diff --git a/.github/workflows/kubernetes-validation.yml b/.github/workflows/kubernetes-validation.yml index d9ae13c0b..d136d7778 100644 --- a/.github/workflows/kubernetes-validation.yml +++ b/.github/workflows/kubernetes-validation.yml @@ -43,10 +43,13 @@ jobs: install_dir="${RUNNER_TEMP}/bin" mkdir -p "${install_dir}" - curl -fsSL -o "${install_dir}/kind" https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 + # Pinned by the projects' published SHA-256 sums (server#677). + curl --proto =https --tlsv1.2 -fsSL -o "${install_dir}/kind" https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 + echo "1d86e3069ffbe3da9f1a918618aecbc778e00c75f838882d0dfa2d363bc4a68c ${install_dir}/kind" | sha256sum --check --strict chmod +x "${install_dir}/kind" - curl -fsSL -o "${install_dir}/kubectl" https://dl.k8s.io/release/v1.29.9/bin/linux/amd64/kubectl + curl --proto =https --tlsv1.2 -fsSL -o "${install_dir}/kubectl" https://dl.k8s.io/release/v1.29.9/bin/linux/amd64/kubectl + echo "7b0de2466458cc3c12cf8742dc800c77d4fa72e831aa522df65e510d33b329e2 ${install_dir}/kubectl" | sha256sum --check --strict chmod +x "${install_dir}/kubectl" echo "${install_dir}" >> "${GITHUB_PATH}" diff --git a/.github/workflows/server-perf-soak.yml b/.github/workflows/server-perf-soak.yml index bf0e8548f..da97bc7c3 100644 --- a/.github/workflows/server-perf-soak.yml +++ b/.github/workflows/server-perf-soak.yml @@ -31,7 +31,7 @@ on: description: "Experimental HTTP runtime built from the exact published image" required: false type: choice - options: [apache, nginx-fpm, apache-event-fpm, frankenphp, swoole, openswoole] + options: [apache, nginx-fpm, apache-event-fpm] default: apache fixed_envelope: description: "Apply Server #137 fixed resource limits to every compared service" diff --git a/docs/perf-runner.md b/docs/perf-runner.md index 34d5840fb..941145431 100644 --- a/docs/perf-runner.md +++ b/docs/perf-runner.md @@ -138,11 +138,10 @@ Artifacts land in `build/perf/` by default. The script removes the Compose proje For the Server #137 HTTP-runtime comparison, dispatch `Server Perf Soak` on `main` with an exact `published_server_image` digest, `fixed_envelope=true`, -and `http_variant=apache`, `swoole`, or `openswoole`. Each dispatch uses the -same published application, queue worker, scheduler, SDK fixture, workload, -host plan, and 1-CPU/1-GiB HTTP cap. The Swoole variants build local Octane -images from the selected published digest with 12 request workers and a -500-request recycle limit; the official image remains the Apache baseline. +and `http_variant=apache`, `nginx-fpm`, or `apache-event-fpm`. Each dispatch +uses the same published application, queue worker, scheduler, SDK fixture, +workload, host plan, and 1-CPU/1-GiB HTTP cap; the official image remains the +Apache baseline. The Octane variants were removed in server#677. The fixed overlay pins MySQL, Redis, Composer and PHP image digests, caps the other services, and sets identical worker/query poll admission limits across variants. `server-image.json` records the actual HTTP diff --git a/scripts/conformance/server-parity/native-image-corpus.sh b/scripts/conformance/server-parity/native-image-corpus.sh index 539eb36ef..c49ad7e02 100755 --- a/scripts/conformance/server-parity/native-image-corpus.sh +++ b/scripts/conformance/server-parity/native-image-corpus.sh @@ -91,7 +91,7 @@ docker run --label "dev.durable-workflow.qualification.scope=$RESOURCE_SCOPE" -d "$image" -c 'while :; do php artisan workflow:v2:repair-pass --json; php artisan schedule:evaluate --limit=100 --json; sleep 1; done' >/dev/null mkdir -p parity-consumers -curl --fail --location --retry 3 --silent --output parity-consumers/dw.phar https://github.com/durable-workflow/cli/releases/download/2.2.0/dw.phar +curl --proto =https --proto-redir =https --fail --location --retry 3 --silent --output parity-consumers/dw.phar https://github.com/durable-workflow/cli/releases/download/2.2.0/dw.phar printf '%s %s\n' "$(jq -r '.visibility.cli.phar_sha256' tests/Fixtures/ServerParity/visibility-current-runs.json)" parity-consumers/dw.phar \ | sha256sum -c > parity-evidence/cli-artifact.txt export DW_PARITY_CLI_PHAR=/source/parity-consumers/dw.phar diff --git a/scripts/perf/Dockerfile.octane-frankenphp-experiment b/scripts/perf/Dockerfile.octane-frankenphp-experiment deleted file mode 100644 index 22ff52967..000000000 --- a/scripts/perf/Dockerfile.octane-frankenphp-experiment +++ /dev/null @@ -1,47 +0,0 @@ -ARG SERVER_IMAGE=durableworkflow/server@sha256:998b9dffe1f8effb159af9660ac190ccbc9d0c954900e68e9946baa6bb4c6c42 - -FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS phpredis-source - -ARG PHPREDIS_VERSION=6.3.0 -ARG PHPREDIS_COMMIT=df4fab2de7fc327c54c94a13af2b9542e4fbd720 - -RUN git clone --depth 1 --branch "${PHPREDIS_VERSION}" https://github.com/phpredis/phpredis.git /phpredis \ - && test "$(git -C /phpredis rev-parse HEAD)" = "${PHPREDIS_COMMIT}" - -FROM ${SERVER_IMAGE} AS published - -FROM dunglas/frankenphp:php8.3@sha256:5918e1fce1e297eca32253a7dc95d5ab09ac156a127dc0eef354043d39ba97d3 - -RUN install-php-extensions pdo_mysql pdo_pgsql pcntl zip bcmath - -COPY --from=phpredis-source /phpredis /usr/src/php/ext/redis - -RUN apt-get update \ - && apt-get install -y --no-install-recommends $PHPIZE_DEPS \ - && docker-php-ext-install redis \ - && apt-get purge -y --auto-remove $PHPIZE_DEPS \ - && rm -rf /var/lib/apt/lists/* - -COPY --from=published /usr/bin/composer /usr/bin/composer -COPY --from=published /usr/local/bin/server-* /usr/local/bin/ -COPY --from=published /workflow/ /workflow/ -COPY --from=published /app/ /app/ - -WORKDIR /app - -# Composer trusts only the pinned Composer image's checked CA store here -# (GHSA-6q48-6c7w-9gw2); this base image's own store may hold a withdrawn root. -COPY --from=phpredis-source /etc/ssl/certs/ca-certificates.crt /etc/composer-trust/ca.pem -RUN install -d -m 0555 /etc/composer-trust/no-ca-directory -ENV COMPOSER_CAFILE=/etc/composer-trust/ca.pem COMPOSER_CAPATH=/etc/composer-trust/no-ca-directory -RUN composer require 'laravel/octane:2.20.0' 'laminas/laminas-diactoros:3.8.0' \ - 'symfony/psr-http-message-bridge:7.4.8' \ - --update-no-dev --no-interaction --no-progress --no-scripts \ - && php artisan package:discover --no-ansi \ - && php artisan octane:install --server=frankenphp --no-interaction - -ENV DW_WORKER_LONG_POLL_MAX_CONCURRENT=2 \ - DW_QUERY_TASK_POLL_MAX_CONCURRENT=1 - -ENTRYPOINT ["server-entrypoint"] -CMD ["php", "artisan", "octane:start", "--server=frankenphp", "--host=0.0.0.0", "--port=8080", "--workers=4"] diff --git a/scripts/perf/Dockerfile.octane-openswoole-experiment b/scripts/perf/Dockerfile.octane-openswoole-experiment deleted file mode 100644 index de56e02d2..000000000 --- a/scripts/perf/Dockerfile.octane-openswoole-experiment +++ /dev/null @@ -1,24 +0,0 @@ -ARG SERVER_IMAGE=durableworkflow/server@sha256:5d435d14fdbe3acc991ae591a155864f132cefbfbf1b53b724a8e7b006a91612 -FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS checked-ca - -FROM ${SERVER_IMAGE} - -RUN apt-get update \ - && apt-get install -y --no-install-recommends $PHPIZE_DEPS libbrotli-dev libssl-dev pkg-config \ - && pecl install openswoole-26.2.0 \ - && docker-php-ext-enable openswoole \ - && rm -rf /var/lib/apt/lists/* - -# Composer trusts only the pinned Composer image's checked CA store here -# (GHSA-6q48-6c7w-9gw2); this base image's own store may hold a withdrawn root. -COPY --from=checked-ca /etc/ssl/certs/ca-certificates.crt /etc/composer-trust/ca.pem -RUN install -d -m 0555 /etc/composer-trust/no-ca-directory -ENV COMPOSER_CAFILE=/etc/composer-trust/ca.pem COMPOSER_CAPATH=/etc/composer-trust/no-ca-directory -RUN composer require 'laravel/octane:2.20.0' 'laminas/laminas-diactoros:3.8.0' \ - 'symfony/psr-http-message-bridge:7.4.8' \ - --update-no-dev --no-interaction --no-progress --no-scripts \ - && php artisan package:discover --no-ansi \ - && php artisan octane:install --server=swoole --no-interaction - -ENTRYPOINT ["server-entrypoint"] -CMD ["php", "artisan", "octane:start", "--server=swoole", "--host=0.0.0.0", "--port=8080", "--workers=4"] diff --git a/scripts/perf/Dockerfile.octane-swoole-experiment b/scripts/perf/Dockerfile.octane-swoole-experiment deleted file mode 100644 index 77e861720..000000000 --- a/scripts/perf/Dockerfile.octane-swoole-experiment +++ /dev/null @@ -1,24 +0,0 @@ -ARG SERVER_IMAGE=durableworkflow/server@sha256:4ee8a95df43fbd7022b4d1b583046063b9d3e6e2d1fd4408e58a6b8f486cff56 -FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS checked-ca - -FROM ${SERVER_IMAGE} - -RUN apt-get update \ - && apt-get install -y --no-install-recommends $PHPIZE_DEPS libbrotli-dev libssl-dev pkg-config \ - && pecl install swoole-6.1.10 \ - && docker-php-ext-enable swoole \ - && rm -rf /var/lib/apt/lists/* - -# Composer trusts only the pinned Composer image's checked CA store here -# (GHSA-6q48-6c7w-9gw2); this base image's own store may hold a withdrawn root. -COPY --from=checked-ca /etc/ssl/certs/ca-certificates.crt /etc/composer-trust/ca.pem -RUN install -d -m 0555 /etc/composer-trust/no-ca-directory -ENV COMPOSER_CAFILE=/etc/composer-trust/ca.pem COMPOSER_CAPATH=/etc/composer-trust/no-ca-directory -RUN composer require 'laravel/octane:2.20.0' 'laminas/laminas-diactoros:3.8.0' \ - 'symfony/psr-http-message-bridge:7.4.8' \ - --update-no-dev --no-interaction --no-progress --no-scripts \ - && php artisan package:discover --no-ansi \ - && php artisan octane:install --server=swoole --no-interaction - -ENTRYPOINT ["server-entrypoint"] -CMD ["php", "artisan", "octane:start", "--server=swoole", "--host=0.0.0.0", "--port=8080", "--workers=4"] diff --git a/scripts/perf/octane-concurrent-isolation.compose.yml b/scripts/perf/octane-concurrent-isolation.compose.yml deleted file mode 100644 index 7222dfde6..000000000 --- a/scripts/perf/octane-concurrent-isolation.compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Disposable Server #137 multi-worker isolation fixture. -services: - server: - environment: - DW_ADMIN_TOKEN: probe-admin-token - DW_OPERATOR_TOKEN: probe-operator-token - DW_WORKER_TOKEN: probe-worker-token - DW_AUTH_BACKWARD_COMPATIBLE: 'false' - command: - - php - - artisan - - octane:start - - --server=swoole - - --host=0.0.0.0 - - --port=8080 - - --workers=4 - - --max-requests=50 diff --git a/scripts/perf/octane-concurrent-request-isolation.py b/scripts/perf/octane-concurrent-request-isolation.py deleted file mode 100644 index 0ff7dc8f2..000000000 --- a/scripts/perf/octane-concurrent-request-isolation.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Bounded concurrent auth and namespace isolation probe for disposable #137 stacks.""" - -import concurrent.futures -import json -import os -import random -import secrets -import time -import urllib.error -import urllib.request - - -BASE = os.environ.get("DW_PROBE_URL", "").rstrip("/") -ADMIN = os.environ.get("DW_PROBE_TOKEN", "") -WORKER = os.environ.get("DW_PROBE_WORKER_TOKEN", "") -if not BASE or not ADMIN or not WORKER: - raise SystemExit("DW_PROBE_URL, DW_PROBE_TOKEN, and DW_PROBE_WORKER_TOKEN are required") - - -def request(token, method, path, namespace=None, body=None): - headers = { - "Accept": "application/json", - "Content-Type": "application/json", - "Authorization": f"Bearer {token}", - "X-Durable-Workflow-Control-Plane-Version": "2", - } - if namespace is not None: - headers["X-Namespace"] = namespace - payload = json.dumps(body).encode() if body is not None else None - req = urllib.request.Request(BASE + path, data=payload, headers=headers, method=method) - try: - with urllib.request.urlopen(req, timeout=15) as response: - status, raw = response.status, response.read() - except urllib.error.HTTPError as error: - status, raw = error.code, error.read() - try: - decoded = json.loads(raw) - except json.JSONDecodeError as error: - raise RuntimeError(f"{method} {path}: invalid JSON at HTTP {status}") from error - if not isinstance(decoded, dict): - raise RuntimeError(f"{method} {path}: response is not a JSON object") - return status, decoded - - -suffix = secrets.token_hex(5) -alpha, beta = "probe-alpha-" + suffix, "probe-beta-" + suffix -alpha_id, beta_id = "probe-alpha-workflow-" + suffix, "probe-beta-workflow-" + suffix -for namespace in (alpha, beta): - status, _ = request(ADMIN, "POST", "/api/namespaces", body={"name": namespace}) - if status != 201: - raise RuntimeError(f"create {namespace}: HTTP {status}") -for namespace, workflow_id in ((alpha, alpha_id), (beta, beta_id)): - status, response = request( - ADMIN, - "POST", - "/api/workflows", - namespace, - { - "workflow_id": workflow_id, - "workflow_type": "probe.concurrent-isolation", - "task_queue": "probe-isolation", - "input": [], - }, - ) - if status != 201 or response.get("namespace") != namespace: - raise RuntimeError(f"start {namespace}: HTTP {status}, wrong namespace") - - -def check(case): - label, token, namespace, workflow_id, expected_status, expected_namespace = case - started = time.monotonic() - status, response = request(token, "GET", "/api/workflows/" + workflow_id, namespace) - if status != expected_status: - raise RuntimeError(f"{label}: expected HTTP {expected_status}, received {status}") - if expected_namespace is not None and response.get("namespace") != expected_namespace: - raise RuntimeError(f"{label}: response escaped requested namespace") - return time.monotonic() - started - - -cases = [ - ("alpha own", ADMIN, alpha, alpha_id, 200, alpha), - ("beta own", ADMIN, beta, beta_id, 200, beta), - ("alpha cross", ADMIN, beta, alpha_id, 404, None), - ("beta cross", ADMIN, alpha, beta_id, 404, None), - ("alpha invalid", "invalid-probe-token", alpha, alpha_id, 401, None), - ("beta invalid", "invalid-probe-token", beta, beta_id, 401, None), - ("alpha worker", WORKER, alpha, alpha_id, 403, None), - ("beta worker", WORKER, beta, beta_id, 403, None), -] * 2 - -durations = [] -with concurrent.futures.ThreadPoolExecutor(max_workers=16) as pool: - for round_number in range(40): - shuffled = list(cases) - random.shuffle(shuffled) - try: - durations.extend(pool.map(check, shuffled)) - except Exception as error: - raise RuntimeError(f"concurrent round {round_number + 1}: {error}") from error - -for label, namespace, workflow_id in (("alpha final", alpha, alpha_id), ("beta final", beta, beta_id)): - status, response = request(ADMIN, "GET", "/api/workflows/" + workflow_id, namespace) - if status != 200 or response.get("namespace") != namespace: - raise RuntimeError(f"{label}: isolation lost after concurrent rounds") - -durations.sort() -print(json.dumps({ - "probe": "concurrent-request-isolation", - "namespaces": 2, - "created_workflows": 2, - "server_workers": 4, - "max_requests_per_worker": 50, - "rounds": 40, - "max_concurrent_clients": 16, - "checked_requests": len(durations), - "p95_response_seconds": round(durations[int(len(durations) * 0.95)], 3), - "result": "pass", -}, sort_keys=True)) diff --git a/scripts/perf/octane-frankenphp-experiment.compose.yml b/scripts/perf/octane-frankenphp-experiment.compose.yml deleted file mode 100644 index 6866d4608..000000000 --- a/scripts/perf/octane-frankenphp-experiment.compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - server: - image: ${COMPOSE_PROJECT_NAME:-dw}-octane-frankenphp:local - build: - context: ../.. - dockerfile: scripts/perf/Dockerfile.octane-frankenphp-experiment - args: - SERVER_IMAGE: ${DW_PROFILE_SERVER_IMAGE:?Set an exact published Server image digest} - healthcheck: - test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8080/api/ready"] - interval: 10s - timeout: 5s - retries: 6 diff --git a/scripts/perf/octane-openswoole-experiment.compose.yml b/scripts/perf/octane-openswoole-experiment.compose.yml deleted file mode 100644 index 3f3b0e227..000000000 --- a/scripts/perf/octane-openswoole-experiment.compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - server: - image: ${COMPOSE_PROJECT_NAME:-dw}-octane-openswoole:local - build: - context: ../.. - dockerfile: scripts/perf/Dockerfile.octane-openswoole-experiment - args: - SERVER_IMAGE: ${DW_PROFILE_SERVER_IMAGE:?Set an exact published Server image digest} - healthcheck: - test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8080/api/ready"] - interval: 10s - timeout: 5s - retries: 6 diff --git a/scripts/perf/octane-request-isolation.php b/scripts/perf/octane-request-isolation.php deleted file mode 100644 index 40bb93d90..000000000 --- a/scripts/perf/octane-request-isolation.php +++ /dev/null @@ -1,112 +0,0 @@ -} */ -function request(string $baseUrl, string $token, string $method, string $path, ?string $namespace = null, ?array $body = null): array -{ - $headers = [ - 'Accept: application/json', - 'Content-Type: application/json', - 'Authorization: Bearer '.$token, - 'X-Durable-Workflow-Control-Plane-Version: 2', - ]; - - if ($namespace !== null) { - $headers[] = 'X-Namespace: '.$namespace; - } - - $options = [ - 'method' => $method, - 'header' => implode("\r\n", $headers), - 'ignore_errors' => true, - 'timeout' => 10, - ]; - - if ($body !== null) { - $options['content'] = json_encode($body, JSON_THROW_ON_ERROR); - } - - $context = stream_context_create(['http' => $options]); - $response = @file_get_contents($baseUrl.$path, false, $context); - $responseHeaders = $http_response_header ?? []; - - if ($response === false || ! preg_match('/^HTTP\/\S+\s+(\d{3})/', $responseHeaders[0] ?? '', $match)) { - throw new RuntimeException('HTTP request failed: '.$method.' '.$path); - } - - $decoded = json_decode($response, true, 512, JSON_THROW_ON_ERROR); - - if (! is_array($decoded)) { - throw new RuntimeException('Expected a JSON object: '.$method.' '.$path); - } - - return ['status' => (int) $match[1], 'body' => $decoded]; -} - -function expectStatus(array $response, int $status, string $case): void -{ - if ($response['status'] !== $status) { - throw new RuntimeException($case.': expected HTTP '.$status.', received '.$response['status']); - } -} - -function expectNamespace(array $response, string $namespace, string $case): void -{ - expectStatus($response, 200, $case); - - if (($response['body']['namespace'] ?? null) !== $namespace) { - throw new RuntimeException($case.': response escaped the requested namespace'); - } -} - -$suffix = bin2hex(random_bytes(5)); -$alpha = 'probe-alpha-'.$suffix; -$beta = 'probe-beta-'.$suffix; -$alphaId = 'probe-alpha-workflow-'.$suffix; -$betaId = 'probe-beta-workflow-'.$suffix; - -foreach ([$alpha, $beta] as $namespace) { - expectStatus(request($baseUrl, $token, 'POST', '/api/namespaces', body: ['name' => $namespace]), 201, 'create '.$namespace); -} - -foreach ([[$alpha, $alphaId], [$beta, $betaId]] as [$namespace, $workflowId]) { - $started = request($baseUrl, $token, 'POST', '/api/workflows', $namespace, [ - 'workflow_id' => $workflowId, - 'workflow_type' => 'probe.request-isolation', - 'task_queue' => 'probe-isolation', - 'input' => [], - ]); - expectStatus($started, 201, 'start '.$namespace); - - if (($started['body']['namespace'] ?? null) !== $namespace) { - throw new RuntimeException('Workflow start escaped the requested namespace'); - } -} - -for ($round = 0; $round < 30; $round++) { - expectNamespace(request($baseUrl, $token, 'GET', '/api/workflows/'.$alphaId, $alpha), $alpha, 'alpha workflow'); - expectNamespace(request($baseUrl, $token, 'GET', '/api/workflows/'.$betaId, $beta), $beta, 'beta workflow'); - expectStatus(request($baseUrl, $token, 'GET', '/api/workflows/'.$alphaId, $beta), 404, 'beta cannot see alpha workflow'); - expectStatus(request($baseUrl, 'invalid-probe-token', 'GET', '/api/workflows/'.$alphaId, $alpha), 401, 'invalid token after success'); - expectStatus(request($baseUrl, $workerToken, 'GET', '/api/workflows/'.$alphaId, $alpha), 403, 'worker role after admin success'); - expectStatus(request($baseUrl, $token, 'GET', '/api/workflows/'.$betaId, $alpha), 404, 'alpha cannot see beta workflow'); - expectNamespace(request($baseUrl, $token, 'GET', '/api/workflows/'.$alphaId, $alpha), $alpha, 'valid token after rejection'); -} - -echo json_encode([ - 'probe' => 'persistent-request-isolation', - 'namespaces' => 2, - 'created_workflows' => 2, - 'rounds' => 30, - 'checked_requests' => 210, - 'result' => 'pass', -], JSON_THROW_ON_ERROR).PHP_EOL; diff --git a/scripts/perf/octane-single-worker-experiment.compose.yml b/scripts/perf/octane-single-worker-experiment.compose.yml deleted file mode 100644 index 1af2865fa..000000000 --- a/scripts/perf/octane-single-worker-experiment.compose.yml +++ /dev/null @@ -1,18 +0,0 @@ -# Force consecutive HTTP requests through one persistent PHP worker and recycle -# it frequently while checking request-scoped namespace and authentication state. -services: - server: - environment: - DW_ADMIN_TOKEN: probe-admin-token - DW_OPERATOR_TOKEN: probe-operator-token - DW_WORKER_TOKEN: probe-worker-token - DW_AUTH_BACKWARD_COMPATIBLE: 'false' - command: - - php - - artisan - - octane:start - - --server=${DW_PROFILE_OCTANE_SERVER:-frankenphp} - - --host=0.0.0.0 - - --port=8080 - - --workers=1 - - --max-requests=20 diff --git a/scripts/perf/octane-swoole-experiment.compose.yml b/scripts/perf/octane-swoole-experiment.compose.yml deleted file mode 100644 index e7f0822fd..000000000 --- a/scripts/perf/octane-swoole-experiment.compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - server: - image: ${COMPOSE_PROJECT_NAME:-dw}-octane-swoole:local - build: - context: ../.. - dockerfile: scripts/perf/Dockerfile.octane-swoole-experiment - args: - SERVER_IMAGE: ${DW_PROFILE_SERVER_IMAGE:?Set an exact published Server image digest} - healthcheck: - test: ["CMD", "curl", "--fail", "--silent", "http://127.0.0.1:8080/api/ready"] - interval: 10s - timeout: 5s - retries: 6 diff --git a/scripts/perf/poll-wait-octane-experiment.compose.yml b/scripts/perf/poll-wait-octane-experiment.compose.yml deleted file mode 100644 index 3d89ac70d..000000000 --- a/scripts/perf/poll-wait-octane-experiment.compose.yml +++ /dev/null @@ -1,14 +0,0 @@ -# Append only for disposable Octane long-poll experiments. -services: - server: - command: - - php - - artisan - - octane:start - - --server=${DW_PROFILE_OCTANE_SERVER:-frankenphp} - - --host=0.0.0.0 - - --port=8080 - - --workers=${DW_PROFILE_OCTANE_WORKERS:-12} - environment: - DW_WORKER_LONG_POLL_MAX_CONCURRENT: ${DW_PROFILE_POLL_WAIT_WORKER_LIMIT:?Set an experimental worker-poll limit} - DW_QUERY_TASK_POLL_MAX_CONCURRENT: ${DW_PROFILE_POLL_WAIT_QUERY_LIMIT:?Set an experimental query-poll limit} diff --git a/scripts/perf/run-server-soak.sh b/scripts/perf/run-server-soak.sh index 9a99b4080..11b2e8693 100755 --- a/scripts/perf/run-server-soak.sh +++ b/scripts/perf/run-server-soak.sh @@ -36,12 +36,12 @@ READINESS_DIAGNOSTICS="${DW_PERF_READINESS_DIAGNOSTICS:-0}" MYSQL_LOCK_DIAGNOSTICS="${DW_PERF_MYSQL_LOCK_DIAGNOSTICS:-0}" PUBLISHED_SERVER_IMAGE="${DW_PERF_PUBLISHED_SERVER_IMAGE:-}" HTTP_VARIANT="${DW_PERF_HTTP_VARIANT:-apache}" -PREBUILT_HTTP_IMAGE_ID="${DW_PERF_PREBUILT_HTTP_IMAGE_ID:-}" FIXED_ENVELOPE="${DW_PERF_FIXED_ENVELOPE:-0}" IMAGE_DISTRIBUTION_METRICS="${DW_PERF_IMAGE_DISTRIBUTION_METRICS:-0}" -if [[ "$HTTP_VARIANT" != apache && "$HTTP_VARIANT" != nginx-fpm && "$HTTP_VARIANT" != apache-event-fpm \ - && "$HTTP_VARIANT" != frankenphp && "$HTTP_VARIANT" != swoole && "$HTTP_VARIANT" != openswoole ]]; then - echo "DW_PERF_HTTP_VARIANT must be apache, nginx-fpm, apache-event-fpm, frankenphp, swoole, or openswoole." >&2 +# The Octane variants were removed with their Dockerfiles, whose PECL and +# extension downloads bypassed the checked CA stores (server#677). +if [[ "$HTTP_VARIANT" != apache && "$HTTP_VARIANT" != nginx-fpm && "$HTTP_VARIANT" != apache-event-fpm ]]; then + echo "DW_PERF_HTTP_VARIANT must be apache, nginx-fpm, or apache-event-fpm." >&2 exit 2 fi if [[ "$FIXED_ENVELOPE" != 0 && "$FIXED_ENVELOPE" != 1 ]]; then @@ -64,11 +64,6 @@ if [[ "$FIXED_ENVELOPE" == 1 && -z "$PUBLISHED_SERVER_IMAGE" ]]; then echo "The fixed comparison envelope requires an exact published Server image." >&2 exit 2 fi -if [[ -n "$PREBUILT_HTTP_IMAGE_ID" \ - && ( "$HTTP_VARIANT" != frankenphp || ! "$PREBUILT_HTTP_IMAGE_ID" =~ ^sha256:[0-9a-f]{64}$ ) ]]; then - echo "A prebuilt HTTP image ID requires FrankenPHP and an exact sha256 image ID." >&2 - exit 2 -fi if [[ "$READINESS_DIAGNOSTICS" != 0 && "$READINESS_DIAGNOSTICS" != 1 ]]; then echo "DW_PERF_READINESS_DIAGNOSTICS must be 0 or 1." >&2 exit 2 @@ -273,15 +268,7 @@ fi if [[ "$FIXED_ENVELOPE" == 1 ]]; then compose_files+=("$ROOT_DIR/scripts/perf/soak-fixed-envelope.compose.yml") fi -if [[ "$HTTP_VARIANT" == swoole || "$HTTP_VARIANT" == openswoole ]]; then - export DW_PROFILE_SERVER_IMAGE="$PUBLISHED_SERVER_IMAGE" - compose_files+=("$ROOT_DIR/scripts/perf/octane-${HTTP_VARIANT}-experiment.compose.yml") - compose_files+=("$ROOT_DIR/scripts/perf/soak-octane-experiment.compose.yml") -elif [[ "$HTTP_VARIANT" == frankenphp ]]; then - export DW_PROFILE_SERVER_IMAGE="$PUBLISHED_SERVER_IMAGE" - compose_files+=("$ROOT_DIR/scripts/perf/octane-frankenphp-experiment.compose.yml") - compose_files+=("$ROOT_DIR/scripts/perf/soak-frankenphp-experiment.compose.yml") -elif [[ "$HTTP_VARIANT" == nginx-fpm || "$HTTP_VARIANT" == apache-event-fpm ]]; then +if [[ "$HTTP_VARIANT" == nginx-fpm || "$HTTP_VARIANT" == apache-event-fpm ]]; then export DW_PROFILE_SERVER_IMAGE="$PUBLISHED_SERVER_IMAGE" compose_files+=("$ROOT_DIR/scripts/perf/fpm-experiment.compose.yml") if [[ "$HTTP_VARIANT" == apache-event-fpm ]]; then @@ -461,16 +448,7 @@ if [[ "$IMAGE_DISTRIBUTION_METRICS" == 1 ]]; then "${compose[@]}" pull mysql redis stack_started_ns="$(date +%s%N)" fi -if [[ -n "$PREBUILT_HTTP_IMAGE_ID" ]]; then - loaded_http_image_id="$(docker image inspect "${PROJECT}-octane-frankenphp:local" --format '{{.Id}}')" || loaded_http_image_id="" - if [[ "$loaded_http_image_id" != "$PREBUILT_HTTP_IMAGE_ID" ]]; then - write_environment_setup_failure 1 "prebuilt_http_image_identity" "transferred FrankenPHP image ID differs from the prebuilt image" - exit 1 - fi - "${compose[@]}" up -d --no-build --wait || setup_status=$? -else - "${compose[@]}" up -d --build --wait || setup_status=$? -fi +"${compose[@]}" up -d --build --wait || setup_status=$? if [[ "$IMAGE_DISTRIBUTION_METRICS" == 1 ]]; then stack_completed_ns="$(date +%s%N)" jq --argjson stack_setup_elapsed_ms "$(((stack_completed_ns - stack_started_ns) / 1000000))" \ @@ -519,23 +497,14 @@ if [[ -n "$PUBLISHED_SERVER_IMAGE" ]]; then if [[ "$service" == server && "$HTTP_VARIANT" != apache ]]; then if [[ "$HTTP_VARIANT" == nginx-fpm ]]; then expected_server_image="$(docker image inspect 'nginx@sha256:a8b39bd9cf0f83869a2162827a0caf6137ddf759d50a171451b335cecc87d236' --format '{{.Id}}')" - elif [[ "$HTTP_VARIANT" == apache-event-fpm ]]; then - expected_server_image="$(docker image inspect "${PROJECT}-apache-event-fpm:local" --format '{{.Id}}')" else - expected_server_image="$(docker image inspect "${PROJECT}-octane-${HTTP_VARIANT}:local" --format '{{.Id}}')" + expected_server_image="$(docker image inspect "${PROJECT}-apache-event-fpm:local" --format '{{.Id}}')" fi if [[ "$actual_image_id" != "$expected_server_image" ]]; then echo "Experimental HTTP server does not match the selected derived/proxy image." >&2 write_environment_setup_failure 1 "experimental_image_identity" "experimental HTTP image differs from selected derived/proxy image" exit 1 fi - if [[ "$HTTP_VARIANT" == swoole || "$HTTP_VARIANT" == openswoole ]]; then - expected_revision="$(docker inspect "${PROJECT}-${service}-1" --format '{{ index .Config.Labels "org.opencontainers.image.revision" }}')" - if [[ "$expected_revision" != "$server_source_sha" ]]; then - write_environment_setup_failure 1 "experimental_image_revision" "experimental HTTP image lacks selected published source revision" - exit 1 - fi - fi continue fi if [[ "$actual_image_id" != "$published_image_id" ]]; then @@ -555,11 +524,6 @@ if [[ "$HTTP_VARIANT" == nginx-fpm || "$HTTP_VARIANT" == apache-event-fpm ]]; th jq -n --arg base_image "$PUBLISHED_SERVER_IMAGE" --arg image_id "$actual_fpm_image" \ --arg php_version "$(docker exec "${PROJECT}-fpm-1" php -r 'echo PHP_VERSION;')" \ '{base_image:$base_image,image_id:$image_id,php_version:$php_version}' > "$ARTIFACT_DIR/fpm-image.json" -elif [[ "$HTTP_VARIANT" == frankenphp ]]; then - jq -n --arg base_image "$PUBLISHED_SERVER_IMAGE" \ - --arg image_id "$(docker inspect "${PROJECT}-server-1" --format '{{.Image}}')" \ - --arg php_version "$(docker exec "${PROJECT}-server-1" php -r 'echo PHP_VERSION;')" \ - '{base_image:$base_image,image_id:$image_id,php_version:$php_version}' > "$ARTIFACT_DIR/http-derived-image.json" fi http_image_id="$(docker inspect "${PROJECT}-server-1" --format '{{.Image}}')" jq --arg variant "$HTTP_VARIANT" --arg fixed_envelope "$FIXED_ENVELOPE" \ diff --git a/scripts/perf/run-vultr-soak.sh b/scripts/perf/run-vultr-soak.sh index 3d458336c..635a46ca4 100755 --- a/scripts/perf/run-vultr-soak.sh +++ b/scripts/perf/run-vultr-soak.sh @@ -50,9 +50,10 @@ if [[ -n "$PUBLISHED_SERVER_IMAGE" \ echo "DW_PERF_PUBLISHED_SERVER_IMAGE must be an exact durableworkflow/server sha256 digest." >&2 exit 2 fi -if [[ "$HTTP_VARIANT" != apache && "$HTTP_VARIANT" != nginx-fpm && "$HTTP_VARIANT" != apache-event-fpm \ - && "$HTTP_VARIANT" != frankenphp && "$HTTP_VARIANT" != swoole && "$HTTP_VARIANT" != openswoole ]]; then - echo "DW_PERF_HTTP_VARIANT must be apache, nginx-fpm, apache-event-fpm, frankenphp, swoole, or openswoole." >&2 +# The Octane variants were removed with their Dockerfiles, whose PECL and +# extension downloads bypassed the checked CA stores (server#677). +if [[ "$HTTP_VARIANT" != apache && "$HTTP_VARIANT" != nginx-fpm && "$HTTP_VARIANT" != apache-event-fpm ]]; then + echo "DW_PERF_HTTP_VARIANT must be apache, nginx-fpm, or apache-event-fpm." >&2 exit 2 fi if [[ "$FIXED_ENVELOPE" != 0 && "$FIXED_ENVELOPE" != 1 ]]; then @@ -147,35 +148,11 @@ cleanup() { status=1 fi - if [[ -n "${PREBUILT_HTTP_IMAGE_TAG:-}" ]]; then - docker image rm "$PREBUILT_HTTP_IMAGE_TAG" >/dev/null 2>&1 || true - fi rm -rf "$WORK_DIR" exit "$status" } trap cleanup EXIT INT TERM -PREBUILT_HTTP_IMAGE_ID="" -PREBUILT_HTTP_IMAGE_TAG="" -PREBUILT_HTTP_ARCHIVE_SHA="" -if [[ "$HTTP_VARIANT" == frankenphp ]]; then - if [[ -z "${DW_PERF_COMPOSE_PROJECT:-}" ]]; then - echo "FrankenPHP prebuild requires DW_PERF_COMPOSE_PROJECT." >&2 - exit 2 - fi - PREBUILT_HTTP_IMAGE_TAG="${DW_PERF_COMPOSE_PROJECT}-octane-frankenphp:local" - log "Building FrankenPHP image before creating a disposable host" - docker build \ - --file scripts/perf/Dockerfile.octane-frankenphp-experiment \ - --build-arg "SERVER_IMAGE=$PUBLISHED_SERVER_IMAGE" \ - --tag "$PREBUILT_HTTP_IMAGE_TAG" . - PREBUILT_HTTP_IMAGE_ID="$(docker image inspect "$PREBUILT_HTTP_IMAGE_TAG" --format '{{.Id}}')" - docker save --output "$WORK_DIR/http-image.tar" "$PREBUILT_HTTP_IMAGE_TAG" - PREBUILT_HTTP_ARCHIVE_SHA="$(sha256sum "$WORK_DIR/http-image.tar" | awk '{print $1}')" - log "Prepared FrankenPHP image $PREBUILT_HTTP_IMAGE_ID archive sha256:$PREBUILT_HTTP_ARCHIVE_SHA for transfer" -fi -export DW_PERF_PREBUILT_HTTP_IMAGE_ID="$PREBUILT_HTTP_IMAGE_ID" - controller_ip="$(curl --fail --silent --show-error https://api.ipify.org)" if [[ ! "$controller_ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then echo "Unable to determine the GitHub runner IPv4 address." >&2 @@ -322,41 +299,6 @@ test "$(git rev-parse HEAD)" = "$sha" test -z "$(git status --porcelain --untracked-files=no)" REMOTE -if [[ -n "$PREBUILT_HTTP_IMAGE_ID" ]]; then - log "Transferring prebuilt FrankenPHP image archive" - scp "${ssh_options[@]}" "$WORK_DIR/http-image.tar" \ - "$SSH_USER@$INSTANCE_IP:http-image.tar" >/dev/null - remote_archive_sha="$(ssh "${ssh_options[@]}" "$SSH_USER@$INSTANCE_IP" \ - 'sha256sum "$HOME/http-image.tar"' | awk 'END {print $1}')" - if [[ "$remote_archive_sha" != "$PREBUILT_HTTP_ARCHIVE_SHA" ]]; then - printf 'Transferred FrankenPHP archive checksum mismatch: expected %s, got %s.\n' \ - "$PREBUILT_HTTP_ARCHIVE_SHA" "$remote_archive_sha" >&2 - exit 1 - fi - remote_http_image_id="$(ssh "${ssh_options[@]}" "$SSH_USER@$INSTANCE_IP" \ - bash -s -- "$PREBUILT_HTTP_IMAGE_TAG" <<'REMOTE' -set -euo pipefail -tag="$1" -trap 'rm -f "$HOME/http-image.tar"' EXIT -docker load --input "$HOME/http-image.tar" >/dev/null -docker image inspect "$tag" --format '{{.Id}}' -REMOTE -)" - if [[ ! "$remote_http_image_id" =~ ^sha256:[0-9a-f]{64}$ ]]; then - printf 'Unable to inspect the transferred FrankenPHP image tag: %q\n' \ - "$remote_http_image_id" >&2 - exit 1 - fi - log "Verified transferred archive sha256:$remote_archive_sha; loaded image ID $remote_http_image_id (builder ID $PREBUILT_HTTP_IMAGE_ID)" - jq -n --arg archive_sha256 "$remote_archive_sha" \ - --arg builder_image_id "$PREBUILT_HTTP_IMAGE_ID" \ - --arg loaded_image_id "$remote_http_image_id" \ - --arg image_tag "$PREBUILT_HTTP_IMAGE_TAG" \ - '{archive_sha256:$archive_sha256,builder_image_id:$builder_image_id,loaded_image_id:$loaded_image_id,image_tag:$image_tag}' \ - > "$(dirname "$PROVISION_LOG")/http-prebuilt-transfer.json" - export DW_PERF_PREBUILT_HTTP_IMAGE_ID="$remote_http_image_id" -fi - remote_env="$WORK_DIR/soak.env" : > "$remote_env" for name in \ @@ -365,7 +307,6 @@ for name in \ DW_PERF_READINESS_DIAGNOSTICS \ DW_PERF_PUBLISHED_SERVER_IMAGE \ DW_PERF_HTTP_VARIANT \ - DW_PERF_PREBUILT_HTTP_IMAGE_ID \ DW_PERF_FIXED_ENVELOPE \ DW_PERF_IMAGE_DISTRIBUTION_METRICS \ DW_PERF_NAMESPACES \ diff --git a/scripts/perf/soak-frankenphp-experiment.compose.yml b/scripts/perf/soak-frankenphp-experiment.compose.yml deleted file mode 100644 index 8c38348f1..000000000 --- a/scripts/perf/soak-frankenphp-experiment.compose.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - server: - build: - context: . - command: - - php - - artisan - - octane:start - - --server=frankenphp - - --host=0.0.0.0 - - --port=8080 - - --workers=12 - - --max-requests=500 diff --git a/scripts/perf/soak-octane-experiment.compose.yml b/scripts/perf/soak-octane-experiment.compose.yml deleted file mode 100644 index ca7816aad..000000000 --- a/scripts/perf/soak-octane-experiment.compose.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Append after the chosen Swoole/OpenSwoole image overlay for Server #137. -services: - server: - # This soak uses the repository-root Compose file first. Override the - # experiment overlay's relative context, which was written for a profile - # Compose file under scripts/perf/ as the first file. - build: - context: . - command: - - php - - artisan - - octane:start - - --server=swoole - - --host=0.0.0.0 - - --port=8080 - - --workers=12 - - --max-requests=500 diff --git a/scripts/perf/standard-workflow-profile.md b/scripts/perf/standard-workflow-profile.md index f8000b77a..76414ec7a 100644 --- a/scripts/perf/standard-workflow-profile.md +++ b/scripts/perf/standard-workflow-profile.md @@ -173,72 +173,7 @@ Remove this project and its synthetic durable state after recording evidence: docker compose down -v --remove-orphans ``` -For the experimental Octane/FrankenPHP candidate, append -`scripts/perf/octane-frankenphp-experiment.compose.yml` to the base profile and -fixed-envelope files. The derived image reuses the published Server application -and installs pinned Octane dependencies on PHP 8.3.33 ZTS; its PHP extensions -are built separately from the published Apache image. The candidate starts four -Octane workers by default. For eight concurrent idle polls, additionally append -`scripts/perf/poll-wait-octane-experiment.compose.yml` and set -`DW_PROFILE_POLL_WAIT_WORKER_LIMIT=6` and -`DW_PROFILE_POLL_WAIT_QUERY_LIMIT=2`; that overlay starts 12 workers, admitting -six workflow/activity polls and two query polls while leaving four workers for -ordinary requests. Keep the sum of the two caps below the available worker -count and record both values with each run. A queued request is not a successful -wait. A canary and idle-poll pass are only -setup evidence: persistent-worker state isolation, backend interruption, -repeatable throughput, and bounded memory still require qualification before -any runtime recommendation. - -To check request-scoped authentication and namespace state in one persistent -Octane worker, append `scripts/perf/octane-single-worker-experiment.compose.yml` -after the Octane overlay and start `server worker` in a fresh project. This -override configures synthetic role tokens, one HTTP worker, and recycling after -20 requests. It defaults to FrankenPHP. For the Swoole or OpenSwoole overlay, -set `DW_PROFILE_OCTANE_SERVER=swoole` when running `docker compose`; both use -Octane's Swoole server driver. Keep the published Server, MySQL and Redis image -digests and the fixed resource envelope the same across candidate fixtures. -Run the black-box probe from a separate PHP container on the project network: - -```sh -docker run --rm --user 1000:1000 --network "${COMPOSE_PROJECT_NAME}_default" \ - -e DW_PROBE_URL=http://server:8080 \ - -e DW_PROBE_TOKEN=probe-admin-token \ - -e DW_PROBE_WORKER_TOKEN=probe-worker-token \ - --mount "type=bind,source=$PWD/scripts/perf/octane-request-isolation.php,target=/probe.php,readonly" \ - php:8.3-cli php /probe.php -``` - -The probe creates disposable namespaces and pending workflows, then alternates -210 control-plane requests across namespace and credential boundaries. It must -report `result: pass`; any unexpected HTTP status or wrong-namespace response -exits nonzero. Remove the entire Compose project and its volumes afterward. - -For a bounded concurrent isolation check, use a new project with the same -published image digests and fixed envelope, replace the single-worker overlay -with `scripts/perf/octane-concurrent-isolation.compose.yml`, and retain the -selected Swoole or OpenSwoole overlay before it. The concurrent overlay sets -four HTTP workers, 50 requests per worker, and synthetic role tokens. For -example, with a unique `COMPOSE_PROJECT_NAME` and the three `DW_PROFILE_*_IMAGE` -digests set as above: - -```sh -export COMPOSE_FILE=scripts/perf/standard-workflow-profile.compose.yml:scripts/perf/standard-workflow-fixed-envelope.compose.yml:scripts/perf/octane-swoole-experiment.compose.yml:scripts/perf/octane-concurrent-isolation.compose.yml -docker compose up -d --build --wait server worker -docker run --rm --user 1000:1000 --network "${COMPOSE_PROJECT_NAME}_default" \ - -e DW_PROBE_URL=http://server:8080 \ - -e DW_PROBE_TOKEN=probe-admin-token \ - -e DW_PROBE_WORKER_TOKEN=probe-worker-token \ - --mount "type=bind,source=$PWD/scripts/perf/octane-concurrent-request-isolation.py,target=/probe.py,readonly" \ - python:3.12-alpine python /probe.py -docker compose down -v --remove-orphans -``` - -Repeat with `octane-openswoole-experiment.compose.yml` in a separate project. -The probe sends 16 simultaneous requests for 40 rounds across two namespaces, -mixing authorized, cross-namespace, invalid-token, and worker-role reads. It -checks all 640 statuses and returned namespaces, then verifies authorized reads -again. Inspect container health, restarts, and OOM state before cleanup. This -only tests a small synthetic request-isolation fixture; long polls, database -interruption, memory slope, sustained throughput, and full conformance still -need separate evidence. +The Octane (FrankenPHP, Swoole, OpenSwoole) experiments and their overlays and +probes were removed in server#677: their PECL and extension downloads bypassed +the checked CA stores, and the PHP Server they measured is retired. Their +earlier evidence stays in the issues that recorded it. diff --git a/tests/Unit/Support/composer-sinks.json b/tests/Unit/Support/composer-sinks.json index 365ec0184..643f29a66 100644 --- a/tests/Unit/Support/composer-sinks.json +++ b/tests/Unit/Support/composer-sinks.json @@ -1338,84 +1338,6 @@ } ] }, - { - "path": "scripts/perf/Dockerfile.octane-frankenphp-experiment", - "line": "FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS phpredis-source", - "count": 1, - "confinement": "Pinned Composer 2.10.3 image; PinnedComposerImageTest checks its CA store.", - "guards": [ - { - "type": "pinned-reference", - "line": null, - "distance": null - } - ] - }, - { - "path": "scripts/perf/Dockerfile.octane-frankenphp-experiment", - "line": "RUN composer require 'laravel/octane:2.20.0' 'laminas/laminas-diactoros:3.8.0' \\", - "count": 1, - "confinement": "COMPOSER_CAFILE is the pinned image's checked store and COMPOSER_CAPATH an empty directory, set before the first package request.", - "guards": [ - { - "type": "checked-ca", - "line": "ENV COMPOSER_CAFILE=/etc/composer-trust/ca.pem COMPOSER_CAPATH=/etc/composer-trust/no-ca-directory", - "distance": 1 - } - ] - }, - { - "path": "scripts/perf/Dockerfile.octane-openswoole-experiment", - "line": "FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS checked-ca", - "count": 1, - "confinement": "Pinned Composer 2.10.3 image; PinnedComposerImageTest checks its CA store.", - "guards": [ - { - "type": "pinned-reference", - "line": null, - "distance": null - } - ] - }, - { - "path": "scripts/perf/Dockerfile.octane-openswoole-experiment", - "line": "RUN composer require 'laravel/octane:2.20.0' 'laminas/laminas-diactoros:3.8.0' \\", - "count": 1, - "confinement": "COMPOSER_CAFILE is the pinned image's checked store and COMPOSER_CAPATH an empty directory, set before the first package request.", - "guards": [ - { - "type": "checked-ca", - "line": "ENV COMPOSER_CAFILE=/etc/composer-trust/ca.pem COMPOSER_CAPATH=/etc/composer-trust/no-ca-directory", - "distance": 1 - } - ] - }, - { - "path": "scripts/perf/Dockerfile.octane-swoole-experiment", - "line": "FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS checked-ca", - "count": 1, - "confinement": "Pinned Composer 2.10.3 image; PinnedComposerImageTest checks its CA store.", - "guards": [ - { - "type": "pinned-reference", - "line": null, - "distance": null - } - ] - }, - { - "path": "scripts/perf/Dockerfile.octane-swoole-experiment", - "line": "RUN composer require 'laravel/octane:2.20.0' 'laminas/laminas-diactoros:3.8.0' \\", - "count": 1, - "confinement": "COMPOSER_CAFILE is the pinned image's checked store and COMPOSER_CAPATH an empty directory, set before the first package request.", - "guards": [ - { - "type": "checked-ca", - "line": "ENV COMPOSER_CAFILE=/etc/composer-trust/ca.pem COMPOSER_CAPATH=/etc/composer-trust/no-ca-directory", - "distance": 1 - } - ] - }, { "path": "scripts/perf/Dockerfile.sdk", "line": "FROM composer:2.10.3@sha256:da624c2c705d54e2f5a9bb77a5dc6c7d091711433ebefd7035672e84c2010fee AS dependencies", diff --git a/tests/Unit/Support/server_soak_test.py b/tests/Unit/Support/server_soak_test.py index 41e993722..dc11f557b 100644 --- a/tests/Unit/Support/server_soak_test.py +++ b/tests/Unit/Support/server_soak_test.py @@ -110,9 +110,6 @@ class RuntimeEvidenceConfigurationTest(unittest.TestCase): def test_installed_workflow_version_uses_php_service(self): for variant, service in ( ("apache", "server"), - ("swoole", "server"), - ("openswoole", "server"), - ("frankenphp", "server"), ("nginx-fpm", "fpm"), ("apache-event-fpm", "fpm"), ): @@ -146,12 +143,12 @@ def test_fpm_summary_counts_proxy_and_php_memory_together(self): self.assertEqual(80, resources["http"]["cpu_mean_percent"]) def test_soak_subprocesses_use_the_same_comparison_overlays(self) -> None: - with patch.dict(os.environ, {"DW_PERF_COMPOSE_FILES": "/tmp/base.yml:/tmp/fixed.yml:/tmp/swoole.yml"}): + with patch.dict(os.environ, {"DW_PERF_COMPOSE_FILES": "/tmp/base.yml:/tmp/fixed.yml:/tmp/overlay.yml"}): self.assertEqual( [ "docker", "compose", "-p", "fixture", "-f", "/tmp/base.yml", "-f", "/tmp/fixed.yml", - "-f", "/tmp/swoole.yml", "ps", "-q", "server", + "-f", "/tmp/overlay.yml", "ps", "-q", "server", ], server_soak.compose_command("fixture", "ps", "-q", "server"), ) diff --git a/tests/Unit/Support/vultr_soak_cleanup_test.py b/tests/Unit/Support/vultr_soak_cleanup_test.py index 5c3b82818..e2abded79 100644 --- a/tests/Unit/Support/vultr_soak_cleanup_test.py +++ b/tests/Unit/Support/vultr_soak_cleanup_test.py @@ -10,33 +10,24 @@ class DisposableRunnerCleanupTest(unittest.TestCase): - def test_frankenphp_build_failure_spends_no_provider_host(self): - with tempfile.TemporaryDirectory() as temporary: - root = Path(temporary) - docker = root / "docker" - docker.write_text("#!/bin/sh\nexit 17\n") - docker.chmod(0o755) - result = subprocess.run( - ["bash", str(SCRIPT)], - cwd=ROOT, - env={ - **os.environ, - "PATH": str(root) + os.pathsep + os.environ["PATH"], - "VULTR_API_KEY": "fixture-only", - "GITHUB_SHA": "a" * 40, - "GITHUB_RUN_ID": "1", - "GITHUB_RUN_ATTEMPT": "1", - "DW_PERF_HTTP_VARIANT": "frankenphp", - "DW_PERF_FIXED_ENVELOPE": "1", - "DW_PERF_COMPOSE_PROJECT": "fixture-frankenphp", - "DW_PERF_ARTIFACT_DIR": str(root / "artifacts"), - "DW_PERF_PUBLISHED_SERVER_IMAGE": "durableworkflow/server@sha256:" + "b" * 64, - }, - text=True, capture_output=True, timeout=5, - ) - self.assertEqual(17, result.returncode, result.stdout + result.stderr) - self.assertIn("Building FrankenPHP image before creating", result.stdout) - self.assertNotIn("Creating disposable Vultr instance", result.stdout) + def test_rejects_a_removed_octane_variant_before_provider_request(self): + result = subprocess.run( + ["bash", str(SCRIPT)], + env={ + **os.environ, + "VULTR_API_KEY": "fixture-only", + "GITHUB_SHA": "a" * 40, + "GITHUB_RUN_ID": "1", + "GITHUB_RUN_ATTEMPT": "1", + "DW_PERF_HTTP_VARIANT": "frankenphp", + "DW_PERF_FIXED_ENVELOPE": "1", + "DW_PERF_PUBLISHED_SERVER_IMAGE": "durableworkflow/server@sha256:" + "b" * 64, + }, + text=True, capture_output=True, timeout=5, + ) + self.assertEqual(2, result.returncode, result.stdout + result.stderr) + self.assertIn("must be apache, nginx-fpm, or apache-event-fpm", result.stderr) + self.assertNotIn("Creating disposable Vultr instance", result.stdout) def test_rejects_unpinned_server_image_before_provider_request(self): result = subprocess.run( @@ -64,7 +55,7 @@ def test_experimental_runtime_requires_fixed_published_tuple_before_provider_req "GITHUB_SHA": "a" * 40, "GITHUB_RUN_ID": "1", "GITHUB_RUN_ATTEMPT": "1", - "DW_PERF_HTTP_VARIANT": "swoole", + "DW_PERF_HTTP_VARIANT": "nginx-fpm", "DW_PERF_FIXED_ENVELOPE": "0", "DW_PERF_PUBLISHED_SERVER_IMAGE": "durableworkflow/server@sha256:" + "b" * 64, },