aws icon indicating copy to clipboard operation
aws copied to clipboard

Retry and Symfony/http-client 5.3.10

Open Nyholm opened this issue 4 years ago • 7 comments

I just get this error message:

In AsyncResponse.php line 438:
                                                                  
  A chunk passthru cannot yield more than one "isFirst()" chunk.  

It means that our Response opens the buffer twice, ie the first chunk is seen twice.

I think it is related to https://github.com/symfony/symfony/pull/43537

Nyholm avatar Nov 01 '21 21:11 Nyholm

I have not seen this issue (yet), though I don't think I have any code that looks at the response body (at least in the success case) - the error handling code in this package will read the response body in some circumstances.

GrahamCampbell avatar Jan 16 '22 15:01 GrahamCampbell

Ok, I was able to replicate a similar error.

PHP Fatal error:  Uncaught LogicException: A chunk passthru must yield an "isLast()" chunk before ending a stream. in /data/vendor/symfony/http-client/Response/AsyncResponse.php:300
Stack trace:
#0 [internal function]: Symfony\Component\HttpClient\Response\AsyncResponse::stream()
#1 /data/vendor/symfony/http-client/Response/ResponseStream.php(42): Generator->next()
#2 /data/vendor/async-aws/core/src/Response.php(209): Symfony\Component\HttpClient\Response\ResponseStream->next()
#3 /data/vendor/async-aws/core/src/Result.php(90): AsyncAws\Core\Response::wait()
#4 /data/vendor/[REDACTED]/src/Variables/VariableResolver.php(119): AsyncAws\Core\Result::wait()
...
Next AsyncAws\Core\Exception\Http\ClientException: HTTP 400 returned for "https://secretsmanager.us-east-1.amazonaws.com/".

Code:    AccessDeniedException
Message: User: [REDACTED] is not authorized to perform: secretsmanager:GetSecretValue on resource: [REDACTED] because no identity-based policy allows the secretsmanager:GetSecretValue action
Type:    
Detail:  
 in /data/vendor/async-aws/core/src/Response.php:406
Stack trace:
#0 /data/vendor/async-aws/core/src/Response.php(423): AsyncAws\Core\Response::AsyncAws\Core\{closure}()
#1 /data/vendor/async-aws/core/src/Response.php(123): AsyncAws\Core\Response->getResolveStatus()
#2 /data/vendor/async-aws/core/src/Response.php(105): AsyncAws\Core\Response->resolve()
#3 /data/vendor/[REDACTED]/src/Variables/VariableResolver.php(83): AsyncAws\Core\Response->__destruct()
...

GrahamCampbell avatar Jan 21 '22 01:01 GrahamCampbell

I'd like to have a reproducer. Doable ?

nicolas-grekas avatar Jan 21 '22 06:01 nicolas-grekas

@nicolas-grekas The only relisble way to get this to happen that I've seen is to call AWS services too hard. I can reliably get this to crash if I blast most services with 100k requests. Say for example, running a dynamodb query:

LogicException A chunk passthru cannot yield more than one "isFirst()" chunk. 
    vendor/symfony/http-client/Response/AsyncResponse.php:444 Symfony\Component\HttpClient\Response\AsyncResponse::openBuffer
    vendor/symfony/http-client/Response/AsyncResponse.php:373 Symfony\Component\HttpClient\Response\AsyncResponse::passthruStream
    vendor/symfony/http-client/Response/AsyncResponse.php:237 Symfony\Component\HttpClient\Response\AsyncResponse::stream
    vendor/symfony/http-client/Response/CommonResponseTrait.php:68 Symfony\Component\HttpClient\Response\AsyncResponse::getContent
    vendor/symfony/http-client/Response/CommonResponseTrait.php:83 Symfony\Component\HttpClient\Response\AsyncResponse::toArray
    vendor/async-aws/core/src/Response.php:328 AsyncAws\Core\Response::toArray
    vendor/async-aws/dynamo-db/src/Result/QueryOutput.php:144 AsyncAws\DynamoDb\Result\QueryOutput::populateResult
    vendor/async-aws/core/src/Result.php:133 AsyncAws\Core\Result::initialize
    vendor/async-aws/dynamo-db/src/Result/QueryOutput.php:66 AsyncAws\DynamoDb\Result\QueryOutput::getCount

I think behind the scenes AWS is returning an HTTP status code of 400.

GrahamCampbell avatar Apr 28 '22 23:04 GrahamCampbell

Would you mind trying this patch @GrahamCampbell

diff --git a/src/Symfony/Component/HttpClient/RetryableHttpClient.php b/src/Symfony/Component/HttpClient/RetryableHttpClient.php
index 4df466f4ce..7aed8580b6 100644
--- a/src/Symfony/Component/HttpClient/RetryableHttpClient.php
+++ b/src/Symfony/Component/HttpClient/RetryableHttpClient.php
@@ -118,6 +118,8 @@ class RetryableHttpClient implements HttpClientInterface, ResetInterface
 
             $delay = $this->getDelayFromHeader($context->getHeaders()) ?? $this->strategy->getDelay($context, !$exception && $chunk->isLast() ? $content : null, $exception);
             ++$retryCount;
+            $content = '';
+            $firstChunk = null;
 
             $this->logger->info('Try #{count} after {delay}ms'.($exception ? ': '.$exception->getMessage() : ', status code: '.$context->getStatusCode()), [
                 'count' => $retryCount,

It might explain the cannot yield more than one "isFirst()" chunk error (1st chunk is not reseted between retries)

jderusse avatar May 17 '22 23:05 jderusse

Did you have time to try my patch @GrahamCampbell ? If it works, I'll be happy to push the patch in synfony/symfony

jderusse avatar May 23 '22 12:05 jderusse

I will try to make time this week.

GrahamCampbell avatar May 23 '22 12:05 GrahamCampbell

I figured out the issue (@jderusse's fix was correct). Will be fixed by https://github.com/symfony/symfony/pull/47990

nicolas-grekas avatar Oct 25 '22 16:10 nicolas-grekas

Thanks! Sorry I never got around to this. 🤹

GrahamCampbell avatar Oct 25 '22 16:10 GrahamCampbell

ohhhhh yes! this one was very hard to fix!

jderusse avatar Oct 25 '22 17:10 jderusse