libcurl
multithreading
DNS lookup
performance issues
network programming

Using libcurl in a multithreaded environment causes VERY slow performance related to DNS lookup

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When developing applications that interact with web services, utilizing a robust library to handle HTTP requests is crucial. `libcurl` is an extensive yet flexible library that facilitates data transfers with a range of protocols, notably HTTP and HTTPS. However, developers often encounter performance issues when deploying `libcurl` in a multithreaded environment, particularly concerning DNS lookups. This article explores the underlying causes of the slow performance and offers insights into potential remedies.

Understanding the Problem

Using `libcurl` in single-threaded applications generally yields satisfactory results. However, when the library is deployed in multithreaded applications, developers often witness escalated latency, especially during DNS resolution. The root of this inefficiency can be attributed to how `libcurl` handles name resolution.

Mechanism

  1. Synchronous DNS resolution: By default, `libcurl` uses system calls such as `getaddrinfo()` which are blocking in nature. This means that in any given thread, DNS resolution must complete before moving on to retrieve or send data, introducing significant bottleneck points in multithreaded applications.
  2. Global DNS cache: If not specifically configured for thread-safety, DNS cache might be shared across threads via global variables, leading to race conditions, data corruption, or, conservatively, a sequential resolution even though multiple threads attempt concurrent lookups.

Mitigating Slow DNS Resolution

Switching to Asynchronous Resolvers

A primary solution is to employ an asynchronous DNS resolver. `libcurl` supports several third-party async resolvers, notable ones being c-ares and threaded-resolver.

  • c-ares: An established asynchronous resolver library that integrates seamlessly with `libcurl` to provide non-blocking DNS queries and event-driven DNS lookup.
  • Threaded Resolver: If asynchronous resolvers are infeasible, `libcurl`'s native threaded resolver option allows background DNS lookups, which decreases the blocking behavior seen during DNS fetches. This can be activated by enabling the `CURL_USE_LIBUV` option via build configuration.

Example Configuration

Here is a basic example of how to configure `libcurl` with c-ares resolver:

  • DNS Cache Sharing: Ensure that each thread uses its own handle or meticulously controls DNS cache share contexts.
  • Avoid Global State: Instantiate separate `CURL` handles per thread or be stringent with mutexes where necessary.
  • Testing and Profiling: Before deployment, leverage profiling tools to isolate and understand DNS lookup times in the stack, allowing you to address any persistent latencies accurately.

Course illustration
Course illustration

All Rights Reserved.