Apr 14, 2011

Caching JSON Requests

ShopWarrens.com is a retail website I've been developing for a couple of months.  The latest version was designed specifically with speed in mind.  We want customers to be able to find exactly what they are looking for quickly and intuitively.

The site is being served from a shared environment.  While its very cost effective, the database performance is less than ideal.  We've done our best to keep db processing to a minimum.  But with every request for a product list comes yet another hit against the database... and they were sometimes very costly hits.

I'd eventually had it with the intermitent performance issues and said to myself - 'there's got to be a way to make these requests load faster for ALL visitors'.  I want peoples minds to be BLOWN away at how fast the web experience is.

Then it hit me... all I'm returning to the browser is a JSON string.  What if that JSON string was cached in 6hr intervals?  Then the product lists would load INSTANTLY... no waiting for db processing...  the server can respond as fast as possible with the appropriate JSON response.

My solution was build in ColdFusion (hangs head in embarrassment) but the same could be applied to any server language.

Here's how it works...

Here's my server directory structure:
   ajax/products.get.cfm
   ajax/products.list.cfm
   ajax/_cache.cfm
   ajax/_cache  (this is a dir)

In each file that processes a request and returns JSON, I include _cache.cfm at the top of that file.  At the very bottom of each file, I also change my <cfoutput> or WriteOutput() to a custom function called CacheOutput(JSON_STRING_HERE).

Here's what _cache.cfm does. (remember - its being included before anything else)

  1. Scan the cache directory for files older than 6hrs and deletes them.
  2. Creates a unique identifier for the given request by lumping the current server file that's being processed with any GET or POST variables.
  3. It then compresses the potentially large unique identifier into an encrypted hash string. This new string becomes the file name for the cache content.
  4. Does a file already exist in the cache with that unique identifier?
    - if YES, serve that content up and ABORT page execution
    - if NO, let the current page on the server finish processing the request
  5. Lastly, it defines the function that will be used on the original page processing the request to output AND cache the result.

Here's the code:

<cfset __cacheMinutes = 60*6>

<!---------------------------------->
<!--- keep the cache files clean --->
<!---------------------------------->

<!--- get all files in the cache ----->
<cfdirectory directory="#ExpandPath('.\')#/_cache" name="qDir" sort="datelastmodified ASC" type="file" />

<!--- determine the expiry date --->
<cfset cacheExpiryDateTime = DateAdd('n',-__cacheMinutes,Now())>
<cfset timestr = '#DateFormat(cacheExpiryDateTime,"yyyy-mm-dd")# #TimeFormat(cacheExpiryDateTime,"HH:mm:ss")#'>

<!--- filter the files by the expiry date --->
<cfquery name="qDir_filtered" dbtype="query">
 SELECT *
    FROM qDir
    WHERE datelastmodified < '#Evaluate("timestr")#' 
</cfquery>

<!--- delete old files --->
<cfloop query="qDir_filtered">
 <cffile action="delete" file="#ExpandPath('.\')#/_cache/#name#" />
</cfloop>


<!-------------------------------->
<!--- build a unique requestid --->
<!-------------------------------->

<!--- start the request id with the server page being executed --->
<cfset __requestID = '#CGI.PATH_INFO#'>

<!--- add url variables to the unique requestid --->
<cfset StructDelete(Url,'FieldNames')>
<cfloop collection="#Url#" item="i">
 <cfset __requestID = '#__requestID#_#i#-#Url[i]#'>
</cfloop>

<!--- add form variables to the unique requestid --->
<cfset StructDelete(Form,'FieldNames')>
<cfloop collection="#Form#" item="i">
 <cfset __requestID = '#__requestID#_#i#-#Form[i]#'>
</cfloop>


<!-------------------------------->
<!--- build a hashed filename ---->
<!-------------------------------->
<cfset __cacheID = Hash(__requestID)>
<cfset __cacheFile = '#ExpandPath('.\')#/_cache/#__cacheID#.bk'>


<!-------------------------------->
<!--- does this cache exist? ----->
<!-------------------------------->
<cfif FileExists(__cacheFile)>
 <cffile action="read" file="#__cacheFile#" variable="CachedContent" />
    <cfoutput>#CachedContent#</cfoutput>
    <cfabort>
</cfif>


<!------------------------------------------------>
<!--- build the function that caches content ----->
<!------------------------------------------------>
<cffunction name="CacheOutput" output="yes">
 <cfargument name="OutputString">
    
    <!--- output the result as fast as possible --->
    <cfoutput>#OutputString#</cfoutput>
    
    <!--- write the output to the cache --->
    <cffile action="write" file="#__cacheFile#" nameconflict="overwrite" output="#OutputString#" />
</cffunction>

Sep 1, 2009

The IE8 Speed Fix!

I hate Microsoft.

Its been a solid few months since I've been using IE8 (only for website testing, not everyday browsing). I noticed that every so often (more often then not), one of the tabs in IE would just hang. It wouldn't load the page. I'd open a new tab to fix it... and it wouldn't load either. I'd pop into Google Chrome and the same URL would load fine. This would also happen in IE8 pop-up windows.

I finally decided to do some research on the issue, and to my surprise, there's actually a solution. So far, it seems to have worked for me. Hopefully it'll do the same for you:

1) Open Command Prompt (Start > Run > Type: 'cmd')
2) Type: 'regsvr32 actxprxy.dll'
3) You should see an alert box pop up saying something was successful.
4) Reboot.
5) Enjoy a less buggy IE8 experience.

I'm running XP and this solution works great. Not sure if this solution will work for Vista users or not. If it doesn't, the next best thing would be to find the IE8 installer and reinstalling IE8 completely.

Apr 25, 2009

Poster Design as Inspiration

I'm always on the lookout for different sources of inspiration to help keep my designs fresh. This book is pretty awesome. Its called 'New Masters of Poster Design' and is a collaboration of poster design from around the world.

First off, the designs are out of this world. Some of the designs have me wondering, "Woah - what client would ever say 'Yes!' to that!?! Its too crazy!" Its obvious that the designers hired to do this work were given 100% creative freedom.

Secondly, the creative influences in this book are global... and it shows. There's so much variety in the designs. The book exposes you to creative genius that you'd, otherwise, be hard pressed to experience.

Lastly, by nature of poster design, its a completely different format then web or corporate collateral design, at least in this book. Much of the strange formatting used in the posters (which work well for posters) can be used to influence new ideas in formatting web designs or corporate collateral.

All in all, its a great purchase. I purchased it just over 8 months ago and I still take a peak through it once or twice a month as a creative refresher.

Pick it up at Indigo - http://www.chapters.indigo.ca/books/New-Masters-Poster-Design-Poster-John-Foster/9781592532223-item.html.

May 17, 2008

ASCII-INSANITY!

Star Wars - done using only ASCII characters!
http://www.asciimation.co.nz/

Apr 22, 2008

300% Cotton - 100% Inspiration

A good friend of mine, Private Dave McDonald, hooked me up with a gift card to Indigo. I'm not a book guy - at all. But the gift card gave me an opportunity to do something I should have done years ago. I needed to expose myself to some new, fresh and creative design influences.

There's a difference between a designer and a creative designer - and I want to be the later. A designer may know how to pair colors or how to layout a page. They may also know how to pump out lifeless templates and designs. But a creative designer should have the ability to CREATE - hence CREATive. Each creation should be a unique manifestation of the design's purpose and should in turn induce emotion - exactly the same way music does. Although each design may contain a designer's common fingerprint elements, it should, for the most part, be a unique creation. And in order to be creative, you need inspiration.

For me, inspiration has come in the form of this book. I randomly grabbed this book off the shelf (one of many) at Indigo. Hats off to Helen Walters on publishing an excellent collection of graphic art. It's entitled 300% Cotton and features 934 color illustrations from some of the most creative graphic-tee designers from across the globe.

I'm making a commitment to pick up books similar to this on a regular basis. This one book alone has freed my creativity from the dreaded "plateau".

300% Cotton at Indigo.Chapters.ca