Browse Source

[PHP] Optimize Swoole test (#8466)

* swoole test

* remove ,

* fix error

* simplify code

* Set the number of processes to be equal to the number of CPU cores

* Generating different numbers of processes in different modes.
MARiA so cute 1 year ago
parent
commit
9bc6dfd7b6

+ 648 - 0
frameworks/PHP/swoole/README.md

@@ -0,0 +1,648 @@
+<h2 align=center>
+<img width="200" height="120" alt="Swoole Logo" src="https://cdn.jsdelivr.net/gh/swoole/swoole-src/swoole-logo.svg" /> <br />
+    Swoole is an event-driven, asynchronous, coroutine-based concurrency library with high performance for PHP.
+</h2>
+
+[![lib-swoole](https://github.com/swoole/swoole-src/workflows/lib-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Alib-swoole)
+[![ext-swoole](https://github.com/swoole/swoole-src/workflows/ext-swoole/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Aext-swoole)
+[![test-linux](https://github.com/swoole/swoole-src/workflows/test-linux/badge.svg)](https://github.com/swoole/swoole-src/actions?query=workflow%3Atest-linux)
+[![Frameworks Tests](https://github.com/swoole/swoole-src/actions/workflows/framework.yml/badge.svg)](https://github.com/swoole/swoole-src/actions/workflows/framework.yml)
+[![codecov](https://codecov.io/gh/swoole/swoole-src/branch/master/graph/badge.svg)](https://codecov.io/gh/swoole/swoole-src)
+
+[![Twitter](https://badgen.net/badge/icon/twitter?icon=twitter&label)](https://twitter.com/phpswoole)
+[![Discord](https://badgen.net/badge/icon/discord?icon=discord&label)](https://discord.swoole.dev)
+[![Latest Release](https://img.shields.io/github/release/swoole/swoole-src.svg)](https://github.com/swoole/swoole-src/releases/)
+[![License](https://badgen.net/github/license/swoole/swoole-src)](https://github.com/swoole/swoole-src/blob/master/LICENSE)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/11654/badge.svg)](https://scan.coverity.com/projects/swoole-swoole-src)
+
+## ⚙️ Quick Start
+
+Run Swoole program by [Docker](https://github.com/swoole/docker-swoole)
+
+```bash
+docker run --rm phpswoole/swoole "php --ri swoole"
+```
+
+> For details on how to use it, see: [How to Use This Image](https://github.com/swoole/docker-swoole#how-to-use-this-image).
+
+### HTTP Service
+```php
+$http = new Swoole\Http\Server('127.0.0.1', 9501);
+$http->set(['hook_flags' => SWOOLE_HOOK_ALL]);
+
+$http->on('request', function ($request, $response) {
+    $result = [];
+    Co::join([
+        go(function () use (&$result) {
+            $result['google'] = file_get_contents("https://www.google.com/");
+        }),
+        go(function () use (&$result) {
+            $result['taobao'] = file_get_contents("https://www.taobao.com/");
+        })
+    ]);
+    $response->end(json_encode($result));
+});
+
+$http->start();
+```
+
+### Concurrency
+```php
+Co\run(function() {
+    Co\go(function() {
+        while(1) {
+            sleep(1);
+            $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30);
+            echo fread($fp, 8192), PHP_EOL;
+        }
+    });
+
+    Co\go(function() {
+        $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN);
+        while(1) {
+            $conn = stream_socket_accept($fp);
+            fwrite($conn, 'The local time is ' . date('n/j/Y g:i a'));
+        }
+    });
+
+    Co\go(function() {
+        $redis = new Redis();
+        $redis->connect('127.0.0.1', 6379);
+        while(true) {
+            $redis->subscribe(['test'], function ($instance, $channelName, $message) {
+                echo 'New redis message: '.$channelName, "==>", $message, PHP_EOL;
+            });
+        }
+    });
+
+    Co\go(function() {
+        $redis = new Redis();
+        $redis->connect('127.0.0.1', 6379);
+        $count = 0;
+        while(true) {
+            sleep(2);
+            $redis->publish('test','hello, world, count='.$count++);
+        }
+    });
+});
+```
+
+## Runtime Hook
+
+**Swoole hooks the blocking io function of PHP at the `bottom layer` and `automatically` converts it to a non-blocking function, so that these functions can be called concurrently in coroutines.**
+
+### Supported extension/functions
+
+* `ext-redis`
+* `ext-mysqli`
+* `ext-pdo_mysql`
+* `ext-pdo-pgsql`
+* `ext-pdo-odbc`
+* `ext-pdo-oci`
+* `ext-pdo-sqlite`
+* `ext-curl` (Support `symfony` or `guzzle`)
+* `stream functions` (e.g. `stream_socket_client`/`stream_socket_server`), Supports `TCP`/`UDP`/`UDG`/`Unix`/`SSL/TLS`/`FileSystem API`/`Pipe`
+* `ext-socket`
+* `ext-soap`
+* `sleep`/`usleep`/`time_sleep_until`
+* `proc_open`
+* `gethostbyname`/`shell_exec`/`exec`
+* `fread`/`fopen`/`fsockopen`/`fwrite`
+
+
+## 🛠 Develop & Discussion
+
++ __IDE Helper & API__: <https://github.com/swoole/ide-helper>
++ __Twitter__: <https://twitter.com/phpswoole>
++ __Discord__: <https://discord.swoole.dev>
++ __中文文档__: <https://wiki.swoole.com>
++ __中文社区__: <https://wiki.swoole.com/#/other/discussion>
+
+## 💎 Awesome Swoole
+Project [Awesome Swoole](https://github.com/swoole/awesome-swoole) maintains a curated list of awesome things related to Swoole, including
+
+* Swoole-based frameworks and libraries.
+* Packages to integrate Swoole with popular PHP frameworks, including Laravel, Symfony, Slim, and Yii.
+* Books, videos, and other learning materials about Swoole.
+* Debugging, profiling, and testing tools for developing Swoole-based applications.
+* Coroutine-friendly packages and libraries.
+* Other Swoole related projects and resources.
+
+## ✨ Event-based
+
+The network layer in Swoole is event-based and takes full advantage of the underlying epoll/kqueue implementation, making it really easy to serve millions of requests.
+
+Swoole 4.x uses a brand new engine kernel and now it has a full-time developer team, so we are entering an unprecedented period in PHP history which offers a unique possibility for rapid evolution in performance.
+
+## ⚡ Coroutine
+
+Swoole 4.x or later supports the built-in coroutine with high availability, and you can use fully synchronized code to implement asynchronous performance. PHP code without any additional keywords, the underlying automatic coroutine-scheduling.
+
+Developers can understand coroutines as ultra-lightweight threads, and you can easily create thousands of coroutines in a single process.
+
+### MySQL
+
+Concurrency 10K requests to read data from MySQL takes only 0.2s!
+
+```php
+$s = microtime(true);
+Co\run(function() {
+    for ($c = 100; $c--;) {
+        go(function () {
+            $mysql = new Swoole\Coroutine\MySQL;
+            $mysql->connect([
+                'host' => '127.0.0.1',
+                'user' => 'root',
+                'password' => 'root',
+                'database' => 'test'
+            ]);
+            $statement = $mysql->prepare('SELECT * FROM `user`');
+            for ($n = 100; $n--;) {
+                $result = $statement->execute();
+                assert(count($result) > 0);
+            }
+        });
+    }
+});
+echo 'use ' . (microtime(true) - $s) . ' s';
+```
+
+### Mixed server
+
+You can create multiple services on the single event loop: TCP, HTTP, Websocket and HTTP2, and easily handle thousands of requests.
+
+```php
+function tcp_pack(string $data): string
+{
+    return README.mdpack('N', strlen($data)) . $data;
+}
+function tcp_unpack(string $data): string
+{
+    return substr($data, 4, unpack('N', substr($data, 0, 4))[1]);
+}
+$tcp_options = [
+    'open_length_check' => true,
+    'package_length_type' => 'N',
+    'package_length_offset' => 0,
+    'package_body_offset' => 4
+];
+```
+
+```php
+$server = new Swoole\WebSocket\Server('127.0.0.1', 9501, SWOOLE_BASE);
+$server->set(['open_http2_protocol' => true]);
+// http && http2
+$server->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) {
+    $response->end('Hello ' . $request->rawcontent());
+});
+// websocket
+$server->on('message', function (Swoole\WebSocket\Server $server, Swoole\WebSocket\Frame $frame) {
+    $server->push($frame->fd, 'Hello ' . $frame->data);
+});
+// tcp
+$tcp_server = $server->listen('127.0.0.1', 9502, SWOOLE_TCP);
+$tcp_server->set($tcp_options);
+$tcp_server->on('receive', function (Swoole\Server $server, int $fd, int $reactor_id, string $data) {
+    $server->send($fd, tcp_pack('Hello ' . tcp_unpack($data)));
+});
+$server->start();
+```
+
+### Coroutine clients
+
+Whether you DNS query or send requests or receive responses, all of these are scheduled by coroutine automatically.
+
+```php
+go(function () {
+    // http
+    $http_client = new Swoole\Coroutine\Http\Client('127.0.0.1', 9501);
+    assert($http_client->post('/', 'Swoole Http'));
+    var_dump($http_client->body);
+    // websocket
+    $http_client->upgrade('/');
+    $http_client->push('Swoole Websocket');
+    var_dump($http_client->recv()->data);
+});
+go(function () {
+    // http2
+    $http2_client = new Swoole\Coroutine\Http2\Client('localhost', 9501);
+    $http2_client->connect();
+    $http2_request = new Swoole\Http2\Request;
+    $http2_request->method = 'POST';
+    $http2_request->data = 'Swoole Http2';
+    $http2_client->send($http2_request);
+    $http2_response = $http2_client->recv();
+    var_dump($http2_response->data);
+});
+go(function () use ($tcp_options) {
+    // tcp
+    $tcp_client = new Swoole\Coroutine\Client(SWOOLE_TCP);
+    $tcp_client->set($tcp_options);
+    $tcp_client->connect('127.0.0.1', 9502);
+    $tcp_client->send(tcp_pack('Swoole Tcp'));
+    var_dump(tcp_unpack($tcp_client->recv()));
+});
+```
+
+### Channel
+
+Channel is the only way for exchanging data between coroutines, the development combination of the `Coroutine + Channel` is the famous CSP programming model.
+
+In Swoole development, Channel is usually used for implementing connection pool or scheduling coroutine concurrent.
+
+#### The simplest example of a connection pool
+
+In the following example, we have a thousand concurrently requests to redis. Normally, this has exceeded the maximum number of Redis connections setting and will throw a connection exception, but the connection pool based on Channel can perfectly schedule requests. We don't have to worry about connection overload.
+
+```php
+class RedisPool
+{
+    /**@var \Swoole\Coroutine\Channel */
+    protected $pool;
+
+    /**
+     * RedisPool constructor.
+     * @param int $size max connections
+     */
+    public function __construct(int $size = 100)
+    {
+        $this->pool = new \Swoole\Coroutine\Channel($size);
+        for ($i = 0; $i < $size; $i++) {
+            $redis = new \Swoole\Coroutine\Redis();
+            $res = $redis->connect('127.0.0.1', 6379);
+            if ($res == false) {
+                throw new \RuntimeException("failed to connect redis server.");
+            } else {
+                $this->put($redis);
+            }
+        }
+    }
+
+    public function get(): \Swoole\Coroutine\Redis
+    {
+        return $this->pool->pop();
+    }
+
+    public function put(\Swoole\Coroutine\Redis $redis)
+    {
+        $this->pool->push($redis);
+    }
+
+    public function close(): void
+    {
+        $this->pool->close();
+        $this->pool = null;
+    }
+}
+
+go(function () {
+    $pool = new RedisPool();
+    // max concurrency num is more than max connections
+    // but it's no problem, channel will help you with scheduling
+    for ($c = 0; $c < 1000; $c++) {
+        go(function () use ($pool, $c) {
+            for ($n = 0; $n < 100; $n++) {
+                $redis = $pool->get();
+                assert($redis->set("awesome-{$c}-{$n}", 'swoole'));
+                assert($redis->get("awesome-{$c}-{$n}") === 'swoole');
+                assert($redis->delete("awesome-{$c}-{$n}"));
+                $pool->put($redis);
+            }
+        });
+    }
+});
+```
+
+#### Producer and consumers
+
+Some Swoole's clients implement the defer mode for concurrency, but you can still implement it flexible with a combination of coroutines and channels.
+
+```php
+go(function () {
+    // User: I need you to bring me some information back.
+    // Channel: OK! I will be responsible for scheduling.
+    $channel = new Swoole\Coroutine\Channel;
+    go(function () use ($channel) {
+        // Coroutine A: Ok! I will show you the github addr info
+        $addr_info = Co::getaddrinfo('github.com');
+        $channel->push(['A', json_encode($addr_info, JSON_PRETTY_PRINT)]);
+    });
+    go(function () use ($channel) {
+        // Coroutine B: Ok! I will show you what your code look like
+        $mirror = Co::readFile(__FILE__);
+        $channel->push(['B', $mirror]);
+    });
+    go(function () use ($channel) {
+        // Coroutine C: Ok! I will show you the date
+        $channel->push(['C', date(DATE_W3C)]);
+    });
+    for ($i = 3; $i--;) {
+        list($id, $data) = $channel->pop();
+        echo "From {$id}:\n {$data}\n";
+    }
+    // User: Amazing, I got every information at earliest time!
+});
+```
+
+### Timer
+
+```php
+$id = Swoole\Timer::tick(100, function () {
+    echo "⚙️ Do something...\n";
+});
+Swoole\Timer::after(500, function () use ($id) {
+    Swoole\Timer::clear($id);
+    echo "⏰ Done\n";
+});
+Swoole\Timer::after(1000, function () use ($id) {
+    if (!Swoole\Timer::exists($id)) {
+        echo "✅ All right!\n";
+    }
+});
+```
+#### The way of coroutine
+
+```php
+go(function () {
+    $i = 0;
+    while (true) {
+        Co::sleep(0.1);
+        echo "📝 Do something...\n";
+        if (++$i === 5) {
+            echo "🛎 Done\n";
+            break;
+        }
+    }
+    echo "🎉 All right!\n";
+});
+```
+
+## 🔥 Amazing runtime hooks
+
+**As of Swoole v4.1.0, we added the ability to transform synchronous PHP network libraries into co-routine libraries using a single line of code.**
+
+Simply call the `Swoole\Runtime::enableCoroutine()` method at the top of your script. In the sample below we connect to php-redis and concurrently read 10k requests in 0.1s:
+
+```php
+Swoole\Runtime::enableCoroutine();
+$s = microtime(true);
+Co\run(function() {
+    for ($c = 100; $c--;) {
+        go(function () {
+            ($redis = new Redis)->connect('127.0.0.1', 6379);
+            for ($n = 100; $n--;) {
+                assert($redis->get('awesome') === 'swoole');
+            }
+        });
+    }
+});
+echo 'use ' . (microtime(true) - $s) . ' s';
+```
+
+By calling this method, the Swoole kernel replaces ZendVM stream function pointers. If you use `php_stream` based extensions, all socket operations can be dynamically converted to be asynchronous IO scheduled by coroutine at runtime!
+
+### How many things you can do in 1s?
+
+Sleep 10K times, read, write, check and delete files 10K times, use PDO and MySQLi to communicate with the database 10K times, create a TCP server and multiple clients to communicate with each other 10K times, create a UDP server and multiple clients to communicate with each other 10K times... Everything works well in one process!
+
+Just see what the Swoole brings, just imagine...
+
+```php
+Swoole\Runtime::enableCoroutine();
+$s = microtime(true);
+Co\run(function() {
+    // i just want to sleep...
+    for ($c = 100; $c--;) {
+        go(function () {
+            for ($n = 100; $n--;) {
+                usleep(1000);
+            }
+        });
+    }
+
+    // 10K file read and write
+    for ($c = 100; $c--;) {
+        go(function () use ($c) {
+            $tmp_filename = "/tmp/test-{$c}.php";
+            for ($n = 100; $n--;) {
+                $self = file_get_contents(__FILE__);
+                file_put_contents($tmp_filename, $self);
+                assert(file_get_contents($tmp_filename) === $self);
+            }
+            unlink($tmp_filename);
+        });
+    }
+
+    // 10K pdo and mysqli read
+    for ($c = 50; $c--;) {
+        go(function () {
+            $pdo = new PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'root');
+            $statement = $pdo->prepare('SELECT * FROM `user`');
+            for ($n = 100; $n--;) {
+                $statement->execute();
+                assert(count($statement->fetchAll()) > 0);
+            }
+        });
+    }
+    for ($c = 50; $c--;) {
+        go(function () {
+            $mysqli = new Mysqli('127.0.0.1', 'root', 'root', 'test');
+            $statement = $mysqli->prepare('SELECT `id` FROM `user`');
+            for ($n = 100; $n--;) {
+                $statement->bind_result($id);
+                $statement->execute();
+                $statement->fetch();
+                assert($id > 0);
+            }
+        });
+    }
+
+    // php_stream tcp server & client with 12.8K requests in single process
+    function tcp_pack(string $data): string
+    {
+        return pack('n', strlen($data)) . $data;
+    }
+
+    function tcp_length(string $head): int
+    {
+        return unpack('n', $head)[1];
+    }
+
+    go(function () {
+        $ctx = stream_context_create(['socket' => ['so_reuseaddr' => true, 'backlog' => 128]]);
+        $socket = stream_socket_server(
+            'tcp://0.0.0.0:9502',
+            $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctx
+        );
+        if (!$socket) {
+            echo "$errstr ($errno)\n";
+        } else {
+            $i = 0;
+            while ($conn = stream_socket_accept($socket, 1)) {
+                stream_set_timeout($conn, 5);
+                for ($n = 100; $n--;) {
+                    $data = fread($conn, tcp_length(fread($conn, 2)));
+                    assert($data === "Hello Swoole Server #{$n}!");
+                    fwrite($conn, tcp_pack("Hello Swoole Client #{$n}!"));
+                }
+                if (++$i === 128) {
+                    fclose($socket);
+                    break;
+                }
+            }
+        }
+    });
+    for ($c = 128; $c--;) {
+        go(function () {
+            $fp = stream_socket_client("tcp://127.0.0.1:9502", $errno, $errstr, 1);
+            if (!$fp) {
+                echo "$errstr ($errno)\n";
+            } else {
+                stream_set_timeout($fp, 5);
+                for ($n = 100; $n--;) {
+                    fwrite($fp, tcp_pack("Hello Swoole Server #{$n}!"));
+                    $data = fread($fp, tcp_length(fread($fp, 2)));
+                    assert($data === "Hello Swoole Client #{$n}!");
+                }
+                fclose($fp);
+            }
+        });
+    }
+
+    // udp server & client with 12.8K requests in single process
+    go(function () {
+        $socket = new Swoole\Coroutine\Socket(AF_INET, SOCK_DGRAM, 0);
+        $socket->bind('127.0.0.1', 9503);
+        $client_map = [];
+        for ($c = 128; $c--;) {
+            for ($n = 0; $n < 100; $n++) {
+                $recv = $socket->recvfrom($peer);
+                $client_uid = "{$peer['address']}:{$peer['port']}";
+                $id = $client_map[$client_uid] = ($client_map[$client_uid] ?? -1) + 1;
+                assert($recv === "Client: Hello #{$id}!");
+                $socket->sendto($peer['address'], $peer['port'], "Server: Hello #{$id}!");
+            }
+        }
+        $socket->close();
+    });
+    for ($c = 128; $c--;) {
+        go(function () {
+            $fp = stream_socket_client("udp://127.0.0.1:9503", $errno, $errstr, 1);
+            if (!$fp) {
+                echo "$errstr ($errno)\n";
+            } else {
+                for ($n = 0; $n < 100; $n++) {
+                    fwrite($fp, "Client: Hello #{$n}!");
+                    $recv = fread($fp, 1024);
+                    list($address, $port) = explode(':', (stream_socket_get_name($fp, true)));
+                    assert($address === '127.0.0.1' && (int)$port === 9503);
+                    assert($recv === "Server: Hello #{$n}!");
+                }
+                fclose($fp);
+            }
+        });
+    }
+});
+echo 'use ' . (microtime(true) - $s) . ' s';
+```
+
+## ⌛️ Installation
+
+> As with any open source project, Swoole always provides the most reliable stability and the most powerful features in **the latest released version**. Please ensure as much as possible that you are using the latest version.
+
+### Compiling requirements
+
++ Linux, OS X or Cygwin, WSL
++ PHP 7.2.0 or later (The higher the version, the better the performance.)
++ GCC 4.8 or later
+
+### 1. Install via PECL (beginners)
+
+```shell
+pecl install swoole
+```
+
+### 2. Install from source (recommended)
+
+Please download the source packages from [Releases](https://github.com/swoole/swoole-src/releases) or:
+
+```shell
+git clone https://github.com/swoole/swoole-src.git && \
+cd swoole-src
+```
+
+Compile and install at the source folder:
+
+```shell
+phpize && \
+./configure && \
+make && make install
+```
+
+#### Enable extension in PHP
+
+After compiling and installing to the system successfully, you have to add a new line `extension=swoole.so` to `php.ini` to enable Swoole extension.
+
+#### Extra compiler configurations
+
+> for example: `./configure --enable-openssl --enable-sockets`
+
++ `--enable-openssl` or `--with-openssl-dir=DIR`
++ `--enable-sockets`
++ `--enable-mysqlnd` (need mysqlnd, it just for supporting `$mysql->escape` method)
++ `--enable-swoole-curl`
+
+### Upgrade
+
+>  ⚠️ If you upgrade from source, don't forget to `make clean` before you upgrade your swoole
+
+1. `pecl upgrade swoole`
+2. `cd swoole-src && git pull && make clean && make && sudo make install`
+3. if you change your PHP version, please re-run `phpize clean && phpize` then try to compile
+
+### Major change since version 4.3.0
+
+Async clients and API are moved to a separate PHP extension `swoole_async` since version 4.3.0, install `swoole_async`:
+
+```shell
+git clone https://github.com/swoole/ext-async.git
+cd ext-async
+phpize
+./configure
+make -j 4
+sudo make install
+```
+
+Enable it by adding a new line `extension=swoole_async.so` to `php.ini`.
+
+## 🍭 Benchmark
+
++ On the open source [Techempower Web Framework benchmarks](https://www.techempower.com/benchmarks/#section=data-r17) Swoole used MySQL database benchmark to rank first, and all performance tests ranked in the first echelon.
++ You can just run [Benchmark Script](https://github.com/swoole/benchmark/blob/master/benchmark.php) to quickly test the maximum QPS of Swoole-HTTP-Server on your machine.
+
+## 🔰️ Security issues
+
+Security issues should be reported privately, via email, to the Swoole develop team [[email protected]](mailto:[email protected]). You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message.
+
+## 🖊️ Contribution
+
+Your contribution to Swoole development is very welcome!
+
+You may contribute in the following ways:
+
+* [Report issues and feedback](https://github.com/swoole/swoole-src/issues)
+* Submit fixes, features via Pull Request
+* Write/polish documentation
+
+## ❤️ Contributors
+
+This project exists thanks to all the people who contribute. [[Contributors](https://github.com/swoole/swoole-src/graphs/contributors)].
+<a href="https://github.com/swoole/swoole-src/graphs/contributors"><img src="https://opencollective.com/swoole-src/contributors.svg?width=890&button=false" /></a>
+
+## 🎙️ Official Evangelist
+
+[Demin](https://deminy.in) has been playing with PHP since 2000, focusing on building high-performance, secure web services. He is an occasional conference speaker on PHP and Swoole, and has been working for companies in the states like eBay, Visa and Glu Mobile for years. You may find Demin on [Twitter](https://twitter.com/deminy) or [GitHub](https://github.com/deminy).
+
+## 📃 License
+
+Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html

+ 35 - 10
frameworks/PHP/swoole/benchmark_config.json

@@ -2,10 +2,11 @@
   "framework": "swoole",
   "tests": [{
     "default": {
+      "dockerfile": "swoole-sync-mysql.dockerfile",
       "json_url": "/json",
       "plaintext_url": "/plaintext",
       "db_url": "/db",
-      "query_url": "/db?queries=",
+      "query_url": "/query?queries=",
       "fortune_url": "/fortunes",
       "update_url": "/updates?queries=",
       "port": 8080,
@@ -14,19 +15,20 @@
       "database": "MySQL",
       "framework": "swoole",
       "language": "PHP",
-      "flavor": "PHP8.1",
+      "flavor": "PHP8.2",
       "orm": "Raw",
       "platform": "swoole",
       "webserver": "none",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Swoole",
+      "display_name": "Swoole-sync-mysql",
       "notes": "",
       "versus": "php"
     },
     "postgres": {
+      "dockerfile": "swoole-sync-postgres.dockerfile",
       "db_url": "/db",
-      "query_url": "/db?queries=",
+      "query_url": "/query?queries=",
       "fortune_url": "/fortunes",
       "update_url": "/updates?queries=",
       "port": 8080,
@@ -35,36 +37,59 @@
       "database": "Postgres",
       "framework": "swoole",
       "language": "PHP",
-      "flavor": "PHP8",
+      "flavor": "PHP8.2",
       "orm": "Raw",
       "platform": "swoole",
       "webserver": "none",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Swoole-postgres",
+      "display_name": "Swoole-sync-postgres",
       "notes": "",
       "versus": "php"
     },
     "no-async": {
+      "dockerfile": "swoole-async-mysql.dockerfile",
       "db_url": "/db",
-      "query_url": "/query?q=",
+      "query_url": "/query?queries=",
       "fortune_url": "/fortunes",
-      "update_url": "/updates?q=",
+      "update_url": "/updates?queries=",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",
       "database": "MySQL",
       "framework": "swoole",
       "language": "PHP",
-      "flavor": "PHP8.1",
+      "flavor": "PHP8.2",
       "orm": "Raw",
       "platform": "swoole",
       "webserver": "none",
       "os": "Linux",
       "database_os": "Linux",
-      "display_name": "Swoole-noasync",
+      "display_name": "Swoole-async-mysql",
       "notes": "Without async db pool connection",
       "versus": "php"
+    },
+    "async-postgres": {
+      "dockerfile": "swoole-async-postgres.dockerfile",
+      "db_url": "/db",
+      "query_url": "/query?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "swoole",
+      "language": "PHP",
+      "flavor": "PHP8.2",
+      "orm": "Raw",
+      "platform": "swoole",
+      "webserver": "none",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Swoole-async-postgres",
+      "notes": "",
+      "versus": "php"
     }
   }]
 }

+ 25 - 6
frameworks/PHP/swoole/config.toml

@@ -2,10 +2,27 @@
 name = "swoole"
 
 [main]
+dockerfile = "swoole-sync-mysql.dockerfile"
 urls.plaintext = "/plaintext"
 urls.json = "/json"
 urls.db = "/db"
-urls.query = "/db?queries="
+urls.query = "/query?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
+approach = "Realistic"
+classification = "Platform"
+database = "MySQL"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "swoole"
+webserver = "none"
+versus = "php"
+
+[no-async]
+dockerfile = "swoole-async-mysql.dockerfile"
+urls.db = "/db"
+urls.query = "/query?queries="
 urls.update = "/updates?queries="
 urls.fortune = "/fortunes"
 approach = "Realistic"
@@ -19,8 +36,9 @@ webserver = "none"
 versus = "php"
 
 [postgres]
+dockerfile = "swoole-sync-postgres.dockerfile"
 urls.db = "/db"
-urls.query = "/db?queries="
+urls.query = "/query?queries="
 urls.update = "/updates?queries="
 urls.fortune = "/fortunes"
 approach = "Realistic"
@@ -33,14 +51,15 @@ platform = "swoole"
 webserver = "none"
 versus = "php"
 
-[no-async]
+[async-postgres]
+dockerfile = "swoole-async-postgres.dockerfile"
 urls.db = "/db"
-urls.query = "/query?q="
-urls.update = "/updates?q="
+urls.query = "/query?queries="
+urls.update = "/updates?queries="
 urls.fortune = "/fortunes"
 approach = "Realistic"
 classification = "Platform"
-database = "MySQL"
+database = "Postgres"
 database_os = "Linux"
 os = "Linux"
 orm = "Raw"

+ 184 - 0
frameworks/PHP/swoole/database.php

@@ -0,0 +1,184 @@
+<?php
+declare(strict_types=1);
+
+use Swoole\Database\PDOConfig;
+use Swoole\Database\PDOPool;
+use Swoole\Database\PDOProxy;
+use Swoole\Database\PDOStatementProxy;
+
+class Operation
+{
+    public const WORLD_SELECT_SQL = 'SELECT id,randomNumber FROM World WHERE id = ?';
+    public const FORTUNE_SQL = 'SELECT id, message FROM Fortune';
+    public const WORLD_UPDATE_SQL = 'UPDATE World SET randomNumber = ? WHERE id = ?';
+
+    public static function db(PDOStatement|PDOStatementProxy $db): string
+    {
+        $db->execute([mt_rand(1, 10000)]);
+        return json_encode($db->fetch(PDO::FETCH_ASSOC), JSON_NUMERIC_CHECK);
+    }
+
+    public static function fortunes(PDOStatement|PDOStatementProxy $fortune): string
+    {
+        $fortune->execute();
+        $results = $fortune->fetchAll(PDO::FETCH_KEY_PAIR);
+        $results[0] = 'Additional fortune added at request time.';
+        asort($results);
+
+        $html = '';
+        foreach ($results as $id => $message) {
+            $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
+            $html .= "<tr><td>$id</td><td>$message</td></tr>";
+        }
+
+        return "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>$html</table></body></html>";
+    }
+
+    public static function query(PDOStatement|PDOStatementProxy $query, int $queries): string
+    {
+        $query_count = 1;
+        if ($queries > 1) {
+            $query_count = min($queries, 500);
+        }
+
+        $results = [];
+        while ($query_count--) {
+            $query->execute([mt_rand(1, 10000)]);
+            $results[] = $query->fetch(PDO::FETCH_ASSOC);
+        }
+
+        return json_encode($results, JSON_NUMERIC_CHECK);
+    }
+
+    public static function updates(PDOStatement|PDOStatementProxy $random, PDOStatement|PDOStatementProxy $update, int $queries): string
+    {
+        $query_count = 1;
+        if ($queries > 1) {
+            $query_count = min($queries, 500);
+        }
+
+        $results = [];
+        while ($query_count--) {
+            $id = mt_rand(1, 10000);
+            $random->execute([$id]);
+            $item = $random->fetch(PDO::FETCH_ASSOC);
+            $update->execute([$item['randomNumber'] = mt_rand(1, 10000), $id]);
+
+            $results[] = $item;
+        }
+
+        return json_encode($results, JSON_NUMERIC_CHECK);
+    }
+}
+
+class Connection
+{
+    private static PDOStatement $db;
+    private static PDOStatement $fortune;
+    private static PDOStatement $random;
+    private static PDOStatement $update;
+    private static PDOStatement $query;
+
+    public static function init(string $driver): void
+    {
+        $pdo = new PDO(
+            "$driver:host=tfb-database;dbname=hello_world",
+            "benchmarkdbuser",
+            "benchmarkdbpass",
+            [
+                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+                PDO::ATTR_EMULATE_PREPARES => false
+            ]
+        );
+
+        self::$db = self::$random = self::$query = $pdo->prepare(Operation::WORLD_SELECT_SQL);
+        self::$fortune = $pdo->prepare(Operation::FORTUNE_SQL);
+        self::$update = $pdo->prepare(Operation::WORLD_UPDATE_SQL);
+
+    }
+
+    public static function db(): string
+    {
+        return Operation::db(self::$db);
+    }
+
+    public static function fortunes(): string
+    {
+        return Operation::fortunes(self::$fortune);
+    }
+
+    public static function query(int $queries): string
+    {
+        return Operation::query(self::$query, $queries);
+    }
+
+    public static function updates(int $queries): string
+    {
+        return Operation::updates(self::$random, self::$update, $queries);
+    }
+}
+
+class Connections
+{
+    private static PDOPool $pool;
+
+    public static function init(string $driver): void
+    {
+        $config = (new PDOConfig())
+            ->withDriver($driver)
+            ->withHost('tfb-database')
+            ->withPort($driver == 'mysql' ? 3306 : 5432)
+            ->withDbName('hello_world')
+            ->withUsername('benchmarkdbuser')
+            ->withPassword('benchmarkdbpass');
+
+        self::$pool = new PDOPool($config, 20);
+    }
+
+    public static function db(): string
+    {
+        $pdo = self::get();
+        $result = Operation::db($pdo->prepare(Operation::WORLD_SELECT_SQL));
+        self::put($pdo);
+
+        return $result;
+    }
+
+    public static function fortunes(): string
+    {
+        $pdo = self::get();
+        $result = Operation::fortunes($pdo->prepare(Operation::FORTUNE_SQL));
+        self::put($pdo);
+
+        return $result;
+    }
+
+    public static function query(int $queries): string
+    {
+        $pdo = self::get();
+        $result = Operation::query($pdo->prepare(Operation::WORLD_SELECT_SQL), $queries);
+        self::put($pdo);
+
+        return $result;
+    }
+
+    public static function updates(int $queries): string
+    {
+        $pdo = self::get();
+        $result = Operation::updates($pdo->prepare(Operation::WORLD_SELECT_SQL), $pdo->prepare(Operation::WORLD_UPDATE_SQL), $queries);
+        self::put($pdo);
+
+        return $result;
+    }
+
+    private static function get(): PDO|PDOProxy
+    {
+        return self::$pool->get();
+    }
+
+    private static function put(PDO|PDOProxy $db): void
+    {
+        self::$pool->put($db);
+    }
+}

+ 0 - 116
frameworks/PHP/swoole/db-no-async.php

@@ -1,116 +0,0 @@
-<?php
-class Db
-{
-    public static PDOStatement $db;
-    public static PDOStatement $fortune;
-    public static PDOStatement $random;
-    public static PDOStatement $update;
-
-    public static function init()
-    {
-        $pdo = new PDO(
-            "mysql:host=tfb-database;dbname=hello_world",
-            "benchmarkdbuser",
-            "benchmarkdbpass",
-            [
-                PDO::ATTR_DEFAULT_FETCH_MODE  => PDO::FETCH_ASSOC,
-                PDO::ATTR_ERRMODE             => PDO::ERRMODE_EXCEPTION,
-                PDO::ATTR_EMULATE_PREPARES    => false
-            ]
-        );
-
-        self::$db        = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id = ?');
-        self::$fortune   = $pdo->prepare('SELECT id,message FROM Fortune');
-        self::$random    = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id = ?');
-        self::$update    = $pdo->prepare('UPDATE World SET randomNumber = ? WHERE id = ?');
-    }
-}
-
-/**
- * The DB test
- *
- * @return string
- */
-function db(): string
-{
-    Db::$db->execute([mt_rand(1, 10000)]);
-    return json_encode(Db::$db->fetch(), JSON_NUMERIC_CHECK);
-}
-
-/**
- * The Queries test
- *
- * @param int $queries
- *
- * @return string
- */
-function query(int $queries = 1): string
-{
-    // Read number of queries to run from URL parameter
-    $query_count = 1;
-    if ($queries > 1) {
-        $query_count = $queries > 500 ? 500 : $queries;
-    }
-
-    // Create an array with the response string.
-    $arr = [];
-
-    // For each query, store the result set values in the response array
-    while ($query_count--) {
-        DB::$db->execute([mt_rand(1, 10000)]);
-        $arr[] = Db::$db->fetch();
-    }
-
-    return json_encode($arr, JSON_NUMERIC_CHECK);
-}
-
-/**
- * The Fortunes test
- *
- * @return string
- */
-function fortunes(): string
-{
-    $fortune = [];
-    Db::$fortune->execute();
-    $fortune = Db::$fortune->fetchAll(PDO::FETCH_KEY_PAIR);
-
-    $fortune[0] = 'Additional fortune added at request time.';
-    asort($fortune);
-
-    $html = '';
-    foreach ($fortune as $id => $message) {
-        $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
-        $html .= "<tr><td>$id</td><td>$message</td></tr>";
-    }
-
-    return "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>$html</table></body></html>";
-}
-
-/**
- * The Updates test
- *
- * @param int $queries
- *
- * @return string
- */
-function updates(int $queries): string
-{
-    $query_count = 1;
-    if ($queries > 1) {
-        $query_count = $queries > 500 ? 500 : $queries;
-    }
-
-    while ($query_count--) {
-        $id = mt_rand(1, 10000);
-        Db::$random->execute([$id]);
-
-        $world = ["id" => $id, "randomNumber" => Db::$random->fetchColumn()];
-        $world['randomNumber'] = mt_rand(1, 10000);
-        Db::$update->execute([$world['randomNumber'], $world['id']]);
-
-        $arr[] = $world;
-    }
-
-    return json_encode($arr, JSON_NUMERIC_CHECK);
-}

+ 1 - 1
frameworks/PHP/swoole/php.ini

@@ -1,6 +1,6 @@
 opcache.enable_cli=1
 opcache.validate_timestamps=0
-memory_limit = 512M
+memory_limit = 1024M
 
 opcache.jit_buffer_size=128M
 opcache.jit=tracing

+ 31 - 0
frameworks/PHP/swoole/swoole-async-mysql.dockerfile

@@ -0,0 +1,31 @@
+FROM ubuntu:22.04
+
+ENV SWOOLE_VERSION 5.1.0
+ENV ENABLE_COROUTINE 1
+ENV DATABASE_DRIVER mysql
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update -yqq > /dev/null \
+    && apt install -yqq software-properties-common > /dev/null \
+    && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \
+    && apt update -yqq > /dev/null \
+    && apt install php8.2-cli php8.2-pdo-mysql php8.2-dev -y > /dev/null \
+    && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
+    && cd /tmp/swoole-src-${SWOOLE_VERSION} \
+    && phpize > /dev/null \
+    && ./configure > /dev/null \
+    && make -j8 > /dev/null \
+    && make install > /dev/null \
+    && echo "extension=swoole.so" > /etc/php/8.2/cli/conf.d/50-swoole.ini
+
+WORKDIR /swoole
+
+ADD ./swoole-server.php /swoole
+ADD ./php.ini /swoole
+ADD ./database.php /swoole
+
+RUN cat /swoole/php.ini >> /etc/php/8.2/cli/php.ini
+
+EXPOSE 8080
+CMD php /swoole/swoole-server.php

+ 31 - 0
frameworks/PHP/swoole/swoole-async-postgres.dockerfile

@@ -0,0 +1,31 @@
+FROM ubuntu:22.04
+
+ENV SWOOLE_VERSION 5.1.0
+ENV ENABLE_COROUTINE 1
+ENV DATABASE_DRIVER pgsql
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update -yqq > /dev/null \
+    && apt install -yqq software-properties-common > /dev/null \
+    && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \
+    && apt update -yqq > /dev/null \
+    && apt install php8.2-cli php8.2-pdo-pgsql php8.2-dev libpq-dev -y > /dev/null \
+    && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
+    && cd /tmp/swoole-src-${SWOOLE_VERSION} \
+    && phpize > /dev/null \
+    && ./configure --enable-swoole-pgsql > /dev/null \
+    && make -j8 > /dev/null \
+    && make install > /dev/null \
+    && echo "extension=swoole.so" > /etc/php/8.2/cli/conf.d/50-swoole.ini
+
+WORKDIR /swoole
+
+ADD ./swoole-server.php /swoole
+ADD ./php.ini /swoole
+ADD ./database.php /swoole
+
+RUN cat /swoole/php.ini >> /etc/php/8.2/cli/php.ini
+
+EXPOSE 8080
+CMD php /swoole/swoole-server.php

+ 0 - 15
frameworks/PHP/swoole/swoole-no-async.dockerfile

@@ -1,15 +0,0 @@
-FROM php:8.1-cli
-
-RUN pecl install swoole > /dev/null && \
-    docker-php-ext-enable swoole
-
-RUN docker-php-ext-install opcache pdo_mysql > /dev/null
-
-ADD ./ /swoole
-WORKDIR /swoole
-
-COPY php.ini /usr/local/etc/php/
-
-EXPOSE 8080
-
-CMD php swoole-server-noasync.php

+ 0 - 28
frameworks/PHP/swoole/swoole-postgres.dockerfile

@@ -1,28 +0,0 @@
-FROM php:8.0-cli
-
-RUN docker-php-ext-install opcache  > /dev/null
-
-ENV SWOOLE_VERSION 4.8.0
-ENV SWOOLE_POSTGRES 4.8.0
-
-RUN     apt-get update && apt-get install -y libpq-dev \
-        && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
-        && cd swoole-src-${SWOOLE_VERSION} \
-        && phpize && ./configure > /dev/null && make > /dev/null && make install > /dev/null \
-        && docker-php-ext-enable swoole
-
-RUN     cd /tmp && curl -sSL "https://github.com/swoole/ext-postgresql/archive/v${SWOOLE_POSTGRES}.tar.gz" | tar xzf - \
-        && cd ext-postgresql-${SWOOLE_POSTGRES} \
-        && phpize && ./configure > /dev/null && make > /dev/null && make install > /dev/null \
-        && docker-php-ext-enable swoole_postgresql
- 
-WORKDIR /swoole
-
-COPY swoole-server.php swoole-server.php
-RUN sed -i "s|_postgres||g" swoole-server.php
-
-COPY php.ini /usr/local/etc/php/
-
-EXPOSE 8080
-
-CMD php swoole-server.php

+ 0 - 69
frameworks/PHP/swoole/swoole-server-noasync.php

@@ -1,69 +0,0 @@
-<?php
-require_once __DIR__.'/db-no-async.php';
-
-use Swoole\Http\Server;
-use Swoole\Http\Request;
-use Swoole\Http\Response;
-
-$server = new Swoole\Http\Server('0.0.0.0', 8080, SWOOLE_BASE);
-$server->set([
-    'worker_num' => swoole_cpu_num() * 4,
-    'log_file' => '/dev/null',
-    'log_level' => 5,
-]);
-
-/**
- * On start of the PHP worker. One worker per server process is started.
- */
-$server->on('workerStart', function () {
-    Db::init();
-});
-
-/**
- * On every request to the (web)server, execute the following code
- */
-$server->on('request', static function (Request $req, Response $res) {
-    try {
-        switch ($req->server['request_uri']) {
-            case '/json':
-                $res->header('Content-Type', 'application/json');
-                $res->end(json_encode(['message' => 'Hello, World!']));
-                break;
-
-            case '/plaintext':
-                $res->header('Content-Type', 'text/plain; charset=utf-8');
-                $res->end('Hello, World!');
-                break;
-
-            case '/db':
-                $res->header('Content-Type', 'application/json');
-                $res->end(db());
-                break;
-            
-            case '/query':
-                $res->header('Content-Type', 'application/json');
-                $res->end(query((int) $req->get['q'] ?? 1));
-                break;
-
-            case '/fortunes':
-                $res->header('Content-Type', 'text/html; charset=utf-8');
-                $res->end(fortunes());
-                break;
-
-            case '/updates':
-                $res->header('Content-Type', 'application/json');
-                $res->end(updates((int) $req->get['q'] ?? 1));
-                break;
-
-            default:
-                $res->status(404);
-                $res->end('Not Found.');
-        }
-
-    } catch (\Throwable $e) {
-        $res->status(500);
-        $res->end('code ' . $e->getCode(). 'msg: '. $e->getMessage());
-    }
-});
-
-$server->start();

+ 39 - 335
frameworks/PHP/swoole/swoole-server.php

@@ -1,369 +1,73 @@
 <?php
+require __DIR__.'/database.php';
 
 use Swoole\Http\Server;
 use Swoole\Http\Request;
 use Swoole\Http\Response;
 
-$server = new Swoole\Http\Server('0.0.0.0', 8080, SWOOLE_BASE);
-$server->set([
-    'worker_num' => swoole_cpu_num(),
-    'log_file' => '/dev/null',
-    'log_level' => 5,
-]);
+$enableCoroutine = getenv('ENABLE_COROUTINE') == 1;
+$connection      = $enableCoroutine ? Connections::class : Connection::class;
 
-$pool = new \DatabasePool('postgres');
+$server  = new Server('0.0.0.0', 8080);
+$setting = [
+    'worker_num'        => swoole_cpu_num() * 4,
+    'log_file'          => '/dev/null',
+    'enable_coroutine'  => $enableCoroutine,
+    'enable_reuse_port' => true
+];
 
-/**
- * On start of the PHP worker. One worker per server process is started.
- */
-$server->on('workerStart', function ($srv) use ($pool) {
-    $pool->init(\intdiv(512, $srv->setting['worker_num']));
-});
-
-/**
- * The DB test
- *
- * @param string $database_type
- * @param int $queries
- *
- * @return string
- */
-$db_postgres = function (int $queries = 0) use ($pool): string {
-    $db = $pool->get();
-    // Read number of queries to run from URL parameter
-    $query_count = 1;
-    if ($queries > 1) {
-        $query_count = $queries > 500 ? 500 : $queries;
-    }
-
-    // Create an array with the response string.
-    $arr = [];
-
-    $db->s ??= $db->prepare('s', 'SELECT id, randomnumber FROM World WHERE id = $1');
-
-    // For each query, store the result set values in the response array
-    while ($query_count--) {
-        $id = mt_rand(1, 10000);
-        $res = $db->execute('s', [$id]);
-        $ret = $db->fetchAssoc($res);
-        // Store result in array.
-        $arr[] = ['id' => $id, 'randomnumber' => $ret['randomnumber']];
-    }
-
-    // Use the PHP standard JSON encoder.
-    // http://www.php.net/manual/en/function.json-encode.php
-    if ($queries === -1) {
-        $arr = $arr[0];
-    }
-
-    $pool->put($db);
-
-    return \json_encode($arr, JSON_NUMERIC_CHECK);
-};
-
-/**
- * The Fortunes test
- *
- * @param string $database_type
- *
- * @return string
- */
-$fortunes_postgres = function () use ($pool): string {
-    $db = $pool->get();
-
-    $fortune = [];
-
-    $db->f ??= $db->prepare('f', 'SELECT id, message FROM Fortune');
-    $res = $db->execute('f', []);
-    $arr = $db->fetchAll($res);
-
-    foreach ($arr as $row) {
-        $fortune[$row['id']] = $row['message'];
-    }
-    $fortune[0] = 'Additional fortune added at request time.';
-    \asort($fortune);
-
-    $html = '';
-    foreach ($fortune as $id => $message) {
-        $message = \htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
-        $html .= "<tr><td>{$id}</td><td>{$message}</td></tr>";
-    }
-
-    $pool->put($db);
-
-    return '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
-            .$html.
-            '</table></body></html>';
-};
-
-/**
- * The Updates test
- *
- * @param string $database_type
- * @param int $queries
- *
- * @return string
- */
-$updates_postgres = function (int $queries = 0) use ($pool): string {
-    $db = $pool->get();
-
-    $query_count = 1;
-    if ($queries > 1) {
-        $query_count = $queries > 500 ? 500 : $queries;
-    }
-
-    $arr = [];
-
-    $db->us ??= $db->prepare('us', 'SELECT id,randomnumber FROM World WHERE id = $1');
-    $db->uu ??= $db->prepare('uu', 'UPDATE World SET randomnumber = $1 WHERE id = $2');
-
-    while ($query_count--) {
-        $id = \mt_rand(1, 10000);
-        $randomNumber = \mt_rand(1, 10000);
-        $res = $db->execute('us', [$id]);
-        $ret = $db->fetchAssoc($res);
-        // Store result in array.
-        $world = ['id' => $id, 'randomnumber' => $ret['randomnumber']];
-        $world['randomnumber'] = $randomNumber;
-        $res = $db->execute('uu', [$randomNumber, $id]);
-        $arr[] = $world;
-    }
-
-    $pool->put($db);
-
-    return \json_encode($arr, JSON_NUMERIC_CHECK);
-};
-
-/**
- * The DB test
- *
- * @param string $database_type
- * @param int $queries
- *
- * @return string
- */
-$db_mysql = function (int $queries = 0) use ($pool): string {
-    $db = $pool->get();
-
-    // Read number of queries to run from URL parameter
-    $query_count = 1;
-    if ($queries > 1) {
-        $query_count = $queries > 500 ? 500 : $queries;
-    }
-
-    // Create an array with the response string.
-    $arr = [];
-    // Define query
-    $db->db_test ??= $db->prepare('SELECT id, randomNumber FROM World WHERE id = ?');
-
-    // For each query, store the result set values in the response array
-    while ($query_count--) {
-        $id = \mt_rand(1, 10000);
-        $ret = $db->db_test->execute([$id]);
-
-        // Store result in array.
-        $arr[] = ['id' => $id, 'randomNumber' => $ret[0]['randomNumber']];
-    }
-
-    // Use the PHP standard JSON encoder.
-    // http://www.php.net/manual/en/function.json-encode.php
-    if ($queries === -1) {
-        $arr = $arr[0];
-    }
-
-    $pool->put($db);
-
-    return \json_encode($arr, JSON_NUMERIC_CHECK);
-};
-
-/**
- * The Fortunes test
- *
- * @param string $database_type
- *
- * @return string
- */
-$fortunes_mysql = function () use ($pool): string {
-    $db = $pool->get();
-
-    $fortune = [];
-
-    $db->fortune_test ??= $db->prepare('SELECT id, message FROM Fortune');
-    $arr = $db->fortune_test->execute();
-
-    foreach ($arr as $row) {
-        $fortune[$row['id']] = $row['message'];
-    }
-    $fortune[0] = 'Additional fortune added at request time.';
-    \asort($fortune);
-
-    $html = '';
-    foreach ($fortune as $id => $message) {
-        $message = \htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
-        $html .= "<tr><td>{$id}</td><td>{$message}</td></tr>";
-    }
-
-    $pool->put($db);
-
-    return '<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>'
-            .$html.
-            '</table></body></html>';
-};
-
-/**
- * The Updates test
- *
- * @param string $database_type
- * @param int $queries
- *
- * @return string
- */
-$updates_mysql = function (int $queries = 0) use ($pool): string {
-    $db = $pool->get();
-
-    $query_count = 1;
-    if ($queries > 1) {
-        $query_count = $queries > 500 ? 500 : $queries;
-    }
-
-    $arr = [];
-    $db->updates_test_select ??= $db->prepare('SELECT id,randomNumber FROM World WHERE id = ?');
-    $db->updates_test_update ??= $db->prepare('UPDATE World SET randomNumber = ? WHERE id = ?');
-
-    while ($query_count--) {
-        $id = \mt_rand(1, 10000);
-        $randomNumber = \mt_rand(1, 10000);
-        $ret = $db->updates_test_select->execute([$id]);
-
-        // Store result in array.
-        $world = ['id' => $id, 'randomNumber' => $ret[0]['randomNumber']];
-        $world['randomNumber'] = $randomNumber;
-        $db->updates_test_update->execute([$randomNumber, $id]);
-
-        $arr[] = $world;
-    }
+if ($enableCoroutine) {
+    $setting['hook_flags'] = SWOOLE_HOOK_ALL;
+    $setting['worker_num'] = swoole_cpu_num();
+}
 
-    $pool->put($db);
+$server->set($setting);
 
-    return \json_encode($arr, JSON_NUMERIC_CHECK);
-};
 
+$server->on('workerStart', function () use ($connection) {
+    $connection::init(getenv('DATABASE_DRIVER'));
+});
 
-/**
- * On every request to the (web)server, execute the following code
- */
-$server->on('request', function (Request $req, Response $res) use ($db, $fortunes, $updates) {
+$server->on('request', function (Request $req, Response $res) use ($connection) {
     try {
         switch ($req->server['request_uri']) {
-            case '/json':
-                $res->header('Content-Type', 'application/json');
-                $res->end(json_encode(['message' => 'Hello, World!']));
-                break;
-
             case '/plaintext':
-                $res->header('Content-Type', 'text/plain; charset=utf-8');
+                $res->header['Content-Type'] = 'text/plain; charset=utf-8';
                 $res->end('Hello, World!');
                 break;
-
+            case '/json':
+                $res->header['Content-Type'] = 'application/json';
+                $res->end(json_encode(['message' => 'Hello, World!']));
+                break;
             case '/db':
-                $res->header('Content-Type', 'application/json');
-
-                if (isset($req->get['queries'])) {
-                    $res->end($db((int)$req->get['queries']));
-                } else {
-                    $res->end($db(-1));
-                }
+                $res->header['Content-Type'] = ['application/json'];
+                $res->end($connection::db());
                 break;
-
             case '/fortunes':
-                $res->header('Content-Type', 'text/html; charset=utf-8');
-                $res->end($fortunes());
+                $res->header['Content-Type'] = 'text/html; charset=utf-8';
+                $res->end($connection::fortunes());
+                break;
+            case '/query':
+                $res->header['Content-Type'] = 'application/json';
+                $res->end($connection::query(
+                    isset($req->get['queries']) ? (int) $req->get['queries'] : -1
+                ));
                 break;
-
             case '/updates':
-                $res->header('Content-Type', 'application/json');
-
-                if (isset($req->get['queries'])) {
-                    $res->end($updates((int)$req->get['queries']));
-                } else {
-                    $res->end($updates(-1));
-                }
+                $res->header['Content-Type'] = 'application/json';
+                $res->end($connection::updates(
+                    isset($req->get['queries']) ? (int) $req->get['queries'] : -1
+                ));
                 break;
 
             default:
                 $res->status(404);
                 $res->end('Error 404');
-
         }
-
-    } catch (\Throwable $e) {
+    } catch (Throwable) {
         $res->status(500);
         $res->end('Error 500');
     }
 });
 
 $server->start();
-
-/**
- * Class DatabasePool
- *
- * Deal with the fact that Swoole 2.1.3 has no build in database pooling
- */
-class DatabasePool
-{
-    private $server = [
-        'host' => '',
-        'user' => 'benchmarkdbuser',
-        'password' => 'benchmarkdbpass',
-        'database' => 'hello_world'
-    ];
-
-    private $pool;
-
-    private $type;
-
-    public function __construct($type)
-    {
-        $this->server['host'] = \gethostbyname('tfb-database');
-        $this->type = $type;
-    }
-
-    public function init($capacity)
-    {
-        $this->pool=new \Swoole\Coroutine\Channel($capacity);
-        while($capacity>0){
-            $db=$this->createDbInstance();
-            if($db!==false){
-                $this->pool->push($db);
-                $capacity--;
-            }
-        }
-    }
-
-    private function createDbInstance()
-    {
-        if ($this->type === 'postgres') {
-            $db = new Swoole\Coroutine\PostgreSql;
-            if ($db->connect("host={$this->server['host']} port=5432 dbname={$this->server['database']} user={$this->server['user']} password={$this->server['password']}")){
-                return $db;
-            }
-        } else if($this->type === 'mysql') {
-            $db = new Swoole\Coroutine\Mysql;
-            if ($db->connect($this->server)){
-                return $db;
-            }
-        }
-
-        return false;
-    }
-
-    public function put($db)
-    {
-        $this->pool->push($db);
-    }
-
-    public function get()
-    {
-        return $this->pool->pop();
-    }
-}

+ 31 - 0
frameworks/PHP/swoole/swoole-sync-mysql.dockerfile

@@ -0,0 +1,31 @@
+FROM ubuntu:22.04
+
+ENV SWOOLE_VERSION 5.1.0
+ENV ENABLE_COROUTINE 0
+ENV DATABASE_DRIVER mysql
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update -yqq > /dev/null \
+    && apt install -yqq software-properties-common > /dev/null \
+    && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \
+    && apt update -yqq > /dev/null \
+    && apt install php8.2-cli php8.2-pdo-mysql php8.2-dev -y > /dev/null \
+    && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
+    && cd /tmp/swoole-src-${SWOOLE_VERSION} \
+    && phpize > /dev/null \
+    && ./configure > /dev/null \
+    && make -j8 > /dev/null \
+    && make install > /dev/null \
+    && echo "extension=swoole.so" > /etc/php/8.2/cli/conf.d/50-swoole.ini
+
+WORKDIR /swoole
+
+ADD ./swoole-server.php /swoole
+ADD ./php.ini /swoole
+ADD ./database.php /swoole
+
+RUN cat /swoole/php.ini >> /etc/php/8.2/cli/php.ini
+
+EXPOSE 8080
+CMD php /swoole/swoole-server.php

+ 31 - 0
frameworks/PHP/swoole/swoole-sync-postgres.dockerfile

@@ -0,0 +1,31 @@
+FROM ubuntu:22.04
+
+ENV SWOOLE_VERSION 5.1.0
+ENV ENABLE_COROUTINE 0
+ENV DATABASE_DRIVER pgsql
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+RUN apt update -yqq > /dev/null \
+    && apt install -yqq software-properties-common > /dev/null \
+    && LC_ALL=C.UTF-8 add-apt-repository ppa:ondrej/php > /dev/null \
+    && apt update -yqq > /dev/null \
+    && apt install php8.2-cli php8.2-pdo-pgsql php8.2-dev libpq-dev -y > /dev/null \
+    && cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
+    && cd /tmp/swoole-src-${SWOOLE_VERSION} \
+    && phpize > /dev/null \
+    && ./configure > /dev/null \
+    && make -j2 > /dev/null \
+    && make install > /dev/null \
+    && echo "extension=swoole.so" > /etc/php/8.2/cli/conf.d/50-swoole.ini
+
+WORKDIR /swoole
+
+ADD ./swoole-server.php /swoole
+ADD ./php.ini /swoole
+ADD ./database.php /swoole
+
+RUN cat /swoole/php.ini >> /etc/php/8.2/cli/php.ini
+
+EXPOSE 8080
+CMD php /swoole/swoole-server.php

+ 0 - 18
frameworks/PHP/swoole/swoole.dockerfile

@@ -1,18 +0,0 @@
-FROM php:8.1-cli
-
-RUN pecl install swoole > /dev/null && \
-    docker-php-ext-enable swoole
-
-RUN docker-php-ext-install opcache  > /dev/null
-
-WORKDIR /swoole
-
-COPY swoole-server.php swoole-server.php
-RUN sed -i "s|DatabasePool('postgres|DatabasePool('mysql|g" swoole-server.php
-RUN sed -i "s|_mysql||g" swoole-server.php
-
-COPY php.ini /usr/local/etc/php/
-
-EXPOSE 8080
-
-CMD php swoole-server.php