Project: September–October 2026
A complex website can experience serious performance problems even when its server has sufficient computing resources. Sometimes the bottleneck isn’t hardware or database performance, but the way different parts of the application interact.
During a recent infrastructure modernization project, I investigated a series of performance and reliability issues affecting a large Drupal-based website. The work involved tracing problems across the application, web server, PHP runtime, databases, external APIs, and caching systems.
One particularly interesting issue revealed how an application could effectively deadlock itself through an unexpected interaction between internal HTTP requests and PHP-FPM worker limits.
The challenge
The website was an established Drupal 10 platform supporting a substantial collection of content, search functionality, external data integrations, and dynamically generated pages.
Like many mature applications, it had accumulated several layers of functionality that depended on one another:
- Drupal application logic and custom modules
- PHP-FPM and Apache
- Relational databases and Apache Solr search
- External APIs supplying dynamically loaded content
- Scheduled maintenance and content-processing tasks
- Security and caching systems
The project included migrating the application to new infrastructure, improving performance, and investigating intermittent slowdowns and outages.
Some requests were taking tens of seconds to complete. At times, the application exhausted its available PHP workers, causing subsequent requests to queue, time out, or return HTTP 504 errors.
Simply increasing server resources wasn’t going to be a sufficient long-term solution.
Finding the bottleneck
My investigation began with the PHP-FPM configuration, Apache error logs, database behaviour, and Drupal’s application logs.
A recurring symptom was exhaustion of the PHP-FPM worker pool. When all available workers were occupied, new requests could not be processed until existing workers completed.
Increasing the number of workers helped provide additional capacity, but it didn’t explain why some requests were taking so long.
The more important question was: what were those workers waiting for?
An application waiting on itself
Further investigation revealed an unusual architectural problem.
Part of the Drupal application retrieved information by making HTTP requests back to endpoints on the same website.
This pattern might appear harmless. A page needs some data, so it calls an API endpoint and incorporates the response.
But in this case, both the original request and the internal API request depended on the same limited PHP-FPM worker pool.
Under sufficient concurrency, the application could enter a state where every available worker was processing an outer request that was waiting for another request to complete.
Those inner requests couldn’t execute because no workers were available.
The result was a form of resource deadlock:
- A visitor requests a page.
- Drupal begins rendering the page and makes an HTTP request to another endpoint on the same server.
- The original PHP worker waits for the response.
- Additional visitor requests consume the remaining workers.
- Eventually, all workers are waiting, while the internal requests they depend on are queued behind them.
The system could become unresponsive without exhausting CPU or memory.
This explained why increasing worker limits alone wasn’t a reliable fix: it increased the amount of concurrent traffic needed to trigger the problem without eliminating the underlying dependency.
The solution involved changing how the application retrieved the required data, eliminating the problematic reliance on synchronous HTTP requests back into the same PHP worker pool.
Investigating external API latency
A separate class of performance problems involved requests to external services.
By enabling PHP-FPM slow-request logging and examining stack traces, I identified page requests spending substantial time waiting for responses from an external media API.
These delays had consequences beyond individual slow-loading pages. Every request blocked on an external service also occupied a PHP worker, reducing the application’s capacity to serve other visitors.
I worked on improving the loading and caching behaviour of externally sourced content, including media galleries and embedded video content.
The broader principle is straightforward: a website’s performance shouldn’t be unnecessarily dependent on the response time of a third-party service.
Appropriate caching and carefully designed data retrieval can isolate an application from external latency and reduce unnecessary requests.
Migration and infrastructure improvements
Alongside the performance investigation, I migrated the application to new server infrastructure.
The work included configuring and validating the application environment, database services, Solr search, PHP settings, scheduled tasks, and supporting infrastructure.
Database compatibility required attention during migration, as did the distinction between restoring a search index’s configuration and restoring its indexed content.
I also investigated background tasks running across the old and new environments, including duplicate scheduled jobs that could create unnecessary activity.
These are examples of the operational details that can complicate a seemingly straightforward server migration.
Successful migration requires more than transferring files and databases. It means verifying that the entire application ecosystem behaves correctly in its new environment.
Separating genuine demand from bot traffic
Another complication was substantial automated traffic.
Crawlers and bots were generating requests to expensive application endpoints, including URLs that did not correspond to valid content.
This traffic added load to an already constrained system and complicated the interpretation of website analytics.
I analyzed request patterns, investigated high-cost endpoints, and worked on caching and security-rule adjustments intended to reduce unnecessary application processing.
An important consideration was avoiding overly aggressive filtering that could interfere with legitimate visitors or search engine crawlers.
Bot mitigation, application caching, and performance engineering need to be considered together rather than as isolated concerns.
The outcome
The investigation identified several distinct sources of performance and reliability problems:
- Internal request dependencies could exhaust the PHP-FPM worker pool and produce widespread timeouts.
- Slow external API calls could tie up workers and affect otherwise unrelated pages.
- Application caching offered opportunities to reduce repeated work and expensive requests.
- Automated traffic contributed unnecessary load and required targeted handling.
- Infrastructure migration exposed configuration, search-index, and background-task dependencies requiring careful validation.
The work addressed these problems through application-level changes, caching improvements, server configuration, traffic analysis, and infrastructure maintenance.
Although these changes addressed identified failure modes, I don’t have a consolidated before-and-after benchmark suitable for publishing a quantitative performance improvement.
The most significant result was a clearer understanding of the application’s behaviour and the correction of architectural problems that couldn’t be solved simply by allocating more hardware.
Lessons from the project
This project illustrates a recurring theme in software engineering: performance problems are often symptoms of unexpected interactions between otherwise reasonable components.
A database timeout might be caused by exhausted application workers. A slow page might be waiting on a third-party API. Adding capacity might postpone an outage without fixing the underlying design problem.
Diagnosing these situations requires working across the whole system rather than focusing on a single technology.
It’s also an example of why maintaining complex business software requires a different set of skills from simply building new features. Established applications accumulate integrations, dependencies, and operational assumptions that only become visible under particular conditions.
Understanding those systems—and making them more reliable—is an important part of the work I do through Entra Consulting.
This case study describes work completed for an established organizational website. Identifying client details have been omitted.