Production failures: the things that page you · Chapter 4 of 24 · Reliability · new chapter every Friday morning

By the end of this chapter: Trace requests queueing on a resource nobody is watching.

The problem

You are the on-call engineer for a high-traffic e-commerce platform, and it's a busy Friday evening. Your pager goes off with a notification that the website is responding slowly to user requests. As you start investigating, you notice that the database connection pool is exhausted, causing a backlog of requests to queue up. The symptom is a increasing latency in the application, with some requests taking over 10 seconds to complete. The cost is a significant increase in user complaints and a potential loss of sales. Your team has been monitoring the system, but nobody has been watching the connection pool size, and now it's too late.

The database connection pool is a critical resource that allows your application to interact with the database efficiently. When the pool is exhausted, it means that all available connections are in use, and new requests are being queued. This can happen when the application is experiencing a high volume of traffic or when there are long-running database queries that are holding onto connections for too long. In your case, the connection pool size is set to 100, and the current usage is at 100%, with over 500 requests waiting in the queue.

Before you start

To diagnose and fix the connection pool exhaustion issue, you will need to have the following prerequisites in place:

  • A working knowledge of your application's architecture and database interactions
  • Access to the application's logs and monitoring tools, such as Grafana or Prometheus
  • A terminal with the necessary credentials to connect to the database and run queries
  • The psql command-line tool installed and configured to connect to your PostgreSQL database (version 13.4 or later)
  • The pg_stat_activity view available in your database, which provides information about current database connections

To verify that you have the necessary setup, run the following command to check the current connection pool usage:

SELECT * FROM pg_stat_activity WHERE state = 'active'; 

This should return a list of currently active database connections.

Understanding connection pool exhaustion

Connection pool exhaustion occurs when all available connections in the pool are in use, and new requests are being queued. This can happen due to a variety of reasons, including:

  • Insufficient connection pool size: If the connection pool size is too small, it can become exhausted quickly, especially during periods of high traffic.
  • Long-running database queries: Queries that take a long time to complete can hold onto connections for an extended period, reducing the number of available connections in the pool.
  • Poor database indexing: Inefficient database indexing can lead to slower query performance, causing connections to be held for longer periods.

To diagnose connection pool exhaustion, you need to monitor the connection pool usage and identify the root cause of the issue.

Build it

To fix the connection pool exhaustion issue, follow these steps:

  1. Increase the connection pool size: Temporarily increase the connection pool size to alleviate the immediate pressure on the pool. This can be done by updating the max_connections parameter in your PostgreSQL configuration file or by using a connection pooling library like PgBouncer.
ALTER SYSTEM SET max_connections = 200; 
  1. Identify long-running queries: Use the pg_stat_activity view to identify long-running queries that may be holding onto connections. You can use the following query to find queries that have been running for more than 5 seconds:
SELECT * FROM pg_stat_activity WHERE state = 'active' AND query_start < now() - interval '5 seconds'; 
  1. Optimize database queries: Optimize the identified long-running queries by adding indexes, rewriting the query, or using a more efficient query algorithm.
  2. Implement connection pooling: Implement a connection pooling mechanism, such as PgBouncer, to manage database connections more efficiently and reduce the likelihood of connection pool exhaustion.

When this breaks

Connection pool exhaustion can manifest in different ways, depending on the underlying cause. Some common symptoms include:

  • Increasing latency in the application
  • Errors indicating that the connection pool is exhausted
  • Database connections being held for extended periods

To diagnose connection pool exhaustion, monitor the connection pool usage and identify the root cause of the issue. Some common causes include:

  • Insufficient connection pool size
  • Long-running database queries
  • Poor database indexing

What it costs

Implementing a larger connection pool size or optimizing database queries can have trade-offs, such as:

  • Increased memory usage: A larger connection pool size can increase memory usage, potentially leading to performance issues.
  • Increased complexity: Optimizing database queries can add complexity to the application, making it harder to maintain and debug.

When deciding whether to increase the connection pool size or optimize database queries, consider the following factors:

  • The expected traffic volume and query patterns
  • The available system resources (e.g., memory, CPU)
  • The complexity of the application and database schema

In the interview

When asked about connection pool exhaustion in an interview, a strong answer should include:

  • A clear explanation of the symptoms and causes of connection pool exhaustion
  • Examples of how to diagnose and fix the issue
  • Discussion of the trade-offs involved in increasing the connection pool size or optimizing database queries

A weak answer might focus solely on the symptoms of connection pool exhaustion without discussing the underlying causes or potential solutions.

As a junior engineer, you might be expected to explain the basics of connection pool exhaustion and how to identify the issue. As a senior engineer, you would be expected to discuss the trade-offs involved in different solutions and provide examples of how to optimize database queries and implement connection pooling mechanisms.

Your tasks

  1. Simulate connection pool exhaustion: Create a simple application that simulates connection pool exhaustion by making a large number of concurrent database requests. Measure the latency and error rates as the connection pool becomes exhausted.
  2. Optimize a long-running query: Identify a long-running query in your application and optimize it using indexing, rewriting, or a more efficient query algorithm. Measure the performance improvement and discuss the trade-offs involved.
  3. Implement connection pooling: Implement a connection pooling mechanism, such as PgBouncer, in your application. Measure the performance improvement and discuss the trade-offs involved in managing database connections.

Your tasks this week

Do the exercises above before the next chapter. Reading a tutorial and doing
one are different activities and only one of them changes what you can build.

Stuck on any of them? Say so — describe what you tried and what happened:
tell me where you got stuck. I read every one, and the questions
that come back more than twice get answered in the next chapter.

Production failures: the things that page you

Chapter 4 of 24. New chapter every Friday morning.
Next: The slow query that only appears at scale.

· The full syllabus and every chapter so far
· Subscribers also get the condensed notes for this chapter, the running
recap of everything the series has covered, and the extended guidance:
subscribe

Written by Amit Chakraborty — founding engineer and senior architect: React Native, AI and RAG systems, production architecture. Portfolio · LinkedIn · GitHub.

Need this built, reviewed or taught to your team? Get in touch or email amit@devamit.co.in. Available for senior and founding engineering roles, consulting and training, remote worldwide.