[HttpClient] Fix Failed to open stream: Too many open files#45073
Conversation
|
@nicolas-grekas I think the issue is related to $multi = (object) [
//'handle' => $this->handle,
//'handle' => &$this->handle,
'logger' => &$this->logger,
'pushedResponses' => &$this->pushedResponses,
'dnsCache' => &$this->dnsCache,
'handlesActivity' => &$this->handlesActivity,
'openHandles' => &$this->openHandles,
'execCounter' => &$this->execCounter,
'pauseExpiries' => &$this->pauseExpiries,
'lastTimeout' => &$this->lastTimeout,
];
// Keep a dummy "onPush" reference to work around a refcount bug in PHP
curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, $this->onPush = static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) {
return self::handlePush($parent, $pushed, $requestHeaders, $multi, $maxPendingPushes);
});It works. But I soon as I uncomment one of the One other thing I noticed, a lot more
var_dump($this->multi->handle);=> . 854 / 4775 ( 17%)
resource(15232) of type (curl_multi)
.resource(15342) of type (curl_multi)
.resource(15383) of type (curl_multi)
var_dump($this->handle);=> . 854 / 4775 ( 17%)
resource(26271) of type (curl_multi)
.resource(26500) of type (curl_multi)
.resource(26569) of type (curl_multi) |
|
@nicolas-grekas might have found a "leak" in if (\is_resource($this->handle) || $this->handle instanceof \CurlMultiHandle) {
if (\defined('CURLMOPT_PUSHFUNCTION')) {
curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, null);
}
$active = 0;
while (\CURLM_CALL_MULTI_PERFORM === curl_multi_exec($this->handle, $active));
}
foreach ($this->openHandles as [$ch]) {
if (\is_resource($ch) || $ch instanceof \CurlHandle) {
curl_setopt($ch, \CURLOPT_VERBOSE, false);
}
}If I add these 2 blocks (especially the one with the |
|
Regarding this part added in your last PR, I don't really understand the goal but shouldn't we have a call to $this->share = curl_share_init();
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_DNS);
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_SSL_SESSION);
if (\defined('CURL_LOCK_DATA_CONNECT')) {
curl_share_setopt($this->share, \CURLSHOPT_SHARE, \CURL_LOCK_DATA_CONNECT);
} |
|
I wish I could reproduce locally. If you can share the code privately with me, you can reach me on Symfony's Slack. |
|
Can you please try this patch instead? --- a/src/Symfony/Component/HttpClient/Internal/CurlClientState.php
+++ b/src/Symfony/Component/HttpClient/Internal/CurlClientState.php
@@ -23,9 +23,9 @@ use Symfony\Component\HttpClient\Response\CurlResponse;
*/
final class CurlClientState extends ClientState
{
- /** @var \CurlMultiHandle|resource */
+ /** @var \CurlMultiHandle|resource|null */
public $handle;
- /** @var \CurlShareHandle|resource */
+ /** @var \CurlShareHandle|resource|null */
public $share;
/** @var PushedResponse[] */
public $pushedResponses = [];
@@ -65,8 +65,17 @@ final class CurlClientState extends ClientState
return;
}
- curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, function ($parent, $pushed, array $requestHeaders) use ($maxPendingPushes) {
- return $this->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes);
+ // Clone to prevent a circular reference
+ $multi = clone $this;
+ $multi->handle = null;
+ $multi->share = null;
+ $multi->pushedResponses = &$this->pushedResponses;
+ $multi->logger = &$this->logger;
+ $multi->handlesActivity = &$this->handlesActivity;
+ $multi->openHandles = &$this->openHandles;
+
+ curl_multi_setopt($this->handle, \CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi, $maxPendingPushes) {
+ return $multi->handlePush($parent, $pushed, $requestHeaders, $maxPendingPushes);
});
} |
a548fe5 to
40d22b8
Compare
|
Thanks @nicolas-grekas for the patch, it works perfectly, no more issues! 🚀 |
|
Thank you @adrienfr. |
With a large PHPUnit tests suite, I receive this error "Failed to open stream: Too many open files".
I found the original code from @nicolas-grekas here and with small adjustments, it resolves my issue.
Warning: tests for HttpClient are OK but would someone try with a real HTTP/2 client push? I was not able to test it.