Solved

Coldfusion Record Locking architecture - how to?

Posted on 2011-09-06
5
335 Views
Last Modified: 2012-05-12
Hi Experts,

I found an article on the web that utilitzes jquery architecture to lock a database record, without actually making database calls. Instead of tying down the database with repeated calls, this architecture claims it can be done just by using an application wide component and then using jquery, checks periodically to see if the lock persists or needs to be released. This is the link that I found it at:

http://www.grantshepert.com/post.cfm/managing-record-locks-without-punishing-your-database

I have created the architecture in my application and the various code modules are attached. I've already downloaded the jquery.timers.js plugin that is required for this. The problem I'm having is understanding what the "lockpath" and "unlockpath" should be, and also instead of with an onclick event, I need this to run as soon as the application is opened by the user. Could someone provide me some help with this?

<!---the cfc that will be instantiated in app.cfc--->
<cfcomponent displayname="filelock" hint="Maintains file locks">
	<cffunction name="init" displayname="init" access="package" output="false" returntype="filelock">  
        <cfargument name="lockTimeLength" type="Numeric" required="false" default="10">  
        <cfargument name="purgeTimeLength" type="Numeric" required="false" default="5">  
  
        <!--- structure to hold locks --->  
        <cfset variables.instance.fileLocks      = StructNew()>  
        <!--- the general lock age --->  
        <cfset variables.instance.lockAge        = getTickCount()>  
        <!--- init the "purge age", which will run occasionally to clean out old locks --->  
        <cfset variables.instance.purgeTime      = getTickCount()>  
           
        <!--- convert seconds to msec --->  
        <cfset variables.lockTimeLength          = arguments.lockTimeLength*1000>  
        <cfset variables.purgeTimeLength     = arguments.purgeTimeLength*1000>  
           
        <cfreturn this>  
    </cffunction>  

	<!--- set a file as locked by passing in its processID --->  
    <cffunction name="setFileLock" access="public" description="Locks file" output="true" returntype="void">  
        <cfargument name="processID" type="Numeric" required="true">  
        <cfset variables.instance.fileLocks[processID] = getTickCount()>  
        <!--- we periodically want to purge the locks, just to keep the list small. --->  
        <cfif getTickCount()-variables.instance.purgeTime gt variables.purgeTimeLength>  
            <cfset purgeOldFileLocks()>  
        </cfif>  
    </cffunction>  
	
    <!--- check for a file lock --->  
<cffunction name="isFileLocked" access="public" description="Checks to see if a file is locked" output="true" returntype="boolean">  
    <cfargument name="processID" type="Numeric" required="true">  
  
    <!--- we periodically want to purge the locks, just to keep the list small. --->  
    <cfif getTickCount()-variables.instance.purgeTime gt variables.purgeTimeLength>  
        <cfset purgeOldFileLocks()>  
    </cfif>  
    <!--- is the 'application' age older than the lock time length?  
    (it might have just restarted, so we need to wait for all the current locks to call in) --->  
    <cfif getTickCount()-variables.instance.lockAge lt variables.lockTimeLength>  
        <cfreturn true>  
    <!--- does the processID exist in the key --->  
    <cfelseif structKeyExists(variables.instance.fileLocks,arguments.processID)>  
        <!--- is the lock less than the lock time length? --->  
        <cfif getTickCount() - variables.instance.fileLocks[arguments.processID] lt variables.lockTimeLength>  
            <cfreturn true>  
        <cfelse>  
            <!--- delete the processID from the fileLock structure --->  
            <cfset clearFileLock( arguments.processID )>  
            <cfreturn false>  
        </cfif>  
    </cfif>  
       
    <cfreturn false>     
</cffunction>  


	<!--- clear a file lock --->  
    <cffunction name="clearFileLock" access="public" description="Locks file" output="true" returntype="void">  
        <cfargument name="engagmentID" type="Numeric" required="true">  
        <cfset structDelete(variables.instance.fileLocks,arguments.engagmentID)>  
    </cffunction>  
      
    <!--- cleanup to keep the lock list from wasting memory --->  
    <cffunction name="purgeOldFileLocks">  
        <!--- sort the structure by the tickcount (which is why we use tickcount, incidentally) --->  
        <cfset var aSortedLocks      = StructSort(variables.instance.fileLocks,"numeric","ASC")>  
        <cfset var iiX               = "">  
        <cfset var currentTime       = getTickCount()>  
           
        <cflock scope="application" timeout="30">  
            <cfloop from="1" to="#ArrayLen(aSortedLocks)#" index="iiX">  
                <!--- if the lock's timestamp is larger than the allowed lock time length, it gets deleted --->  
                <cfif currentTime-variables.instance.fileLocks[aSortedLocks[iiX]] gt variables.lockTimeLength>  
                    <cfset structDelete(variables.instance.fileLocks,aSortedLocks[iiX])>  
                <!--- as soon as we find a 'young' lock, we can exit --->  
                <cfelse>  
                    <cfbreak>  
                </cfif>  
            </cfloop>  
        </cflock>  
        <cfset variables.instance.purgeTime  = getTickCount()>  
    </cffunction>  

</cfcomponent>
<!---end cfc--->

<!---the jquery code that I have created a separate js include file that I can include in every page of my app--->
// JavaScript Document

$(document).ready(function() {   
    var lockUrl = "--your unlock path here--";   
    var unlockUrl = "--your unlock path here--";   
  
    $("#doLock").click(function() {   
        setLock();   
        $("#processID").everyTime("2000ms",label="fileislocked",setLock);   
    });   
  
    $("#releaseLock").click(function() {   
        $("#processID").stopTime(label="fileislocked");   
    });   
    $("#dounLock").click(releaseLock);   
       
    function setLock() {   
        $.ajax({   
            url: lockUrl + "&processID=" + $("#processID").val(),   
            //type: 'GET',  
			type: 'POST', 
            dataType: 'html',   
            timeout: 5000,   
            error: function(){   
              alert('Error loading document');   
            },   
            success: function(html){   
              alert('called lock!');   
            }   
        });        
    }   
       
    function releaseLock() {   
        $("#processID").stopTime(label="fileislocked");   
        $.ajax({   
            url: unlockUrl + "&processID=" + $("#processID").val(),   
            type: 'GET',   
            dataType: 'html',   
            timeout: 5000,   
            error: function(){   
              alert('Error loading document');   
            },   
            success: function(html){   
             alert('called unlock!');   
            }   
        });        
    }   
}); 
<!---end jquery code--->

Open in new window

0
Comment
Question by:roger_v
  • 2
  • 2
5 Comments
 
LVL 28

Assisted Solution

by:strickdd
strickdd earned 250 total points
Comment Utility
I would be weary of this implementation. Since the record locks are determined by the application, there are several things that can go wrong (probably more, these are just off the top of my head)

1. The application recycles on the server, all locks are lost
2. This looks like a file lock, not a database lock
3. A change is made to the database without using the web interface, locks on the website become pointless
0
 
LVL 1

Author Comment

by:roger_v
Comment Utility
@strickdd:

I'm confused. What does "application recycles on the server" mean?

It is a file lock, but based upon a database record. If the primary key which is in the form of a hidden field, is in use, then essentially that record is "locked" until the user either close the browser or logs out.

You're right, direct database changes without web interface could mess things up. But the rationale by the author of this site is that instead of tying down the database, this is an efficient way of implementing record locks. I'll need input from experts like you to determine if that is indeed the case.
0
 
LVL 39

Accepted Solution

by:
gdemaria earned 250 total points
Comment Utility

The code appears to lock by what they are calling "process ID".. .not sure what that refers to exactly because it's a variable that is passed into the function.   But if the lock is ON or OFF based on the user's session ID, I don't know how this will help you.   It seems they script is locking only ONE thing.   So processID has the lock, then gives it up and another user gets the lock.

My guess is Roger, that you want to lock certain data, probably on a record-by-record basis.   That is, for example, user A can lock one product record, while user B can put a lock on a different product record.   IMO, this is best done in the database.   I don't see any downsides to managing data locks in the database, seems to make sense.  And it certainly won't slow things down..

Just my 2 cents... weren't you working on a database level lock before?  What happened with that?
0
 
LVL 39

Expert Comment

by:gdemaria
Comment Utility
> It is a file lock, but based upon a database record. If the primary key which is in the form of a hidden field, is in use, then essentially that record is "locked" until the user either close the browser or logs out.

If this is record-by-record, then that is good.   Perhaps the "processID" is the primary key of the data table?   But what about two tables with the same IDs?   Do you pass in table with ID?    "Product123"  and "Order123" ?   Perhaps I didn't see it right...



> You're right, direct database changes without web interface could mess things up. But the rationale by the author of this site is that instead of tying down the database, this is an efficient way of implementing record locks. I'll need input from experts like you to determine if that is indeed the case.


If the reasoning for using this approach is not to tie up the database, I have to disagree.   You can run 50 queries in a single page request with each one taking a few nano seconds for a total of 0.25 seconds...   I hear this a lot, people use query-of-query to avoid hitting the database; but it's actually slower than another call to the database.   The fact is that the database is a lot more efficient than using server resources.  

For example, I once created a bunch of structures to perform some complex calculations for a report in order to avoid doing it in the database.   It took nearly a minute to run, I moved it to the database and it run in a few seconds.  









0
 
LVL 1

Author Comment

by:roger_v
Comment Utility
"The code appears to lock by what they are calling "process ID"."

GDE, the original code had "fileID" but I changed it to processID for my app. Yes that is the primary key of my table. So, when one user opens up a record with the processID primary key, that value (of processID) is stored in a hidden field. The jQuery timers plugin enables to do a check once every so many seconds and if the record is still available then it is considered locked.

The link is down for some reason, it has all the stuff explained much clearly.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

Having worked on larger scale sites, we found out that you are bound to look at more scalable solutions to integrating widgets, code snippets or complete applications and mesh them into functional sites, in any given composition. To share some of…
International Data Corporation (IDC) prognosticates that before the current the year gets over disbursing on IT framework products to be sent in cloud environs will be $37.1B.
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)
The viewer will learn the basics of jQuery including how to code hide show and toggles. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

6 Experts available now in Live!

Get 1:1 Help Now