Barry Beattie wrote:

I just wanted to check (best practices 'n' all)

I writing a server-scoped singleton CFC that is stores which records are currently being edited (think of it as a "pesimistic lock registry" using a struct of structs in CFC variables memory). there's also methods for setting and deleting "locks" which will just add a struct key or remove it.


I don't know if I have enough experience to throughly answer this question, but I'll dive in here. I understand what you are doing - it sounds like a user is going to be editing something. What happens if a lock doesn't get deleted (lost/timedout session, server problems, connectivity problems, bug/coding mistake) - is there going to be some search and destroy (purge) function that deletes locks based on the timestamp?

I assume you are locking because multiple users might be accessing this server scoped CFC at the same time.

now obviously, because it's a singleton and I need to prevent RACE conditions, I'll have to CFLOCK the setting and deleting. Normally I'd do it in the call but is there anything actually *wrong* with using CFLOCK _within_ the SET and DELETE methods of the singleton?


I can't think of a reason why you can't put it in the cfc directly - except for a deadlock of some sort. I can't come up with an example at the moment (it's 1am in the morning here - what do you expect ;-)

I would think if you put it in the function it might save you if you forget to lock something, however, it could hurt you if you do a lot subsequent calls which would require a lot of getting and releasing of the lock. I don't know if you have thought about it - but maybe a named lock might be helpful and it wouldn't tie up your server scope?? If you are planning on calling many functions in the singleton at one time on the calling page - maybe it's best to put the lock on the calling page. However, for the sake of keeping things in one place and simplicity - it might be best to put it in the CFC. Do you think it might be best to put the lock in the cfc since the internals of the CFC depend of having the lock to operate properly?

so the choices are (1) lock the call

<cflock scope="SERVER" type="EXCLUSIVE" throwontimeout="Yes" timeout="10">
        <cfset bOK = server.kernel.SetEntityLock(entity) />
</cflock>

- OR - (2) lock within the method

<cffunction name="SetEntityLock">
        <cfargument name="lockData" type="struct" required="Yes" />
        <cfset var bOK = false />
        <cflock scope="#variables.lockScope#" type="EXCLUSIVE" throwontimeout="Yes" 
timeout="10">
                <!--- add a struct to the "lock registry" here --->
                <cfset bOK = true />              
        </cflock>
        <cfreturn bOK />
</cffunction>


thanx
barry.b


Example in Quasi-Code:
<cfcomponent display="kernel">

<cffunction name="init">
<cfargument name="lockName" required="false" default="entityLock" type="string" />
<cfargument name="purgeDuration" required="false" default="600" type="numeric" hint="in seconds" />
<cfscript>
// Set instance vars
variables.instance = structNew();
variables.instance.entities = structNew();
variables.instance.lockName = arguments.lockName;
variables.instance.purgeDuration = arguments.purgeDuration;
// run set the next purge check timestamp
setNextPurgeCheck();
<cfscript>
</cffunction>


<cffunction name="SetEntityLock">
        <cfargument name="lockData" type="struct" required="Yes" />
        <cfset var bOK = false />
        <cfset purgeEntityLocks() />
        <cflock scope="# variables.instance.lockName#" type="exclusive" throwontimeout="Yes" 
timeout="10">
                <!--- add a struct to the "lock registry" here --->
                <cfset bOK = true />              
        </cflock>
        <cfreturn bOK />
</cffunction>

<cffunction name="deleteEntityLock">
        <cfargument name="lockData" type="struct" required="Yes" />
        <cfset var bOK = "" />
        <cfset purgeEntityLocks() />
        <cflock scope="# variables.instance.lockName#" type="exclusive" throwontimeout="Yes" 
timeout="10">
                <cfset doDeleteEntity(arguments) />
        </cflock>
        <cfreturn bOK />
</cffunction>

<cffunction name="purgeEntityLocks" access="public(in case you want to manually run 
this from outside the cfc)/private">
        <cfset var oldEntities = arrayNew(1) />
        <cfset var doPurge = FALSE />
        <cfset var currNextPurgeCheck = getNextPurgeCheck() />
        <!--- This only runs if now() has pased the next purge check timestamp - 
pre-check function here --->
        <cfif now() GTE currNextPurgeCheck >
                <!--- I was thinking you might need to have some sort of 
double-checked locking on
                        the nextPurgeCheck variable in case two people hit the 
purgeEntityLocks at the same time
                        so instead of locking down nextPurgeCheck var because 
it would hurt performance,
                        lock down this purge function and check again within an 
exclusive lock just for purging
                        It's probaly not a good idea to lock purge just when 
checking only when purging every 10 minutes
                        I guess there's a few ways of doing it--->           
                <cflock scope="purgeLock" type="exclusive" throwontimeout"yes" 
timeout="10">
                        <cfif getNextPurgeCheck() EQ currNextPurgeCheck>
                                <cfset setNextPurgeCheck() />
                                <cfset doPurge = TRUE />
                        </cfif>
                </cflock>
                <cfif doPurge >
                        <cflock scope="# variables.instance.lockName#" type="EXCLUSIVE" 
throwontimeout="Yes" timeout="10">
                        <!--- Code to find old timestmamp and set them into the 
oldEntities array--->
                        ...
                        <!--- loop over the array --->
                        <cfloop>
                                <cfset doDeleteEntiry(oldEntities[i]) />
                        </cfloop>
                        </cflock>
                </cfif>
        </cfif>
</cffunction>

<cffunction name="doDeleteEntity" access="private" returntype="boolean">
        <!--- Since purgeEntityLocks and deleteEntityLock use the same code to 
delete,
                I'm thinking it's best to separate this for maintainablity --->
        <!--- Delete code here --->
        <cfreturn booleanValue />
</cffunction>

<cffunction name="setNextPurgeCheck" access="private">
        <cfset variables.instance.nextPurgeCheck = dateAdd("s", 
variables.instance.purgeDuration, NOW()) />
</cffucntion>
<cffunction name="getNextPurgeCheck" access="private" returntype="date">
        <cfreturn variables.instance.nextPurgeCheck>
</cffunction>

</cfcompoent>

Maybe I'm just getting this more complicated than necessary.
HTH,
.Peter

--
Peter J. Farrell :: Maestro Publishing

blog    :: http://blog.maestropublishing.com
email   :: [EMAIL PROTECTED]
phone   :: 651-204-0513

OHNOSECOND-- That miniscule fraction of time in which you realize that you've 
just made a BIG mistake.
--


---------------------------------------------------------- You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.

CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting 
(www.cfxhosting.com).

An archive of the CFCDev list is available at
www.mail-archive.com/[email protected]



Reply via email to