1313#include < aws/core/utils/HashingUtils.h>
1414#include < aws/core/utils/logging/ErrorMacros.h>
1515
16+ #include < Poco/Net/NetException.h>
17+
1618#include < IO/S3Common.h>
1719#include < IO/S3/Requests.h>
1820#include < IO/S3/PocoHTTPClientFactory.h>
2325
2426#include < Common/logger_useful.h>
2527
28+ namespace ProfileEvents
29+ {
30+ extern const Event S3WriteRequestsErrors;
31+ extern const Event S3ReadRequestsErrors;
32+
33+ extern const Event DiskS3WriteRequestsErrors;
34+ extern const Event DiskS3ReadRequestsErrors;
35+ }
36+
2637namespace DB
2738{
2839
@@ -346,12 +357,14 @@ Model::HeadObjectOutcome Client::HeadObject(const HeadObjectRequest & request) c
346357
347358Model::ListObjectsV2Outcome Client::ListObjectsV2 (const ListObjectsV2Request & request) const
348359{
349- return doRequest (request, [this ](const Model::ListObjectsV2Request & req) { return ListObjectsV2 (req); });
360+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ true >(
361+ request, [this ](const Model::ListObjectsV2Request & req) { return ListObjectsV2 (req); });
350362}
351363
352364Model::ListObjectsOutcome Client::ListObjects (const ListObjectsRequest & request) const
353365{
354- return doRequest (request, [this ](const Model::ListObjectsRequest & req) { return ListObjects (req); });
366+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ true >(
367+ request, [this ](const Model::ListObjectsRequest & req) { return ListObjects (req); });
355368}
356369
357370Model::GetObjectOutcome Client::GetObject (const GetObjectRequest & request) const
@@ -361,19 +374,19 @@ Model::GetObjectOutcome Client::GetObject(const GetObjectRequest & request) cons
361374
362375Model::AbortMultipartUploadOutcome Client::AbortMultipartUpload (const AbortMultipartUploadRequest & request) const
363376{
364- return doRequest (
377+ return doRequestWithRetryNetworkErrors< /* IsReadMethod */ false > (
365378 request, [this ](const Model::AbortMultipartUploadRequest & req) { return AbortMultipartUpload (req); });
366379}
367380
368381Model::CreateMultipartUploadOutcome Client::CreateMultipartUpload (const CreateMultipartUploadRequest & request) const
369382{
370- return doRequest (
383+ return doRequestWithRetryNetworkErrors< /* IsReadMethod */ false > (
371384 request, [this ](const Model::CreateMultipartUploadRequest & req) { return CreateMultipartUpload (req); });
372385}
373386
374387Model::CompleteMultipartUploadOutcome Client::CompleteMultipartUpload (const CompleteMultipartUploadRequest & request) const
375388{
376- auto outcome = doRequest (
389+ auto outcome = doRequestWithRetryNetworkErrors< /* IsReadMethod */ false > (
377390 request, [this ](const Model::CompleteMultipartUploadRequest & req) { return CompleteMultipartUpload (req); });
378391
379392 if (!outcome.IsSuccess () || provider_type != ProviderType::GCS)
@@ -403,32 +416,38 @@ Model::CompleteMultipartUploadOutcome Client::CompleteMultipartUpload(const Comp
403416
404417Model::CopyObjectOutcome Client::CopyObject (const CopyObjectRequest & request) const
405418{
406- return doRequest (request, [this ](const Model::CopyObjectRequest & req) { return CopyObject (req); });
419+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
420+ request, [this ](const Model::CopyObjectRequest & req) { return CopyObject (req); });
407421}
408422
409423Model::PutObjectOutcome Client::PutObject (const PutObjectRequest & request) const
410424{
411- return doRequest (request, [this ](const Model::PutObjectRequest & req) { return PutObject (req); });
425+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
426+ request, [this ](const Model::PutObjectRequest & req) { return PutObject (req); });
412427}
413428
414429Model::UploadPartOutcome Client::UploadPart (const UploadPartRequest & request) const
415430{
416- return doRequest (request, [this ](const Model::UploadPartRequest & req) { return UploadPart (req); });
431+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
432+ request, [this ](const Model::UploadPartRequest & req) { return UploadPart (req); });
417433}
418434
419435Model::UploadPartCopyOutcome Client::UploadPartCopy (const UploadPartCopyRequest & request) const
420436{
421- return doRequest (request, [this ](const Model::UploadPartCopyRequest & req) { return UploadPartCopy (req); });
437+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
438+ request, [this ](const Model::UploadPartCopyRequest & req) { return UploadPartCopy (req); });
422439}
423440
424441Model::DeleteObjectOutcome Client::DeleteObject (const DeleteObjectRequest & request) const
425442{
426- return doRequest (request, [this ](const Model::DeleteObjectRequest & req) { return DeleteObject (req); });
443+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
444+ request, [this ](const Model::DeleteObjectRequest & req) { return DeleteObject (req); });
427445}
428446
429447Model::DeleteObjectsOutcome Client::DeleteObjects (const DeleteObjectsRequest & request) const
430448{
431- return doRequest (request, [this ](const Model::DeleteObjectsRequest & req) { return DeleteObjects (req); });
449+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
450+ request, [this ](const Model::DeleteObjectsRequest & req) { return DeleteObjects (req); });
432451}
433452
434453Client::ComposeObjectOutcome Client::ComposeObject (const ComposeObjectRequest & request) const
@@ -457,7 +476,8 @@ Client::ComposeObjectOutcome Client::ComposeObject(const ComposeObjectRequest &
457476 return ComposeObjectOutcome (MakeRequest (req, endpointResolutionOutcome.GetResult (), Aws::Http::HttpMethod::HTTP_PUT));
458477 };
459478
460- return doRequest (request, request_fn);
479+ return doRequestWithRetryNetworkErrors</* IsReadMethod*/ false >(
480+ request, request_fn);
461481}
462482
463483template <typename RequestType, typename RequestFn>
@@ -538,6 +558,65 @@ Client::doRequest(const RequestType & request, RequestFn request_fn) const
538558 throw Exception (ErrorCodes::TOO_MANY_REDIRECTS, " Too many redirects" );
539559}
540560
561+ template <bool IsReadMethod, typename RequestType, typename RequestFn>
562+ std::invoke_result_t <RequestFn, RequestType>
563+ Client::doRequestWithRetryNetworkErrors (const RequestType & request, RequestFn request_fn) const
564+ {
565+ auto with_retries = [this , request_fn_ = std::move (request_fn)] (const RequestType & request_)
566+ {
567+ chassert (client_configuration.retryStrategy );
568+ const Int64 max_attempts = client_configuration.retryStrategy ->GetMaxAttempts ();
569+ std::exception_ptr last_exception = nullptr ;
570+ for (Int64 attempt_no = 0 ; attempt_no < max_attempts; ++attempt_no)
571+ {
572+ try
573+ {
574+ // / S3 does retries network errors actually.
575+ // / But it is matter when errors occur.
576+ // / This code retries a specific case when
577+ // / network error happens when XML document is being read from the response body.
578+ // / Hence, the response body is a stream, network errors are possible at reading.
579+ // / S3 doesn't retry them.
580+
581+ // / Not all requests can be retried in that way.
582+ // / Requests that read out response body to build the result are possible to retry.
583+ // / Requests that expose the response stream as an answer are not retried with that code. E.g. GetObject.
584+ return request_fn_ (request_);
585+ }
586+ catch (Poco::Net::ConnectionResetException &)
587+ {
588+
589+ if constexpr (IsReadMethod)
590+ {
591+ if (client_configuration.for_disk_s3 )
592+ ProfileEvents::increment (ProfileEvents::DiskS3ReadRequestsErrors);
593+ else
594+ ProfileEvents::increment (ProfileEvents::S3ReadRequestsErrors);
595+ }
596+ else
597+ {
598+ if (client_configuration.for_disk_s3 )
599+ ProfileEvents::increment (ProfileEvents::DiskS3WriteRequestsErrors);
600+ else
601+ ProfileEvents::increment (ProfileEvents::S3WriteRequestsErrors);
602+ }
603+
604+ tryLogCurrentException (log, " Will retry" );
605+ last_exception = std::current_exception ();
606+
607+ auto error = Aws::Client::AWSError<Aws::Client::CoreErrors>(Aws::Client::CoreErrors::NETWORK_CONNECTION, /* retry*/ true );
608+ client_configuration.retryStrategy ->CalculateDelayBeforeNextRetry (error, attempt_no);
609+ continue ;
610+ }
611+ }
612+
613+ chassert (last_exception);
614+ std::rethrow_exception (last_exception);
615+ };
616+
617+ return doRequest (request, with_retries);
618+ }
619+
541620bool Client::supportsMultiPartCopy () const
542621{
543622 return provider_type != ProviderType::GCS;
0 commit comments