Matt Brundage

Blog

Archive for August 2011

Stale cache mitigation with query string automation!

So you've been a good little developer and define expires headers for page assets such as CSS, JS, and images. Let's say you specify that the caching of CSS files expires one week after initial access. But if you modify a CSS file, your visitors could potentially load stale cache for up to one week.

One solution is to rename the file. For example, main.css would become main.2011-08-12.css This will effectively create a unique cached version of the CSS file. But this solution could get cumbersome with frequent updates, or with disparate references to the asset. A second solution is to add a query string to any references to the asset, for instance, main.css?2011-08-12. Proxy servers will treat this reference as a dynamic file and will likely not cache it. Browsers will treat this as if it were a unique file and re-cache it.

A refinement to our second solution is to automatically add a query string to references to the asset, but only when it's necessary to do so. The following function, assetQueryString(), does just that. It takes two arguments:

  1. The reference to the page asset
  2. (optional) Maximum file age (in days) in which the query string will be appended

The function determines the file modification date and determines whether or not to append a query string, and for how long. The value of the query string, conveniently, is the last modification date of the file. Or, more accurately, it's the number of days between the Unix Epoch and the last modification date.

PHP

function assetQueryString ($filePath, $maxAge = 7) {
	$today = intval(strtotime(date("Y-m-d")) / 86400);
	$fileDate = intval(strtotime(date("Y-m-d", filemtime($_SERVER['DOCUMENT_ROOT'].$filePath))) / 86400);
	$days = $today - $fileDate;

	if ($days <= $maxAge) {
		$filePath .= "?".$fileDate;
	}
	echo $filePath;
}

// usage:
<link rel="stylesheet" href="<?php assetQueryString("/templates/style/main.css"); ?>">

ColdFusion

<cffunction name="assetQueryString" returntype="string" output="FALSE">
	<cfargument name="filePath" type="string" required="TRUE" />
	<cfargument name="maxAge" type="numeric" default="7" required="FALSE" />

	<cfparam name="Variables.unixFileDate" default="0" />
	<cfset Variables.expandedFilePath = expandPath(filePath) />
	<cfset Variables.unixEpoch = CreateDate(1970,1,1) />

	<cfif fileExists(Variables.expandedFilePath)>
		<!--- determine last modified date --->
		<cfset Variables.unixFileDate = DateDiff("d", Variables.unixEpoch, getFileInfo(Variables.expandedFilePath).lastmodified) />

		<!--- determine today's date --->
		<cfset Variables.unixTodaysDate = DateDiff("d", Variables.unixEpoch, Now()) />

		<!--- append unique query string to file path if the file was recently modified --->
		<cfif Variables.unixTodaysDate - Variables.unixFileDate LTE maxAge>
			<cfset filepath &= "?" & Variables.unixFileDate />
		</cfif>
	</cfif>

	<cfreturn filepath />
</cffunction>

<!--- usage: --->
<link rel="stylesheet" href="<cfoutput>#assetQueryString("/templates/style/main.css")#</cfoutput>">

Sisyphus: rock and roll

Sometimes I feel as if the act of listening to my music collection is becoming a Sisyphean task. Let me explain:

Since I began my digital music collection in earnest in 2005, I've amassed close to 14,000 tracks, most of which I've simply ripped from my CD collection or downloaded from podcasts. How much music is this, exactly? Over 36 continuous days. If I were to average one hour of listening per day, it would take me close to two and a half years to traverse through my entire collection without repeating a single track.

While this is infinitely cool, it also presents a problem, as my "oldest" tracks were last heard as far back as May 2009. And this queue of old tracks keeps growing ever larger, as I keep amassing tracks but don't really increase my average listening time per day. Quality songs are getting lost in the shuffle, so to speak. It's easy to forget about them if they only come around once every two or three years. At the same time, new or unknown artists have it equally bad, as they have scant time to impress me with their music.

My only hope? Weeding. I rarely weed, as I understand all too well my penchant for rediscovering lost music.