From 1a2d74fe80ed2ca2657c30f9593944b512e0f3dc Mon Sep 17 00:00:00 2001 From: YanGusik Date: Mon, 4 May 2026 18:04:59 +0300 Subject: [PATCH 01/13] SymfonySpawn with server on C engine --- frameworks/symfony-spawn-tas/.env | 5 + frameworks/symfony-spawn-tas/Dockerfile | 40 +++++ frameworks/symfony-spawn-tas/composer.json | 65 +++++++ .../symfony-spawn-tas/config/bundles.php | 7 + .../config/packages/doctrine.yaml | 7 + .../config/packages/framework.yaml | 6 + .../config/packages/true_async.yaml | 6 + .../symfony-spawn-tas/config/routes.yaml | 5 + .../symfony-spawn-tas/config/services.yaml | 7 + frameworks/symfony-spawn-tas/meta.json | 28 ++++ frameworks/symfony-spawn-tas/public/index.php | 9 + .../src/Controller/BenchmarkController.php | 158 ++++++++++++++++++ frameworks/symfony-spawn-tas/src/Kernel.php | 11 ++ 13 files changed, 354 insertions(+) create mode 100644 frameworks/symfony-spawn-tas/.env create mode 100644 frameworks/symfony-spawn-tas/Dockerfile create mode 100644 frameworks/symfony-spawn-tas/composer.json create mode 100644 frameworks/symfony-spawn-tas/config/bundles.php create mode 100644 frameworks/symfony-spawn-tas/config/packages/doctrine.yaml create mode 100644 frameworks/symfony-spawn-tas/config/packages/framework.yaml create mode 100644 frameworks/symfony-spawn-tas/config/packages/true_async.yaml create mode 100644 frameworks/symfony-spawn-tas/config/routes.yaml create mode 100644 frameworks/symfony-spawn-tas/config/services.yaml create mode 100644 frameworks/symfony-spawn-tas/meta.json create mode 100644 frameworks/symfony-spawn-tas/public/index.php create mode 100644 frameworks/symfony-spawn-tas/src/Controller/BenchmarkController.php create mode 100644 frameworks/symfony-spawn-tas/src/Kernel.php diff --git a/frameworks/symfony-spawn-tas/.env b/frameworks/symfony-spawn-tas/.env new file mode 100644 index 000000000..2b8a6747a --- /dev/null +++ b/frameworks/symfony-spawn-tas/.env @@ -0,0 +1,5 @@ +APP_ENV=prod +APP_DEBUG=0 +APP_SECRET=benchmark_httparena_secret +DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark +DEFAULT_URI=http://localhost diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile new file mode 100644 index 000000000..8dcb3d2fc --- /dev/null +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -0,0 +1,40 @@ +FROM trueasync/php-true-async:0.7.0-alpha.3-php8.6-alpine + +COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer + +WORKDIR /app + +COPY composer.json ./ + +RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --no-scripts --no-interaction + +COPY . . + +RUN APP_ENV=prod APP_DEBUG=0 APP_SECRET=benchmark \ + DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark \ + DEFAULT_URI=http://localhost \ + php bin/console cache:warmup + +RUN echo '\ +opcache.enable=1\n\ +opcache.enable_cli=1\n\ +opcache.jit=1255\n\ +opcache.jit_buffer_size=128M\n\ +opcache.memory_consumption=256\n\ +opcache.max_accelerated_files=10000\n\ +opcache.validate_timestamps=0\n\ +memory_limit=2048M\n\ +' >> /etc/php.d/99-benchmark.ini + +ENV GODEBUG=cgocheck=0 +ENV GOGC=1000 +ENV APP_ENV=prod +ENV APP_DEBUG=0 +ENV APP_SECRET=benchmark +ENV DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark +ENV DEFAULT_URI=http://localhost +ENV APP_RUNTIME=Spawn\Symfony\Runtime\TrueAsyncRuntime + +EXPOSE 8080 8443/tcp 8443/udp + +CMD ["php", "public/index.php"] diff --git a/frameworks/symfony-spawn-tas/composer.json b/frameworks/symfony-spawn-tas/composer.json new file mode 100644 index 000000000..4374c4c8a --- /dev/null +++ b/frameworks/symfony-spawn-tas/composer.json @@ -0,0 +1,65 @@ +{ + "type": "project", + "license": "MIT", + "minimum-stability": "dev", + "prefer-stable": true, + "require": { + "php": ">=8.6", + "ext-ctype": "*", + "ext-iconv": "*", + "ext-pcntl": "*", + "ext-pdo": "*", + "doctrine/dbal": "^4.4", + "doctrine/doctrine-bundle": "^2.18", + "symfony/console": "7.4.*", + "symfony/dotenv": "7.4.*", + "symfony/flex": "^2", + "symfony/framework-bundle": "7.4.*", + "symfony/runtime": "7.4.*", + "symfony/yaml": "7.4.*", + "yangusik/symfony-spawn": "*" + }, + "config": { + "allow-plugins": { + "php-http/discovery": true, + "symfony/flex": true, + "symfony/runtime": true + }, + "sort-packages": true + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + }, + "replace": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*", + "symfony/polyfill-php74": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*", + "symfony/polyfill-php82": "*" + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd" + }, + "post-install-cmd": [ + "@auto-scripts" + ], + "post-update-cmd": [ + "@auto-scripts" + ] + }, + "conflict": { + "symfony/symfony": "*" + }, + "extra": { + "symfony": { + "allow-contrib": false, + "require": "7.4.*" + } + } +} diff --git a/frameworks/symfony-spawn-tas/config/bundles.php b/frameworks/symfony-spawn-tas/config/bundles.php new file mode 100644 index 000000000..7ee07cc31 --- /dev/null +++ b/frameworks/symfony-spawn-tas/config/bundles.php @@ -0,0 +1,7 @@ + ['all' => true], + Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], + Spawn\Symfony\TrueAsyncBundle::class => ['all' => true], +]; diff --git a/frameworks/symfony-spawn-tas/config/packages/doctrine.yaml b/frameworks/symfony-spawn-tas/config/packages/doctrine.yaml new file mode 100644 index 000000000..86df03148 --- /dev/null +++ b/frameworks/symfony-spawn-tas/config/packages/doctrine.yaml @@ -0,0 +1,7 @@ +doctrine: + dbal: + driver_class: Spawn\Symfony\Database\TrueAsyncPgsqlDriver + url: '%env(DATABASE_URL)%' + server_version: '17' + use_savepoints: true + diff --git a/frameworks/symfony-spawn-tas/config/packages/framework.yaml b/frameworks/symfony-spawn-tas/config/packages/framework.yaml new file mode 100644 index 000000000..bafc204cf --- /dev/null +++ b/frameworks/symfony-spawn-tas/config/packages/framework.yaml @@ -0,0 +1,6 @@ +framework: + secret: '%env(APP_SECRET)%' + http_method_override: false + handle_all_throwables: true + php_errors: + log: true diff --git a/frameworks/symfony-spawn-tas/config/packages/true_async.yaml b/frameworks/symfony-spawn-tas/config/packages/true_async.yaml new file mode 100644 index 000000000..c547c2ec0 --- /dev/null +++ b/frameworks/symfony-spawn-tas/config/packages/true_async.yaml @@ -0,0 +1,6 @@ +true_async: + db_pool: + enabled: true + min: 4 + max: 64 + healthcheck_interval: 30 diff --git a/frameworks/symfony-spawn-tas/config/routes.yaml b/frameworks/symfony-spawn-tas/config/routes.yaml new file mode 100644 index 000000000..41ef8140b --- /dev/null +++ b/frameworks/symfony-spawn-tas/config/routes.yaml @@ -0,0 +1,5 @@ +controllers: + resource: + path: ../src/Controller/ + namespace: App\Controller + type: attribute diff --git a/frameworks/symfony-spawn-tas/config/services.yaml b/frameworks/symfony-spawn-tas/config/services.yaml new file mode 100644 index 000000000..1972e6531 --- /dev/null +++ b/frameworks/symfony-spawn-tas/config/services.yaml @@ -0,0 +1,7 @@ +services: + _defaults: + autowire: true + autoconfigure: true + + App\: + resource: '../src/' diff --git a/frameworks/symfony-spawn-tas/meta.json b/frameworks/symfony-spawn-tas/meta.json new file mode 100644 index 000000000..9e1de8d37 --- /dev/null +++ b/frameworks/symfony-spawn-tas/meta.json @@ -0,0 +1,28 @@ +{ + "display_name": "symfony-spawn-tas", + "language": "PHP", + "type": "tuned", + "engine": "C", + "description": "Symfony with symfony-spawn bundle: coroutine-per-request isolation via TrueAsync PHP core, Doctrine DBAL connection pooling, and TrueAsyncServer.", + "repo": "https://github.com/yangusik/symfony-spawn", + "enabled": true, + "tests": [ + "baseline", + "pipelined", + "limited-conn", + "json", + "json-comp", + "upload", + "static", + "async-db", + "api-4", + "api-16", + "baseline-h2", + "static-h2", + "baseline-h3", + "static-h3" + ], + "maintainers": [ + "YanGusik" + ] +} diff --git a/frameworks/symfony-spawn-tas/public/index.php b/frameworks/symfony-spawn-tas/public/index.php new file mode 100644 index 000000000..c0037a8db --- /dev/null +++ b/frameworks/symfony-spawn-tas/public/index.php @@ -0,0 +1,9 @@ + 'text/css', + 'js' => 'application/javascript', + 'html' => 'text/html', + 'woff2' => 'font/woff2', + 'svg' => 'image/svg+xml', + 'webp' => 'image/webp', + 'json' => 'application/json', + ]; + + public function __construct(private readonly Connection $connection) + { + if (self::$dataLoaded) { + return; + } + + self::$dataset = json_decode(file_get_contents('/data/dataset.json'), true); + + $dir = '/data/static'; + if (is_dir($dir)) { + foreach (scandir($dir) as $file) { + if ($file === '.' || $file === '..') continue; + if (str_ends_with($file, '.br') || str_ends_with($file, '.gz')) continue; + $base = $dir . '/' . $file; + $ext = pathinfo($file, PATHINFO_EXTENSION); + self::$staticFiles[$file] = [ + 'data' => file_get_contents($base), + 'mime' => self::MIME_TYPES[$ext] ?? 'application/octet-stream', + 'br' => file_exists($base . '.br') ? file_get_contents($base . '.br') : null, + 'gz' => file_exists($base . '.gz') ? file_get_contents($base . '.gz') : null, + ]; + } + } + + self::$dataLoaded = true; + } + + #[Route('/baseline11', methods: ['GET', 'POST'])] + #[Route('/baseline2', methods: ['GET', 'POST'])] + public function baseline(Request $request): Response + { + $sum = array_sum($request->query->all()); + if ($request->isMethod('POST')) { + $sum += (int) $request->getContent(); + } + return new Response((string) $sum, 200, ['Content-Type' => 'text/plain']); + } + + #[Route('/pipeline')] + public function pipeline(): Response + { + return new Response('ok', 200, ['Content-Type' => 'text/plain']); + } + + #[Route('/json/{count}', requirements: ['count' => '\d+'])] + public function json(int $count, Request $request): Response + { + $count = max(0, min($count, count(self::$dataset))); + $m = (int) ($request->query->get('m', 1) ?: 1); + $items = []; + for ($i = 0; $i < $count; $i++) { + $item = self::$dataset[$i]; + $item['total'] = $item['price'] * $item['quantity'] * $m; + $items[] = $item; + } + return new Response( + json_encode(['items' => $items, 'count' => $count], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), + 200, + ['Content-Type' => 'application/json'] + ); + } + + #[Route('/upload', methods: ['POST'])] + public function upload(Request $request): Response + { + return new Response((string) strlen($request->getContent()), 200, ['Content-Type' => 'text/plain']); + } + + #[Route('/async-db')] + public function asyncDb(Request $request): Response + { + $min = (int) ($request->query->get('min', 10)); + $max = (int) ($request->query->get('max', 50)); + $limit = max(1, min(50, (int) ($request->query->get('limit', 50)))); + + try { + $stmt = $this->connection->prepare( + 'SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN ? AND ? LIMIT ?' + ); + $stmt->bindValue(1, $min); + $stmt->bindValue(2, $max); + $stmt->bindValue(3, $limit, ParameterType::INTEGER); + $result = $stmt->executeQuery(); + $rows = $result->fetchAllAssociative(); + + $items = array_map(static function (array $row): array { + $row['active'] = (bool) $row['active']; + $row['tags'] = json_decode($row['tags'], true); + $row['rating'] = [ + 'score' => (int) $row['rating_score'], + 'count' => (int) $row['rating_count'], + ]; + unset($row['rating_score'], $row['rating_count']); + return $row; + }, $rows); + + return new Response( + json_encode(['items' => $items, 'count' => count($items)]), + 200, + ['Content-Type' => 'application/json'] + ); + } catch (\Throwable) { + return new Response('{"items":[],"count":0}', 200, ['Content-Type' => 'application/json']); + } + } + + #[Route('/static/{file}', requirements: ['file' => '.+'])] + public function static(string $file, Request $request): Response + { + if (!isset(self::$staticFiles[$file])) { + return new Response('Not Found', 404, ['Content-Type' => 'text/plain']); + } + + $f = self::$staticFiles[$file]; + $ae = $request->headers->get('Accept-Encoding', ''); + $headers = ['Content-Type' => $f['mime']]; + + if ($f['br'] !== null && str_contains($ae, 'br')) { + $headers['Content-Encoding'] = 'br'; + return new Response($f['br'], 200, $headers); + } + + if ($f['gz'] !== null && str_contains($ae, 'gzip')) { + $headers['Content-Encoding'] = 'gzip'; + return new Response($f['gz'], 200, $headers); + } + + return new Response($f['data'], 200, $headers); + } +} diff --git a/frameworks/symfony-spawn-tas/src/Kernel.php b/frameworks/symfony-spawn-tas/src/Kernel.php new file mode 100644 index 000000000..779cd1f2b --- /dev/null +++ b/frameworks/symfony-spawn-tas/src/Kernel.php @@ -0,0 +1,11 @@ + Date: Mon, 4 May 2026 18:21:00 +0300 Subject: [PATCH 02/13] hotfix Dockerfile --- frameworks/symfony-spawn-tas/Dockerfile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile index 8dcb3d2fc..751554a7e 100644 --- a/frameworks/symfony-spawn-tas/Dockerfile +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -15,16 +15,14 @@ RUN APP_ENV=prod APP_DEBUG=0 APP_SECRET=benchmark \ DEFAULT_URI=http://localhost \ php bin/console cache:warmup -RUN echo '\ -opcache.enable=1\n\ +RUN printf "opcache.enable=1\n\ opcache.enable_cli=1\n\ opcache.jit=1255\n\ opcache.jit_buffer_size=128M\n\ opcache.memory_consumption=256\n\ opcache.max_accelerated_files=10000\n\ opcache.validate_timestamps=0\n\ -memory_limit=2048M\n\ -' >> /etc/php.d/99-benchmark.ini +memory_limit=2048M\n" > /etc/php.d/99-benchmark.ini ENV GODEBUG=cgocheck=0 ENV GOGC=1000 @@ -33,7 +31,7 @@ ENV APP_DEBUG=0 ENV APP_SECRET=benchmark ENV DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark ENV DEFAULT_URI=http://localhost -ENV APP_RUNTIME=Spawn\Symfony\Runtime\TrueAsyncRuntime +ENV APP_RUNTIME='Spawn\Symfony\Runtime\TrueAsyncRuntime' EXPOSE 8080 8443/tcp 8443/udp From df0dae6ebf0c66294151006ed02111c012c432fe Mon Sep 17 00:00:00 2001 From: YanGusik Date: Mon, 4 May 2026 18:24:26 +0300 Subject: [PATCH 03/13] add git --- frameworks/symfony-spawn-tas/Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile index 751554a7e..51f881e25 100644 --- a/frameworks/symfony-spawn-tas/Dockerfile +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -1,5 +1,7 @@ FROM trueasync/php-true-async:0.7.0-alpha.3-php8.6-alpine +RUN apk add --no-cache git + COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer WORKDIR /app From e6ded20d6a9dd98008ca8c4a83e3b3a7f96a19b9 Mon Sep 17 00:00:00 2001 From: MDA2AV Date: Mon, 4 May 2026 15:52:55 +0000 Subject: [PATCH 04/13] Fix CI build: cap composer parallelism and add openssh-client Composer's default 12-way parallel HTTPS download was overwhelming the DNS resolver inside BuildKit's network namespace, causing every package fetch to fail with curl error 6. Composer fell back to git+ssh, which also failed because the alpine image lacked openssh-client. - COMPOSER_MAX_PARALLEL_HTTP=1 serializes downloads - openssh-client gives the git fallback a working transport --- frameworks/symfony-spawn-tas/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile index 51f881e25..100f83aa8 100644 --- a/frameworks/symfony-spawn-tas/Dockerfile +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -1,6 +1,6 @@ FROM trueasync/php-true-async:0.7.0-alpha.3-php8.6-alpine -RUN apk add --no-cache git +RUN apk add --no-cache git openssh-client COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer @@ -8,7 +8,7 @@ WORKDIR /app COPY composer.json ./ -RUN APP_ENV=prod composer install --no-dev --optimize-autoloader --no-scripts --no-interaction +RUN APP_ENV=prod COMPOSER_MAX_PARALLEL_HTTP=1 composer install --no-dev --optimize-autoloader --no-scripts --no-interaction COPY . . From 5db5e46419cf990fc723e2b968b092ea9372e970 Mon Sep 17 00:00:00 2001 From: YanGusik Date: Mon, 4 May 2026 19:52:01 +0300 Subject: [PATCH 05/13] hotfix Dockerfile --- frameworks/symfony-spawn-tas/Dockerfile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile index 51f881e25..a235cce4e 100644 --- a/frameworks/symfony-spawn-tas/Dockerfile +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -24,7 +24,11 @@ opcache.jit_buffer_size=128M\n\ opcache.memory_consumption=256\n\ opcache.max_accelerated_files=10000\n\ opcache.validate_timestamps=0\n\ -memory_limit=2048M\n" > /etc/php.d/99-benchmark.ini +memory_limit=2048M\n\ +post_max_size=32M\n\ +upload_max_filesize=32M\n\ +zlib.output_compression_level=6\n\ +zlib.output_compression=On\n" > /etc/php.d/99-benchmark.ini ENV GODEBUG=cgocheck=0 ENV GOGC=1000 From a911fd2489a4444d26e14a62fc6ceac6b9b03131 Mon Sep 17 00:00:00 2001 From: YanGusik Date: Mon, 4 May 2026 20:13:51 +0300 Subject: [PATCH 06/13] hotfix tls --- frameworks/symfony-spawn-tas/composer.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frameworks/symfony-spawn-tas/composer.json b/frameworks/symfony-spawn-tas/composer.json index 4374c4c8a..5867a10bd 100644 --- a/frameworks/symfony-spawn-tas/composer.json +++ b/frameworks/symfony-spawn-tas/composer.json @@ -3,6 +3,12 @@ "license": "MIT", "minimum-stability": "dev", "prefer-stable": true, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/YanGusik/symfony-spawn" + } + ], "require": { "php": ">=8.6", "ext-ctype": "*", @@ -17,7 +23,7 @@ "symfony/framework-bundle": "7.4.*", "symfony/runtime": "7.4.*", "symfony/yaml": "7.4.*", - "yangusik/symfony-spawn": "*" + "yangusik/symfony-spawn": "dev-main" }, "config": { "allow-plugins": { From 40e5724c5235d5b297c49f2936b11161210a3ce5 Mon Sep 17 00:00:00 2001 From: YanGusik Date: Mon, 4 May 2026 20:17:52 +0300 Subject: [PATCH 07/13] hotfix github --- frameworks/symfony-spawn-tas/composer.json | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/frameworks/symfony-spawn-tas/composer.json b/frameworks/symfony-spawn-tas/composer.json index 5867a10bd..6ed13177c 100644 --- a/frameworks/symfony-spawn-tas/composer.json +++ b/frameworks/symfony-spawn-tas/composer.json @@ -3,12 +3,6 @@ "license": "MIT", "minimum-stability": "dev", "prefer-stable": true, - "repositories": [ - { - "type": "vcs", - "url": "https://github.com/YanGusik/symfony-spawn" - } - ], "require": { "php": ">=8.6", "ext-ctype": "*", @@ -23,7 +17,7 @@ "symfony/framework-bundle": "7.4.*", "symfony/runtime": "7.4.*", "symfony/yaml": "7.4.*", - "yangusik/symfony-spawn": "dev-main" + "yangusik/symfony-spawn": "dev-master" }, "config": { "allow-plugins": { From 0ab63b1fcf78bf8e3b07a080985693198fe6ec0d Mon Sep 17 00:00:00 2001 From: YanGusik Date: Mon, 4 May 2026 20:37:14 +0300 Subject: [PATCH 08/13] delete HTTPS tls 2 TEST temporary and gzip --- frameworks/symfony-spawn-tas/meta.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/frameworks/symfony-spawn-tas/meta.json b/frameworks/symfony-spawn-tas/meta.json index 9e1de8d37..f40170dfb 100644 --- a/frameworks/symfony-spawn-tas/meta.json +++ b/frameworks/symfony-spawn-tas/meta.json @@ -11,14 +11,11 @@ "pipelined", "limited-conn", "json", - "json-comp", "upload", "static", "async-db", "api-4", "api-16", - "baseline-h2", - "static-h2", "baseline-h3", "static-h3" ], From 5c66192cb4e0386a38adf5ac866f7d6a3d74b3b5 Mon Sep 17 00:00:00 2001 From: YanGusik Date: Tue, 5 May 2026 08:54:38 +0300 Subject: [PATCH 09/13] Add environment variables for Go and PHP configuration --- frameworks/symfony-spawn-tas/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile index a6f2407c6..8b58d9542 100644 --- a/frameworks/symfony-spawn-tas/Dockerfile +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -30,6 +30,7 @@ upload_max_filesize=32M\n\ zlib.output_compression_level=6\n\ zlib.output_compression=On\n" > /etc/php.d/99-benchmark.ini + ENV GODEBUG=cgocheck=0 ENV GOGC=1000 ENV APP_ENV=prod From 19acc25f098f8607c74a1f67d007548c2fdfa0fd Mon Sep 17 00:00:00 2001 From: Yan Date: Sat, 16 May 2026 21:42:30 +0400 Subject: [PATCH 10/13] gitignore --- frameworks/symfony-spawn-tas/.gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 frameworks/symfony-spawn-tas/.gitignore diff --git a/frameworks/symfony-spawn-tas/.gitignore b/frameworks/symfony-spawn-tas/.gitignore new file mode 100644 index 000000000..c37827d6a --- /dev/null +++ b/frameworks/symfony-spawn-tas/.gitignore @@ -0,0 +1,9 @@ +/test/* +/var/ +/vendor/ +/.env.local +/.env.local.php +/.env.*.local +/public/bundles/ +/public/build/ +/node_modules/ From d77fd0450aca116188cea6784ffa424944b69715 Mon Sep 17 00:00:00 2001 From: Yan Date: Sat, 16 May 2026 21:52:17 +0400 Subject: [PATCH 11/13] update true-async server --- frameworks/symfony-spawn-tas/Dockerfile | 45 +++----- frameworks/symfony-spawn-tas/composer.json | 24 +++- frameworks/symfony-spawn-tas/meta.json | 8 +- .../src/Controller/BenchmarkController.php | 105 ++++++++++++------ 4 files changed, 116 insertions(+), 66 deletions(-) diff --git a/frameworks/symfony-spawn-tas/Dockerfile b/frameworks/symfony-spawn-tas/Dockerfile index 8b58d9542..a5c9f76e9 100644 --- a/frameworks/symfony-spawn-tas/Dockerfile +++ b/frameworks/symfony-spawn-tas/Dockerfile @@ -1,7 +1,17 @@ -FROM trueasync/php-true-async:0.7.0-alpha.3-php8.6-alpine +FROM trueasync/php-true-async:0.7.0-alpha.14-php8.6-alpine RUN apk add --no-cache git openssh-client +RUN printf '%s\n' \ + 'opcache.jit=1255' \ + 'opcache.jit_buffer_size=128M' \ + 'opcache.memory_consumption=256' \ + 'opcache.max_accelerated_files=10000' \ + 'opcache.validate_timestamps=0' \ + 'memory_limit=1024M' \ + > /etc/php.d/99-arena.ini + +# Install composer COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer WORKDIR /app @@ -15,31 +25,8 @@ COPY . . RUN APP_ENV=prod APP_DEBUG=0 APP_SECRET=benchmark \ DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark \ DEFAULT_URI=http://localhost \ - php bin/console cache:warmup - -RUN printf "opcache.enable=1\n\ -opcache.enable_cli=1\n\ -opcache.jit=1255\n\ -opcache.jit_buffer_size=128M\n\ -opcache.memory_consumption=256\n\ -opcache.max_accelerated_files=10000\n\ -opcache.validate_timestamps=0\n\ -memory_limit=2048M\n\ -post_max_size=32M\n\ -upload_max_filesize=32M\n\ -zlib.output_compression_level=6\n\ -zlib.output_compression=On\n" > /etc/php.d/99-benchmark.ini - - -ENV GODEBUG=cgocheck=0 -ENV GOGC=1000 -ENV APP_ENV=prod -ENV APP_DEBUG=0 -ENV APP_SECRET=benchmark -ENV DATABASE_URL=pgsql://bench:bench@localhost:5432/benchmark -ENV DEFAULT_URI=http://localhost -ENV APP_RUNTIME='Spawn\Symfony\Runtime\TrueAsyncRuntime' - -EXPOSE 8080 8443/tcp 8443/udp - -CMD ["php", "public/index.php"] + php bin/console cache:warmup || true + +EXPOSE 8080 8443 + +CMD ["php", "/app/public/index.php"] diff --git a/frameworks/symfony-spawn-tas/composer.json b/frameworks/symfony-spawn-tas/composer.json index 6ed13177c..c98d1160b 100644 --- a/frameworks/symfony-spawn-tas/composer.json +++ b/frameworks/symfony-spawn-tas/composer.json @@ -17,7 +17,7 @@ "symfony/framework-bundle": "7.4.*", "symfony/runtime": "7.4.*", "symfony/yaml": "7.4.*", - "yangusik/symfony-spawn": "dev-master" + "yangusik/symfony-spawn": "*" }, "config": { "allow-plugins": { @@ -60,6 +60,28 @@ "symfony": { "allow-contrib": false, "require": "7.4.*" + }, + "runtime": { + "class": "Spawn\\Symfony\\Runtime\\TrueAsyncRuntime", + "options": { + "listeners": [ + {"host": "0.0.0.0", "port": 8080, "tls": false}, + {"host": "0.0.0.0", "port": 8443, "tls": true, "protocol": "auto"} + ], + "static_handlers": [ + { + "prefix": "/static/", + "root": "/data/static", + "precompressed": ["br", "gzip"], + "etag": true, + "open_file_cache": [1024, 60], + "on_missing": "next" + } + ], + "workers": 0, + "compression": true, + "max_body_size": 33554432 + } } } } diff --git a/frameworks/symfony-spawn-tas/meta.json b/frameworks/symfony-spawn-tas/meta.json index f40170dfb..57ff52491 100644 --- a/frameworks/symfony-spawn-tas/meta.json +++ b/frameworks/symfony-spawn-tas/meta.json @@ -11,13 +11,13 @@ "pipelined", "limited-conn", "json", + "json-comp", + "json-tls", "upload", "static", + "static-h2", + "baseline-h2", "async-db", - "api-4", - "api-16", - "baseline-h3", - "static-h3" ], "maintainers": [ "YanGusik" diff --git a/frameworks/symfony-spawn-tas/src/Controller/BenchmarkController.php b/frameworks/symfony-spawn-tas/src/Controller/BenchmarkController.php index 19c391af7..00e03054e 100644 --- a/frameworks/symfony-spawn-tas/src/Controller/BenchmarkController.php +++ b/frameworks/symfony-spawn-tas/src/Controller/BenchmarkController.php @@ -12,9 +12,8 @@ class BenchmarkController { - private static array $dataset = []; - private static array $staticFiles = []; - private static bool $dataLoaded = false; + private array $dataset = []; + private bool $dataLoaded = false; private const MIME_TYPES = [ 'css' => 'text/css', @@ -28,29 +27,15 @@ class BenchmarkController public function __construct(private readonly Connection $connection) { - if (self::$dataLoaded) { + if ($this->dataLoaded) { return; } - self::$dataset = json_decode(file_get_contents('/data/dataset.json'), true); - - $dir = '/data/static'; - if (is_dir($dir)) { - foreach (scandir($dir) as $file) { - if ($file === '.' || $file === '..') continue; - if (str_ends_with($file, '.br') || str_ends_with($file, '.gz')) continue; - $base = $dir . '/' . $file; - $ext = pathinfo($file, PATHINFO_EXTENSION); - self::$staticFiles[$file] = [ - 'data' => file_get_contents($base), - 'mime' => self::MIME_TYPES[$ext] ?? 'application/octet-stream', - 'br' => file_exists($base . '.br') ? file_get_contents($base . '.br') : null, - 'gz' => file_exists($base . '.gz') ? file_get_contents($base . '.gz') : null, - ]; - } + $datasetPath = '/data/dataset.json'; + if (is_readable($datasetPath)) { + $this->dataset = json_decode(file_get_contents($datasetPath), true) ?? []; } - - self::$dataLoaded = true; + $this->dataLoaded = true; } #[Route('/baseline11', methods: ['GET', 'POST'])] @@ -73,11 +58,11 @@ public function pipeline(): Response #[Route('/json/{count}', requirements: ['count' => '\d+'])] public function json(int $count, Request $request): Response { - $count = max(0, min($count, count(self::$dataset))); + $count = max(0, min($count, count($this->dataset))); $m = (int) ($request->query->get('m', 1) ?: 1); $items = []; for ($i = 0; $i < $count; $i++) { - $item = self::$dataset[$i]; + $item = $this->dataset[$i]; $item['total'] = $item['price'] * $item['quantity'] * $m; $items[] = $item; } @@ -132,27 +117,83 @@ public function asyncDb(Request $request): Response } } + #[Route('/sqlite-db')] + public function sqliteDb(Request $request): Response + { + $min = (int) ($request->query->get('min', 10)); + $max = (int) ($request->query->get('max', 50)); + $limit = max(1, min(50, (int) ($request->query->get('limit', 50)))); + + $dbPath = $_ENV['SQLITE_DB_PATH'] ?? '/data/benchmark.db'; + + try { + $pdo = new \PDO('sqlite:' . $dbPath); + $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + + $stmt = $pdo->prepare( + 'SELECT id, name, category, price, quantity, active, tags, rating_score, rating_count FROM items WHERE price BETWEEN :min AND :max LIMIT :limit' + ); + $stmt->bindValue(':min', $min, \PDO::PARAM_INT); + $stmt->bindValue(':max', $max, \PDO::PARAM_INT); + $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT); + $stmt->execute(); + + $rows = $stmt->fetchAll(\PDO::FETCH_ASSOC); + $items = array_map(static function (array $row): array { + $row['active'] = (bool) $row['active']; + $row['tags'] = json_decode($row['tags'], true); + $row['rating'] = [ + 'score' => (int) $row['rating_score'], + 'count' => (int) $row['rating_count'], + ]; + unset($row['rating_score'], $row['rating_count']); + return $row; + }, $rows); + + return new Response( + json_encode(['items' => $items, 'count' => count($items)]), + 200, + ['Content-Type' => 'application/json'] + ); + } catch (\Throwable) { + return new Response('{"items":[],"count":0}', 200, ['Content-Type' => 'application/json']); + } + } + + /** + * Fallback static file handler — only reached when TrueAsync StaticHandler + * could not find the file (on_missing: next) or when running in dev mode. + */ #[Route('/static/{file}', requirements: ['file' => '.+'])] public function static(string $file, Request $request): Response { - if (!isset(self::$staticFiles[$file])) { + $dir = '/data/static'; + $path = $dir . '/' . $file; + + if (!is_file($path) || !str_starts_with(realpath($path), realpath($dir))) { return new Response('Not Found', 404, ['Content-Type' => 'text/plain']); } - $f = self::$staticFiles[$file]; + $ext = pathinfo($file, PATHINFO_EXTENSION); + $mime = self::MIME_TYPES[$ext] ?? 'application/octet-stream'; + $data = file_get_contents($path); + + $headers = ['Content-Type' => $mime]; $ae = $request->headers->get('Accept-Encoding', ''); - $headers = ['Content-Type' => $f['mime']]; - if ($f['br'] !== null && str_contains($ae, 'br')) { + $brPath = $path . '.br'; + $gzPath = $path . '.gz'; + + if (file_exists($brPath) && str_contains($ae, 'br')) { $headers['Content-Encoding'] = 'br'; - return new Response($f['br'], 200, $headers); + return new Response(file_get_contents($brPath), 200, $headers); } - if ($f['gz'] !== null && str_contains($ae, 'gzip')) { + if (file_exists($gzPath) && str_contains($ae, 'gzip')) { $headers['Content-Encoding'] = 'gzip'; - return new Response($f['gz'], 200, $headers); + return new Response(file_get_contents($gzPath), 200, $headers); } - return new Response($f['data'], 200, $headers); + return new Response($data, 200, $headers); } } From 5a1e91e729950a7f22db5889589374253497841d Mon Sep 17 00:00:00 2001 From: Yan Date: Sat, 16 May 2026 21:54:32 +0400 Subject: [PATCH 12/13] json fix --- frameworks/symfony-spawn-tas/meta.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/symfony-spawn-tas/meta.json b/frameworks/symfony-spawn-tas/meta.json index 57ff52491..f7c1126ac 100644 --- a/frameworks/symfony-spawn-tas/meta.json +++ b/frameworks/symfony-spawn-tas/meta.json @@ -17,7 +17,7 @@ "static", "static-h2", "baseline-h2", - "async-db", + "async-db" ], "maintainers": [ "YanGusik" From 1b3831843490202c32a9feae40833376f86d5d74 Mon Sep 17 00:00:00 2001 From: Yan Date: Sat, 16 May 2026 22:00:46 +0400 Subject: [PATCH 13/13] add 8081 port --- frameworks/symfony-spawn-tas/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frameworks/symfony-spawn-tas/composer.json b/frameworks/symfony-spawn-tas/composer.json index c98d1160b..eef16ad0b 100644 --- a/frameworks/symfony-spawn-tas/composer.json +++ b/frameworks/symfony-spawn-tas/composer.json @@ -66,6 +66,7 @@ "options": { "listeners": [ {"host": "0.0.0.0", "port": 8080, "tls": false}, + {"host": "0.0.0.0", "port": 8081, "tls": true, "protocol": "http1"}, {"host": "0.0.0.0", "port": 8443, "tls": true, "protocol": "auto"} ], "static_handlers": [