Closed RedChops closed 2 years ago
Okay, this seems to have something to do with Octane. If I create the test class above and invoke it from a minimal server.php and Swoole HTTP server setup, I don't hit this issue.
Thinking it might have something to do with Laravel's heavy use of Reflection, I tried to declare the property dynamically. This doesn't change the situation unfortunately.
I discovered the source of this issue, but I don't really know the best way to solve it totally. I have a solution that works in my project.
In my first two cases, the requests fail for two different reasons. Because Octane does not run a controller constructor for consecutive requests, the PostgreSQL object stored as an instance variable is either used twice concurrently, failing because the file descriptor is already in use, or overwritten by each request in which case a similar issue appears. I discovered this issue while writing a Laravel database driver for this client, so my failure was pretty similar to the test cases in the end.
So basically, if the PostgreSQL client as it stands today is used within a context managed by Laravel/Octane, where the context may be stored or optimized between requests, the client cannot be stored as an instance variable because it cannot detect if it should be using a new coroutine context or not.
I don't know if there is anything to actually fix or improve here. If not, you can feel free to close this ticket.
PostgreSQL object can't be used concurrently, so you can create a pool of clients and borrow and return when using within the controller.
Please answer these questions before submitting your issue.
Note: I'm using Laravel Octane for this experiment, but I'm not sure if that matters in this case.
I've noted that regardless of where the PostgreSQL client object is created in scope, if it's stored then accessed as a class property, swoole will crash. If I store it as a class property in the constructor, then assign it to a local variable in the function which will use it, that crashes swoole the same way.
It's important to note that this only happens with concurrent requests, such as using
wrk
, or the way I've been testing:xargs -I % -P 20 curl -s "http://localhost:8000/test" < <(printf '%s\n' {1..20})
. If the request is made just from the browser, it works just fine.Some basic tests:
^Crashes
^Also crashes
^Works as intended
Another note: If I have another class create the client object and pass it to the
__invoke()
method, that also works as intended. The example of this is:Even though that is using a connection pool, if I call
->connect()
from my__invoke()
function, the requests all execute concurrently and without error.I'd expect the results to all execute concurrently and without error regardless of where the PostgreSQL() client object is stored within a class.
The Swoole worker itself actually crashes. In the worker output log, there is some number of entries like the following:
On a couple of occasions a PHP ErrorException was thrown with the message "swoole_event_add failed", which looks like it must come from https://github.com/openswoole/swoole-src/blob/4cf2ae4af2799ff344b4290103e0f72a9415acb5/ext-src/swoole_postgres_coro.cc#L776
To rule out any issues with WSL2, I ran this container in a pure Linux environment and had the exact same failures.
The requests also will hang curl, likely because of https://github.com/openswoole/swoole-src/issues/151
php --ri openswoole
)?uname -a
&php -v
&gcc -v
) ?uname: Linux 50d1a3fdb76e 5.10.60.1-microsoft-standard-WSL2 #1 SMP Wed Aug 25 23:20:18 UTC 2021 x86_64 GNU/Linux Linux xxx 5.10.0-11-amd64 #1 SMP Debian 5.10.92-1 (2022-01-18) x86_64 GNU/Linux
php: PHP 8.1.3 (cli) (built: Mar 11 2022 02:58:22) (NTS) Copyright (c) The PHP Group Zend Engine v4.1.3, Copyright (c) Zend Technologies with Zend OPcache v8.1.3, Copyright (c), by Zend Technologies
gcc is not installed in the Docker container.