HelpCaching in the Coldfusion API
Caching in the Coldfusion API
The Sprawk API support we provide for Coldfusion is in form of a custom tag, cf_sprawk, and a Coldfusion component, sprawk.cfc. A core part of the functionality is to offer client side (client's server side rather, as opposed the the Sprawk server) caching of translated texts. If a text or a piece of a text has been translated already and has not changed since it was translated, requesting it again from the Sprawk server introduces an unneccessary lag, while retrieving it from the local cache is more or less instant.
For our built-in caching, we decided to use Ben Nadel's SimpleCache. It is a basic, easy to use caching system contained in a single Coldfusion component. It does not offer features like a max size or "first in first out" functionality, but should suffice in most situations since the gradual expiration of cached entries will eventually make the memory consumption level out.
In any caching situation, the expiration time of a cached entry is a trade-off between perfomance and accuracy. In the Sprawk context for example, if you have an expiration time of one week and you change the translation of let's say the French version of a piece of English text, it may take up to seven days before the change becomes visible on your website. While it's always possible to do a global reset of the cache if you want your change to take effect immediately, you want to avoid doing that too often, since the site's perfomance will degade until the cache is fully populated again. On the other hand, if you have an expiration time of one hour and a particular page is only requested in French every fifth hour on average, the translation will be requested from Sprawk on every page load, degrading the responsiveness of your website. It's all about finding the right balance for your unique situation.
In sprawk.cfc the expiration time is controlled by this line of code:
<!--- One day ---> <cfset this.expirationTime = createTimeSpan(1, 0, 0, 0)>
This variable is referenced every time something is inserted into the cache, unless explicitly overridden by the expirationTime attribute of the cf_sprawk tag. Since different types of pages or contents may require different expiration times, it is possible to specify the expiration time each time you invoke cf_sprawk. For example you may have a busy page where translations are often fine tuned after the "base language" version has been fixed, in which case a shorter expiration time may be desired, while more static pages where the translations rarely change can do with longer than default expiration times.
When getting or setting cache entries, you need to provide the functions a unique identifier for the entry, i.e. a key. In sprawk.cfc we use the simple function
<cffunction name="getCacheKey" access="public" returntype="string" output="false"> <cfargument name="text" type="string" required="true"> <cfreturn this.toLang & "_" & hash(text)> </cffunction>
for generating our keys. It simply concatenates the language code for the language we are currently translating into, for example "fr" for French, with a hash of the source text. Coldfusion has a built in
hash() function which accepts a string of any length and returns a unique identifier.
Though very simple, a function like this for generating the key is usually a good idea, since building the key every time it is used sooner or later inevitably leads to key mismatches breaking the caching, which can be cumbersome to troubleshoot. (We've been there!)
Accessing the SimpleCache
For accessing the instance of simpleCache, we use the function
<cffunction name="getCache" access="public" returntype="any" output="false"> <cfif (not structkeyexists(Application, "sprawkCache")) > <cfset Application.sprawkCache = createObject("component", "uidcore.simpleCache") .Init()> </cfif> <cfreturn Application.sprawkCache> </cffunction>
which returns the simpleCache instance called sprawkCache in the Application scope, after setting it up first if it doesn't exist.
SimpleCache is capable of storing any type of Coldfusion data, for example a string or a struct. While a string (the translated text) is really what we want to store, we decided to use a struct like this:
entry.in = [source text] entry.out = [translated text]
for the purpose of ease of debugging and for performing an additional check so that two different entries don't by coincidence have the same key generated (which would be extremely unlikely).
Caching the translations
The actual cache management takes place inside the
t() function, which is what cf_sprawk invokes for performing the translation. First (after making sure the to and from languages are different) it sets up the key:
<cfset key = getCacheKey(arguments.in)>
using arguments.in, which is the text to be translated. It then queries the cache to see if this entry already exists:
<cfif getCache().hasData(key) and getCache().getData(key).in eq arguments.in> ... <cfelse> ... </cfif>
If the entry is found to be cached, it is retrieved and its .out text returned:
<cfif getCache().hasData(key) and getCache().getData(key).in eq arguments.in> <cfreturn getCache().getData(key).out> <cfelse> ... </cfif>
If it's not in the cache, it's sent off for translation by the Sprawk server, and then added to the cache and finally returned:
<cfif getCache().hasData(key) and getCache().getData(key).in eq arguments.in> ... <cfelse> <!--- Get the translation from Sprawk ---> ... <!--- Save the results to the cache for future lookups ---> <cfset data = structNew()> <cfset data.in = arguments.in> <cfset data.out = cfhttp.fileContent> <cfscript> getCache().setData(key, data, now() + this.expirationTime); </cfscript> <cfreturn cfhttp.fileContent> </cfif>
cfhttp.fileContent contains the translated text after the call to the Sprawk server, and
now() + this.expirationTime is passed to the setData function to specify for how long we want the entry to remain in cache.
To sum things up, caching is a powerful concept that can improve speed greatly for a Sprawk translated website at the same time as a high level of translation accuracy is maintained. Though the examples in this guide are Coldfusion based, the general principles should apply to any software platform.
blog comments powered by Disqus