LRUCache

Package: MachII.caching.strategies
Inherits from: caching.strategies.AbstractCacheStrategy
A caching strategy which uses an LRU eviction policy.

<!--- License: Copyright 2008 GreatBizTools, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Copyright: GreatBizTools, LLC Author: Kurt Wiersma (kurt@mach-ii.com) $Id: LRUCache.cfc 1189 2008-12-02 07:27:46Z peterfarrell $ Created version: 1.6.0 Updated version: 1.6.0 Notes: Configuration parameters Size - The size of the LRU cache size. - The default setting for the LRU cache "size" is 100. - Valid numeric value only. Scope - The scope that the cache should be placed in. - The default setting for "scope" is "application". - Valid values are "application", "server" and "session". ScopeKey - The key place the cache in the choosen scope. - Optional and by default the cache will be placed in scope._MachIICache.Hash(appKey & moduleName & cacheName) - Rarely will this need to be used Using all of the default settings will result in caching 100 elements of data in the application scope. <property name="Caching" type="MachII.caching.CachingProperty"> <parameters> <!-- Naming a default cache name is not required, but required if you do not want to specify the 'name' attribute in the cache command --> <parameter name="defaultCacheName" value="default" /> <parameter name="default"> <struct> <key name="type" value="MachII.caching.strategies.LRUCache" /> <key name="size" value="100" /> <key name="scope" value="application" /> </struct> </parameter> </parameters> </property> --->

Method Summary
public void configure()

Configures the strategy.

public void flush()

Flushes the entire cache.

public any get(string key)

Gets data from the cache by key. Returns null if the key is not in the cache.

public struct getConfigurationData()

Gets pretty configuration data for this caching strategy.

private any getCurrentTickCount()

Gets the current tick count.

private string getNamedLockName(string actionType)

Gets a named lock name based on choosen scope and other factors

public string getScope()

Returns the scope where the LRU cache is stored.

public string getScopeKey()

Gets the unique cache key for this cache strategy.

public string getSize()

Returns the configured maximum size of the LRU cache.

public struct getStorage()

Gets a reference to the cache data storage.

private string hashKey(string key)

Creates a hashed version of the passed key.

public boolean keyExists(string key)

Checkes if a key exists in the cache.

public void put(string key, any data)

Puts data into the cache by key.

public void reap()

Looks at the timestamps of the cache pieces and throws out oldest one if the cache has more then the its max size.

public void remove(string key)

Removes data from the cache by key.

private void removeHashedKey(string hashedKey)

Removes a cached element by hashed key.

private void setScope(string scope)
private void setScopeKey(string scopeKey)
private void setSize(numeric size)
Methods inherited from caching.strategies.AbstractCacheStrategy:   getStrategyTypeName , getStrategyType , setParameter , init , setParameters , setCacheEnabled , getLog , getParameterNames , getParameter , isCacheEnabled , getParameters , setLog , getCacheStats , isParameterDefined
Method Detail
configure

public void configure( )

Configures the strategy.

Parameters:

Code:

	<cffunction name="configure" access="public" returntype="void" output="false"
		hint="Configures the strategy.">

		
		<cfif isParameterDefined("size")>
			<cfif NOT isNumeric(getParameter("size")) OR getParameter("size") LTE 0>
				<cfthrow type="MachII.caching.strategies.LRUCache"
					message="Invalid Size of '#getParameter("size")#'."
					detail="Size must be numeric and greater than 0." />
			<cfelse>			
				<cfset setSize(getParameter("size")) />
			</cfif>
		</cfif>
		<cfif isParameterDefined("scope")>
			<cfif NOT ListFindNoCase("application,server,session", getParameter("scope"))>
				<cfthrow type="MachII.caching.strategies.LRUCache"
					message="Invalid Scope of '#getParameter("scope")#'."
					detail="Use 'application', 'server' or 'session'." />
			<cfelse>
				<cfset setScope(getParameter("scope")) />
			</cfif>
		</cfif>
		<cfif isParameterDefined("scopeKey")>
			<cfif NOT Len(getParameter("scopeKey"))>
				<cfthrow type="MachII.caching.strategies.LRUCache"
					message="Invalid ScopeKey of '#getParameter("ScopeKey")#'."
					detail="ScopeKey must have a length greater than 0 and be a valid struct key." />
			<cfelse>
				<cfset setScopeKey(getParameter("scopeKey")) />
			</cfif>
		<cfelseif isParameterDefined("generatedScopeKey")>
			<cfset setScopeKey(getParameter("generatedScopeKey")) />
		<cfelse>
			<cfset setScopeKey(REReplace(CreateUUID(), "[[:punct:]]", "", "ALL")) />
		</cfif>
		
		<cfset flush() />
	</cffunction> 

flush

public void flush( )

Flushes the entire cache.

Parameters:

Code:

	<cffunction name="flush" access="public" returntype="void" output="false"
		hint="Flushes the entire cache.">
		
		<cfset var dataStorage = getStorage() />

		<cfset StructClear(dataStorage) />
		<cfset getCacheStats().reset() />
	</cffunction> 

get

public any get( string key )

Gets data from the cache by key. Returns null if the key is not in the cache.

Parameters:
string key

Code:

	<cffunction name="get" access="public" returntype="any" output="false"
		hint="Gets data from the cache by key. Returns null if the key is not in the cache.">
		<cfargument name="key" type="string" required="true"
			hint="Key does not need to be hashed." />

		<cfset var dataStorage = getStorage() />
		<cfset var cacheElement = "" />
		<cfset var hashedKey = hashKey(arguments.key) />
		
		<cfif keyExists(arguments.key)>
			<cfset cacheElement = dataStorage[hashedKey] />
			<cfset cacheElement.timestamp = getCurrentTickCount() />

			<cfset getCacheStats().incrementCacheHits(1) />

			<cfreturn cacheElement.data />
		<cfelse>
			<cfset getCacheStats().incrementCacheMisses(1) />
		</cfif>
	</cffunction> 

getConfigurationData

public struct getConfigurationData( )

Gets pretty configuration data for this caching strategy.

Parameters:

Code:

	<cffunction name="getConfigurationData" access="public" returntype="struct" output="false"
		hint="Gets pretty configuration data for this caching strategy.">
		
		<cfset var data = StructNew() />
		
		<cfset data["Scope"] = getScope() />
		<cfset data["Size"] = getSize() />
		<cfset data["Cache Enabled"] = YesNoFormat(isCacheEnabled()) />
		
		<cfreturn data />
	</cffunction> 

getCurrentTickCount

private any getCurrentTickCount( )

Gets the current tick count.

Parameters:

Code:

	<cffunction name="getCurrentTickCount" access="private" returntype="any" output="false"
		hint="Gets the current tick count.">
		<cfreturn getTickCount() />
	</cffunction> 

getNamedLockName

private string getNamedLockName( string actionType )

Gets a named lock name based on choosen scope and other factors

Parameters:
string actionType

Code:

	<cffunction name="getNamedLockName" access="private" returntype="string" output="false"
		hint="Gets a named lock name based on choosen scope and other factors">
		<cfargument name="actionType" type="string" required="true" />
		
		<cfset var name = "_MachIILRUCache_" & arguments.actionType & "_" & getScopeKey() />
		
		
		<cfif getScope() EQ "session">
			
			
			<cfif StructKeyExists(StructGet("session"), "sessionId")>
				<cfset name = name & "_" & StructGet("session").sessionId />
			</cfif>
		</cfif>

		<cfreturn name />
	</cffunction> 

getScope

public string getScope( )

Returns the scope where the LRU cache is stored.

Parameters:

Code:

	<cffunction name="getScope" access="public" returntype="string" output="false"
		hint="Returns the scope where the LRU cache is stored.">
		<cfreturn variables.instance.scope />
	</cffunction> 

getScopeKey

public string getScopeKey( )

Gets the unique cache key for this cache strategy.

Parameters:

Code:

	<cffunction name="getScopeKey" access="public" returntype="string" output="false"
		hint="Gets the unique cache key for this cache strategy.">
		<cfreturn variables.instance.scopeKey />
	</cffunction> 

getSize

public string getSize( )

Returns the configured maximum size of the LRU cache.

Parameters:

Code:

	<cffunction name="getSize" access="public" returntype="string" output="false"
		hint="Returns the configured maximum size of the LRU cache.">
		<cfreturn variables.instance.size />
	</cffunction> 

getStorage

public struct getStorage( )

Gets a reference to the cache data storage.

Parameters:

Code:

	<cffunction name="getStorage" access="public" returntype="struct" output="false"
		hint="Gets a reference to the cache data storage.">
		<cfreturn StructGet(getScope() & "." & getScopeKey()) />
	</cffunction> 

hashKey

private string hashKey( string key )

Creates a hashed version of the passed key.

Parameters:
string key

Code:

	<cffunction name="hashKey" access="private" returntype="string" output="false"
		hint="Creates a hashed version of the passed key.">
		<cfargument name="key" type="string" required="true" />
		<cfreturn Hash(UCase(Trim(arguments.key))) />
	</cffunction> 

keyExists

public boolean keyExists( string key )

Checkes if a key exists in the cache.

Parameters:
string key

Code:

	<cffunction name="keyExists" access="public" returntype="boolean" output="false"
		hint="Checkes if a key exists in the cache.">
		<cfargument name="key" type="string" required="true"
			hint="Key should not be hashed." />
		<cfif NOT StructKeyExists(getStorage(), hashKey(arguments.key))>
			<cfreturn false />
		<cfelse>
			<cfreturn true />
		</cfif>
	</cffunction> 

put

public void put( string key, any data )

Puts data into the cache by key.

Parameters:
string key
any data

Code:

	<cffunction name="put" access="public" returntype="void" output="false"
		hint="Puts data into the cache by key.">
		<cfargument name="key" type="string" required="true"
			hint="Key does not need to be hashed." />
		<cfargument name="data" type="any" required="true" />

		<cfset var dataStorage = getStorage() />
		<cfset var cacheElement = StructNew() />
		
		
		<cfset reap() />
		
		
		<cfif NOT keyExists(arguments.key)>
			<cfset getCacheStats().incrementTotalElements(1) />
			<cfset getCacheStats().incrementActiveElements(1) />
		</cfif>
		
		
		<cfset cacheElement.data = arguments.data />
		<cfset cacheElement.timestamp = getCurrentTickCount() />
		<cfset dataStorage[hashKey(arguments.key)] = cacheElement />
	</cffunction> 

reap

public void reap( )

Looks at the timestamps of the cache pieces and throws out oldest one if the cache has more then the its max size.

Parameters:

Code:

	<cffunction name="reap" access="public" returntype="void" output="false"
		hint="Looks at the timestamps of the cache pieces and throws out oldest one if the cache has more then the its max size.">
			
		<cfset var dataStorage = getStorage() />
		<cfset var sortedTimestamps = "" />
		<cfset var i = "" />
		
		<cfif (StructCount(dataStorage) + 1) GT getSize()>
	
			
			<cflock name="#getNamedLockName("cleanup")#" type="exclusive" 
				timeout="20" throwontimeout="false">
				
				<cfif (StructCount(dataStorage) + 1) GT getSize()>
				
					<cfset sortedTimestamps = StructSort(dataStorage, "numeric", "asc", "timestamp") />
					
					<cfloop from="1" to="#ArrayLen(sortedTimestamps)#" index="i">
						<cftry>
							<cfif (StructCount(dataStorage) + 1) GT getSize()>
								<cfset removeHashedKey(sortedTimestamps[i]) />
							<cfelse>
								<cfbreak />
							</cfif>
							<cfcatch type="any">
								
							</cfcatch>
						</cftry>
					</cfloop>
				</cfif>
			</cflock>
			
		</cfif>
	</cffunction> 

remove

public void remove( string key )

Removes data from the cache by key.

Parameters:
string key

Code:

	<cffunction name="remove" access="public" returntype="void" output="false"
		hint="Removes data from the cache by key.">
		<cfargument name="key" type="string" required="true"
			hint="The key does not need to be hashed." />
		<cfset removeHashedKey(hashKey(arguments.key)) />
	</cffunction> 

removeHashedKey

private void removeHashedKey( string hashedKey )

Removes a cached element by hashed key.

Parameters:
string hashedKey

Code:

	<cffunction name="removeHashedKey" access="private" returntype="void" output="false"
		hint="Removes a cached element by hashed key.">
		<cfargument name="hashedKey" type="string" required="true"
			hint="The key must be hashed." />

		<cfset var dataStorage = getStorage() />

		<cfif StructKeyExists(dataStorage, arguments.hashedKey)>
			<cfset StructDelete(dataStorage, arguments.hashedKey, false) />
			<cfset getCacheStats().incrementEvictions(1) />
			<cfset getCacheStats().decrementTotalElements(1) />
			<cfset getCacheStats().decrementActiveElements(1) />
		</cfif>
	</cffunction> 

setScope

private void setScope( string scope )

Parameters:
string scope

Code:

	<cffunction name="setScope" access="private" returntype="void" output="false">
		<cfargument name="scope" type="string" required="true" />
		<cfset variables.instance.scope = arguments.scope />
	</cffunction> 

setScopeKey

private void setScopeKey( string scopeKey )

Parameters:
string scopeKey

Code:

	<cffunction name="setScopeKey" access="private" returntype="void" output="false">
		<cfargument name="scopeKey" type="string" required="true" />
		<cfset variables.instance.scopeKey = arguments.scopeKey />
	</cffunction> 

setSize

private void setSize( numeric size )

Parameters:
numeric size

Code:

	<cffunction name="setSize" access="private" returntype="void" output="false">
		<cfargument name="size" type="numeric" required="true" />
		<cfset variables.instance.size = arguments.size />
	</cffunction>