Getting multiple timeouts when running multiple concurant connections from the same user

<%- if @topic_view.topic.tags.present? %>
<%= t 'js.tagging.tags' %>: <%- @topic_view.topic.tags.each do |t| %> <%= t %> <%- end %>
<% end %>

Silverstripe Version: 4.2

I have a dashboard for a site that shows a simple list of DataObjects in the admin (not using gridfield). Each object has two images that change regularly and are generated dynamically.

The images are a bit like a graph (but different enough that I cannot use some JS based graphing software, which would be my preference).

At the moment the images are generated via a SilverStripe controller. In order to improve performance, the controller caches an image once generated and uses that if the cached image exists.

The dashboard is showing 8 DataObjects (so 16 images) and when this loads, we quite regularly get a long load time and then most (or all) of the images time out (nginx returns a 499 error).

I have checked the error logs and nothing is being raised. I have also run top and atop on the server while the images are being loaded and there is a negligible increase in CPU, RAM and HDD I/O.

I am kind of a bit stumped, I was wondering if anyone had any ideas what might be causing this issue?

I don’t think I am allowed to post the entire controller (as technically the code is owned by our client), but the code to load and output the cache is as follows:

        if ($this->isGIF()) {
            header('Content-type:image/gif');
        } else {
            header("Content-type: image/png");
        }

        // Set a cache time of 1 year
        $cache_time = 60 * 60 * 24 * 365;
        header('Cache-control: max-age=' . $cache_time);
        header('Expires: '. date(DATE_RFC1123, time() + $cache_time));

        $cached = $this->getCachedFileName();

        if (file_exists($cached)) {
            header('Content-Length: ' . filesize($cached));
            readfile($cached);
            exit;
        }