Speeding Up CFDirectory in Coldfusion

In which I tangle with a poorly-programmed <cfdirectory> implementation and win.

TL;DR: Don't use <cfdirectory>, it's slow! But if you have to, use listinfo="name" so coldfusion only returns the file names, not other unnecessary attributes.

We were having high CPU load (and frequent alerts) on an older server at work this week, so I took a look at what I suspected was the culprit - a particularly nasty piece of code I'd attempted to speed up before by wrapping a <cache> tag around it.

The Problem

Each database page on the website had related images stored in a /photos directory, and the previous programmer had used Coldfusion's cfdirectory tag to pull out ALL files from this directory for reference / displaying on the page. Next, a query-of-queries was used to filter these filenames to match the current page before they were printed out into HTML.

<cfdirectory
  action="list"
  directory="#request.filepath#\photos\"
  name="images"
  />
<cfquery name="getimages" dbtype="query">
    select * from images where name like '#variables.page_id#%'
</cfquery>

These pages took over FOUR seconds to load without caching. I wasn't sure of the exact timeline, but my hypothesis was that any bot crawling the site - Googlebot, Bing, etc - would trigger enough of these super-slow four-second requests for Coldfusion to hang up and/or crash.

The Solution

I'd been aware of the poor performance of this before, and attempted wrapping a <cfcache> around the issue before. This worked "fine", I suppose, but it was only cached for six hours and it was still slow for newer pages.

Use ListInfo="Name"

This is a "duh" performance improvement. Ben Nadel says:

Listing only the name column can have significant performance increases and should be used whenever you don't need the rest of the information returned in CFDirectory.

I quickly added listinfo="name" and checked - load time was now around 1 second. Woah, what a difference!

Prefer Filter="#var#" Over a Query-Of-Queries

Mike Sprague had passed this on to me a while ago - don't use query-of-queries! I was curious to see if cfdirectory's filter attribute would be faster or slower than this query - my suspicion was it would be slower. I was happy to be proven wrong!

<cfquery name="getimages" dbtype="query">
Select * from dirimages where name like '#variables.page_id#%'
</cfquery>

The Result

The final <cfdirectory> looks like

<cfdirectory
    action="list"
    directory="#request.filepath#\photos\"
    name="dirimages"
    listinfo="name"
    type="file"
    filter="#variables.page_id#*.jpg"
    />

The page now renders in ~400ms. Woop woop! This should make a huge difference in CPU load, server performance, etc.

April 6, 2018

« Beginning a phone call - TIL: How to use add_filter() in Wordpress »