Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
The Azure SDK for C++ uses an HTTP pipeline architecture to process HTTP requests to Azure services. This document explains how HTTP pipelines work, how retry policies are implemented, and how you can customize them for your application needs.
HTTP pipeline architecture
What is an HTTP pipeline?
An HTTP pipeline is a stack of HTTP policies that get applied sequentially to process HTTP requests and responses. Each client in the Azure SDK has its own HTTP pipeline. The policies in the pipeline shape how HTTP requests are handled, including operations like:
- Adding authentication headers
- Request/response logging
- Retry logic for failed requests
- Telemetry collection
- Transport handling (actually sending the HTTP request)
The pipeline is split into two main parts:
- Per-call policies - Execute once per API operation
- Per-retry policies - Execute for each retry attempt
This structure ensures that appropriate policies (like authentication) only execute once per operation, while others (like logging) execute for each retry attempt.
Policy ordering
A typical HTTP pipeline in the Azure SDK for C++ includes the following policies in order:
- Telemetry Policy (per-call) - Adds Azure SDK telemetry information
- Request ID Policy (per-call) - Ensures each request has a unique ID
- Service-specific Per-Call Policies - Custom policies specific to a service
- Retry Policy (per-call) - Implements retry logic
- Service-specific Per-Retry Policies - Custom policies specific to a service that run on each retry
- Request Activity Policy (per-retry) - Manages distributed tracing
- Log Policy (per-retry) - Handles logging requests and responses
- Transport Policy (per-retry) - Handles the actual sending of the HTTP request
Retry policy
How retries work
The retry policy is designed to handle transient failures that may occur when making HTTP requests to Azure services. When a request fails due to a transient error, the retry policy will:
- Determine if the failure is retryable
- Calculate an appropriate delay
- Wait for that delay
- Retry the request
The policy supports retrying on both transport-level failures (network issues) and certain HTTP status codes.
Default retry behavior
By default, the retry policy is configured with:
- Maximum of three retry attempts
- Initial retry delay of 800 milliseconds
- Maximum retry delay of 60 seconds
- Retryable status codes: 408, 429, 500, 502, 503, 504
The retry delay uses an exponential backoff strategy with jitter:
- First retry: ~800 ms
- Second retry: ~1,600 ms
- Third retry: ~3,200 ms
- And so on, until max retry delay is reached
When retries happen
The retry policy attempts to retry a request in the following scenarios:
Transport failures:
- Network connectivity issues
- Connection time-outs
- DNS (Domain Name System) resolution failures
HTTP status codes:
- 408 (Request time-out)
- 429 (Too Many Requests)
- 500 (Internal Server Error)
- 502 (Bad Gateway)
- 503 (Service Unavailable)
- 504 (Gateway time-out)
Service-specific retry logic:
- Some services like Storage implement specialized retry logic for failover scenarios
Customizing retry behavior
You can customize the retry behavior when creating a client by modifying the RetryOptions
in the client options.
Example: customizing retry options
#include <azure/storage/blobs.hpp>
int main()
{
// Create client options
Azure::Storage::Blobs::BlobClientOptions options;
// Modify retry options
options.Retry.MaxRetries = 5; // Increase max retries
options.Retry.RetryDelay = std::chrono::milliseconds(1000); // Set initial retry delay to 1 second
options.Retry.MaxRetryDelay = std::chrono::seconds(30); // Cap maximum retry delay at 30 seconds
// Add a custom status code to retry on
options.Retry.StatusCodes.insert(Azure::Core::Http::HttpStatusCode::Forbidden); // Retry on 403 errors
// Create the client with custom retry options
auto blobClient = Azure::Storage::Blobs::BlobClient::CreateFromConnectionString(
connectionString,
containerName,
blobName,
options);
// Use the client...
}
Adding custom policies
You can add custom policies to the HTTP pipeline to implement specialized behavior:
Adding a per-operation policy
Per-operation policies are called once per API operation, regardless of how many retries are needed:
class MyCustomPolicy final : public Azure::Core::Http::Policies::HttpPolicy {
public:
~MyCustomPolicy() override = default;
std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<MyCustomPolicy>(*this);
}
std::unique_ptr<Azure::Core::Http::RawResponse> Send(
Azure::Core::Http::Request& request,
Azure::Core::Http::Policies::NextHttpPolicy nextPolicy,
Azure::Core::Context const& context) const override
{
// Custom logic before the request
auto response = nextPolicy.Send(request, context);
// Custom logic after the response
return response;
}
};
// Adding the policy to client options
Azure::Storage::Blobs::BlobClientOptions options;
options.PerOperationPolicies.emplace_back(std::make_unique<MyCustomPolicy>());
Adding a per-retry policy
Per-retry policies are called for each retry attempt:
// Similar implementation to above, but add to PerRetryPolicies
options.PerRetryPolicies.emplace_back(std::make_unique<MyCustomRetryPolicy>());
Handling secondary endpoints
Some Azure services like Storage support secondary endpoints for high availability. The SDK includes support for automatic failover to secondary endpoints:
Azure::Storage::Blobs::BlobClientOptions options;
// Configure secondary endpoint for Storage
std::string primaryUrl = blobClient.GetUrl();
std::string secondaryUrl = InferSecondaryUrl(primaryUrl); // Your logic to determine secondary URL
std::string secondaryHost = Azure::Core::Url(secondaryUrl).GetHost();
options.SecondaryHostForRetryReads = secondaryHost;
Logging retry attempts
The HTTP pipeline includes built-in logging for retry attempts. You can configure the logging level to see information about retries:
// Set log level to see retry information
Azure::Core::Diagnostics::Logger::SetLevel(Azure::Core::Diagnostics::Logger::Level::Informational);
// Set a custom log listener to capture logs
Azure::Core::Diagnostics::Logger::SetListener([](auto level, auto message) {
std::cout << "Log [" << static_cast<int>(level) << "]: " << message << std::endl;
});
When a retry occurs, log entries appear like:
- "HTTP Transport error: [error details]"
- "HTTP Retry attempt #1 will be made in 800 ms."
- "HTTP status code 503 will be retried."
Best practices
Use default retry settings when possible
- The default settings are tuned for most scenarios and include best practices like exponential backoff
Be careful with non-idempotent operations
- Consider limiting retries for operations that aren't safe to retry (like nonidempotent POST requests)
Consider circuit breaker patterns
- For high-volume applications, implement circuit breaker patterns to prevent overwhelming services that are responding with errors
Test retry scenarios
- Test your application's behavior when retries occur to ensure proper handling
Monitor retry telemetry
- High retry rates might indicate underlying issues that should be addressed
Advanced: pipeline internals
The HTTP pipeline is implemented in the Azure::Core::Http::_internal::HttpPipeline
class, which manages the sequence of policy execution. When a request is made, the pipeline:
- Starts with the first policy in the pipeline
- Each policy processes the request and then passes it to the next policy
- The last policy is typically the transport policy, which actually sends the request
- The response then flows back through the policies in reverse order
The retry policy is special in that it can repeat the entire sequence of policies that come after it in the pipeline.
Troubleshooting
If you're experiencing issues with retries:
Enable informational logging
- Set the
AZURE_LOG_LEVEL
environment variable toInformational
to see retry attempts
- Set the
Check for transport errors
- Network issues often manifest as transport exceptions
Verify service health
- Persistent 500-level errors may indicate an Azure service issue
Review request IDs
- Each request has a unique ID that can be used when working with Azure Support
Check timeout settings
- Ensure your application's time-outs are compatible with the retry policy