|
@@ -1,134 +1,147 @@
|
|
|
<?php
|
|
|
+
|
|
|
+use Psr\Http\Message\ResponseInterface;
|
|
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
|
|
+use React\EventLoop\Loop;
|
|
|
+use React\MySQL\ConnectionInterface as DbConnection;
|
|
|
+use React\MySQL\Factory as DbFactory;
|
|
|
use React\Http\Message\Response;
|
|
|
+use React\MySQL\QueryResult;
|
|
|
+use React\Promise\PromiseInterface;
|
|
|
|
|
|
-function init()
|
|
|
-{
|
|
|
- global $world, $fortune, $update;
|
|
|
- $pdo = new PDO(
|
|
|
- 'mysql:host=tfb-database;dbname=hello_world',
|
|
|
- 'benchmarkdbuser',
|
|
|
- 'benchmarkdbpass',
|
|
|
- [
|
|
|
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
|
- PDO::ATTR_EMULATE_PREPARES => false
|
|
|
- ]
|
|
|
- );
|
|
|
- $world = $pdo->prepare('SELECT id,randomNumber FROM World WHERE id=?');
|
|
|
- $update = $pdo->prepare('UPDATE World SET randomNumber=? WHERE id=?');
|
|
|
- $fortune = $pdo->prepare('SELECT id,message FROM Fortune');
|
|
|
- $fortune->setFetchMode(PDO::FETCH_KEY_PAIR);
|
|
|
-}
|
|
|
+use function React\Promise\all;
|
|
|
|
|
|
-function router(Request $request)
|
|
|
+/** @return Closure(Request):ResponseInterface */
|
|
|
+function requestHandler(): Closure
|
|
|
{
|
|
|
- return match($request->getUri()->getPath()) {
|
|
|
- '/plaintext' => text(),
|
|
|
- '/json' => json(),
|
|
|
- '/db' => db(),
|
|
|
- '/fortunes' => fortune(),
|
|
|
- '/query' => query($request),
|
|
|
- '/update' => updateraw($request),
|
|
|
- // '/info' => info(),
|
|
|
- default => new Response(404, [], 'Error 404'),
|
|
|
+ $connection = establishDbConnection('benchmarkdbuser:benchmarkdbpass@tfb-database/hello_world?idle=0.5');
|
|
|
+
|
|
|
+ $world = static function (int $id) use ($connection): PromiseInterface {
|
|
|
+ return $connection->query('SELECT id,randomNumber FROM World WHERE id=?', [$id]);
|
|
|
};
|
|
|
-}
|
|
|
|
|
|
-function text()
|
|
|
-{
|
|
|
- return new Response(200, [
|
|
|
- 'Content-Type' => 'text/plain'
|
|
|
- ], 'Hello, World!');
|
|
|
-}
|
|
|
+ $fortune = static function () use ($connection): PromiseInterface {
|
|
|
+ return $connection->query('SELECT id,message FROM Fortune');
|
|
|
+ };
|
|
|
|
|
|
-function json()
|
|
|
-{
|
|
|
- return new Response(200, [
|
|
|
- 'Content-Type' => 'application/json'
|
|
|
- ], json_encode(['message' => 'Hello, World!']));
|
|
|
+ $update = static function (int $id, int $randomNumber) use ($connection): PromiseInterface {
|
|
|
+ return $connection->query('UPDATE World SET randomNumber=? WHERE id=?', [$randomNumber, $id]);
|
|
|
+ };
|
|
|
+
|
|
|
+ return static function (Request $request) use ($world, $fortune, $update): ResponseInterface | PromiseInterface {
|
|
|
+ return match($request->getUri()->getPath()) {
|
|
|
+ '/plaintext' => Response::plaintext('Hello, World!'),
|
|
|
+ '/json' => Response::json(['message' => 'Hello, World!']),
|
|
|
+ '/db' => db($world),
|
|
|
+ '/fortunes' => fortune($fortune),
|
|
|
+ '/query' => query(queryCount($request), $world),
|
|
|
+ '/update' => updateraw(queryCount($request), $world, $update),
|
|
|
+ // '/info' => info(),
|
|
|
+ default => new Response(404, [], 'Error 404'),
|
|
|
+ };
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
-function db()
|
|
|
-{
|
|
|
- global $world;
|
|
|
+function establishDbConnection(
|
|
|
+ #[SensitiveParameter]
|
|
|
+ string $uri,
|
|
|
+): DbConnection {
|
|
|
+ $connection = (new DbFactory())->createLazyConnection($uri);
|
|
|
+
|
|
|
+ $interrupt = $connection->quit(...);
|
|
|
|
|
|
- $world->execute([mt_rand(1, 10000)]);
|
|
|
+ $connection->on('close', static function () use (&$interrupt) {
|
|
|
+ Loop::removeSignal(SIGINT, $interrupt);
|
|
|
+ Loop::removeSignal(SIGTERM, $interrupt);
|
|
|
+ });
|
|
|
|
|
|
- return new Response(200, [
|
|
|
- 'Content-Type' => 'application/json'
|
|
|
- ], json_encode($world->fetch()));
|
|
|
+ Loop::addSignal(SIGINT, $interrupt);
|
|
|
+ Loop::addSignal(SIGTERM, $interrupt);
|
|
|
+
|
|
|
+ return $connection;
|
|
|
}
|
|
|
|
|
|
-function query($request)
|
|
|
+/** @param Closure(int):PromiseInterface $world */
|
|
|
+function db(Closure $world): PromiseInterface
|
|
|
{
|
|
|
- global $world;
|
|
|
+ $id = mt_rand(1, 10000);
|
|
|
|
|
|
- $query_count = 1;
|
|
|
- $q = (int) $request->getQueryParams()['q'];
|
|
|
- if ($q > 1) {
|
|
|
- $query_count = min($q, 500);
|
|
|
- }
|
|
|
+ return $world($id)->then(
|
|
|
+ static fn (QueryResult $result): ResponseInterface => Response::json($result->resultRows[0]),
|
|
|
+ );
|
|
|
+}
|
|
|
|
|
|
- while ($query_count--) {
|
|
|
- $world->execute([mt_rand(1, 10000)]);
|
|
|
- $arr[] = $world->fetch();
|
|
|
+function queryCount(Request $request): int
|
|
|
+{
|
|
|
+ $count = (int) ($request->getQueryParams()['q'] ?? 1);
|
|
|
+
|
|
|
+ if ($count > 1) {
|
|
|
+ return min($count, 500);
|
|
|
}
|
|
|
|
|
|
- return new Response(200, [
|
|
|
- 'Content-Type' => 'application/json'
|
|
|
- ], json_encode($arr));
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
-function updateraw($request)
|
|
|
+/** @param Closure(int):PromiseInterface $world */
|
|
|
+function query(int $queryCount, Closure $world): PromiseInterface
|
|
|
{
|
|
|
- global $world, $update;
|
|
|
+ $processQueries = static function (int $count) use ($world): iterable {
|
|
|
+ while ($count--) {
|
|
|
+ $id = mt_rand(1, 10000);
|
|
|
|
|
|
- $query_count = 1;
|
|
|
- $q = (int) $request->getQueryParams()['q'];
|
|
|
- if ($q > 1) {
|
|
|
- $query_count = min($q, 500);
|
|
|
- }
|
|
|
+ yield $world($id)->then(static fn (QueryResult $result): array => $result->resultRows[0]);
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- while ($query_count--) {
|
|
|
- $id = mt_rand(1, 10000);
|
|
|
- $world->execute([$id]);
|
|
|
- $item = $world->fetch();
|
|
|
- $update->execute(
|
|
|
- [$item['randomNumber'] = mt_rand(1, 10000), $id]
|
|
|
- );
|
|
|
+ return all($processQueries($queryCount))
|
|
|
+ ->then(static fn (array $result): ResponseInterface => Response::json($result));
|
|
|
+}
|
|
|
|
|
|
- $arr[] = $item;
|
|
|
- }
|
|
|
+/**
|
|
|
+ * @param Closure(int):PromiseInterface $world
|
|
|
+ * @param Closure(int, int):PromiseInterface $update
|
|
|
+ */
|
|
|
+function updateraw(int $queryCount, Closure $world, Closure $update): PromiseInterface
|
|
|
+{
|
|
|
+ $processQueries = static function (int $count) use ($world, $update): iterable {
|
|
|
+ while ($count--) {
|
|
|
+ $id = mt_rand(1, 10000);
|
|
|
+
|
|
|
+ yield $world($id)->then(
|
|
|
+ static function (QueryResult $result) use ($update): PromiseInterface {
|
|
|
+ $updated = $result->resultRows[0];
|
|
|
+ $updated['randomNumber'] = mt_rand(1, 10000);
|
|
|
+
|
|
|
+ return $update($updated['id'], $updated['randomNumber'])
|
|
|
+ ->then(static fn (): array => $updated);
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ };
|
|
|
|
|
|
- // $pdo->beginTransaction();
|
|
|
- // foreach($arr as $world) {
|
|
|
- // $update->execute([$world['randomNumber'], $world['id']]);
|
|
|
- // }
|
|
|
- // $pdo->commit();
|
|
|
- return new Response(200, [
|
|
|
- 'Content-Type' => 'application/json'
|
|
|
- ], json_encode($arr));
|
|
|
+ return all($processQueries($queryCount))
|
|
|
+ ->then(static fn (array $result): ResponseInterface => Response::json($result));
|
|
|
}
|
|
|
|
|
|
-function fortune()
|
|
|
+function fortune(Closure $fortune): PromiseInterface
|
|
|
{
|
|
|
- global $fortune;
|
|
|
+ $formatResult = static function (array $rows): string {
|
|
|
+ $rows[] = ['id' => 0, 'message' => 'Additional fortune added at request time.'];
|
|
|
+ usort($rows, static fn (array $one, array $other) => $one['message'] <=> $other['message']);
|
|
|
|
|
|
- $fortune->execute();
|
|
|
+ $html = '';
|
|
|
|
|
|
- $arr = $fortune->fetchAll();
|
|
|
- $arr[0] = 'Additional fortune added at request time.';
|
|
|
- asort($arr);
|
|
|
+ foreach ($rows as $row) {
|
|
|
+ $message = htmlspecialchars($row['message'], ENT_QUOTES, 'UTF-8');
|
|
|
|
|
|
- $html = '';
|
|
|
- foreach ($arr as $id => $message) {
|
|
|
- $message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8');
|
|
|
- $html .= "<tr><td>$id</td><td>$message</td></tr>";
|
|
|
- }
|
|
|
+ $html .= "<tr><td>${row['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>";
|
|
|
+ };
|
|
|
|
|
|
- return new Response(200, [
|
|
|
- 'Content-Type' => 'text/html; charset=UTF-8',
|
|
|
- ], "<!DOCTYPE html><html><head><title>Fortunes</title></head><body><table><tr><th>id</th><th>message</th></tr>$html</table></body></html>"
|
|
|
+ return $fortune()->then(
|
|
|
+ static fn (QueryResult $result): ResponseInterface => Response::html($formatResult($result->resultRows)),
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -138,4 +151,4 @@ function fortune()
|
|
|
phpinfo();
|
|
|
return new Response(200, ['Content-Type' => 'text/plain'], ob_get_clean());
|
|
|
}
|
|
|
- */
|
|
|
+ */
|