This repository reproduces edge cases for the PHP runtime environments that are using the shared memory model. It uses an example Symfony project with single test controller.
Clone the content of this repository:
cd ~/Projects
git clone [email protected]:SerheyDolgushev/php-shared-memory-model.git
cd php-shared-memory-model
If you are on macOS, please follow the next steps:
-
Install
pcre2
:brew install pcre2
-
Link
pcre2
into PHP, in this example/opt/homebrew/opt/[email protected]
is the PHP path:ln -s /opt/homebrew/opt/pcre2/include/pcre2.h /opt/homebrew/opt/[email protected]/include/php/ext/pcre/
-
Install
swool
using via PECL:pecl install -o -D 'enable-sockets="yes" enable-swoole-curl="yes" enable-brotli="yes" enable-cares="no" enable-mysqlnd="no" enable-swoole-pgsql="no" with-swoole-odbc="no" with-swoole-oracle="no" enable-swoole-sqlite="no" enable-openssl="no"' swoole
-
Disable swoole extension by default by removing
extension=swoole.so
from/opt/homebrew/etc/php/8.3/php.ini
Otherwise, please follow Swoole Installation guide.
If you are on macOS, the simplest way to install RoadRunner is using Homebrew
brew install roadrunner
Otherwise, please follow RoadRunner Installation guide.
Follow the official instructions to download Standalone Binary, and please save it to ~/frankenphp
.
First of all, have a glance at TestController. It has the only testAction
method that returns the date and increased value of the counter
property.
Start local PHP server:
php -S 127.0.0.1:8000 public/index.php
And send a few requests to the TestController::testAction
:
% curl http://127.0.0.1:8000/test
[2024-02-24T08:05:03+00:00] Counter: 1
% curl http://127.0.0.1:8000/test
[2024-02-24T08:05:07+00:00] Counter: 1
% curl http://127.0.0.1:8000/test
[2024-02-24T08:05:10+00:00] Counter: 1
As expected, each response returns 1
. Nothing strange here.
Try to perform the same steps by running the Swoole runtime:
APP_RUNTIME=Runtime\\Swoole\\Runtime php -d extension=swoole.so public/swoole.php
And sending the same test requests:
% curl http://127.0.0.1:8000/test
[2024-02-24T08:07:59+00:00] Counter: 1
% curl http://127.0.0.1:8000/test
[2024-02-24T08:08:02+00:00] Counter: 2
% curl http://127.0.0.1:8000/test
[2024-02-24T08:08:06+00:00] Counter: 3
In this case, each response returns the incremented value of the previous response. Which might be unexpected behavior.
Let's reproduce the same test in RoadRunner runtime:
rr serve -c rr.yaml -o http.pool.num_workers=1
And the same test requests:
% curl http://127.0.0.1:8000/test
[2024-02-24T08:10:45+00:00] Counter: 1
% curl http://127.0.0.1:8000/test
[2024-02-24T08:10:49+00:00] Counter: 2
% curl http://127.0.0.1:8000/test
[2024-02-24T08:10:52+00:00] Counter: 3
And the results are similar to the Swoole ones.
Let's use FrankenPHP to test:
cd ./public
APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime ~/frankenphp php-server -l 127.0.0.1:8000 -w ./index.php,1
And again the same test requests:
% curl http://127.0.0.1:8000/test
[2024-03-01T08:35:40+00:00] Counter: 1
% curl http://127.0.0.1:8000/test
[2024-03-01T08:35:41+00:00] Counter: 2
% curl http://127.0.0.1:8000/test
[2024-03-01T08:35:42+00:00] Counter: 3
The simplified explanation is that PHP runtime uses Shared-nothing architecture by running the garbage collector to clear the memory between the requests. So memory is cleared after the previous request is completed and before the next one starts. But Swoole and RoarRunner runtimes are using Shared memory model which gives it its power. In this case, the memory is shared between different requests handled by the same worker. Please note, single-worker configuration have been used for all runtimes to simplify showcasing the problem. You can find more details in RoarRunner documentation.