Record locking
Since the very beggining of Lotus Notes, record (document) locking has been on the minds of most Notes developers; prior to R6, this was just a collection of ‘hacks’ and workarounds to give us some sort of a robust mechanism. I thought I’d revisit the concept, that led me to an interesting discovery using profile documents.
Every Notes developer has been asked at some point in his/her career to implement a record locking mechanism for Notes. The bad news is that Notes prior to R6 did not (officially) support record locking; R6 has added the ability to lock documents/elements as long as you use an R6 client and server. What about R5? Well, many attempts have been made to code something up, since the concept is quite straight forward - if someone is editing a document, then the document becomes locked and no other edits are permitted until the document is closed.
This sort of technique has many drawbacks, mainly dealing with replicated databases and the fact that the document has to be modified (saved) in order for the locking to take place; it may not be such a big deal for most of the implementations, but is some cases where the user simply canceled out of editing a document, the document will still be modified (since the locking flag has to be removed).
Lotus’s knowledge base has a good article on how to do this, however it doesn’t deal with replicas and it still modifies the document even if the edit action is canceled. I thought I’d give it a shot and see if I could improve the model a little (I hear you asking, “…but why bother since R6 can do it?” - it’s a challenge plus a lot of current R5 apps can still benefit).
The idea
Now I like profile documents just as much as I like dialog boxes - they are fast, efficient, small and hidden. I came up with the idea of using profile documents like semaphores or flags to store the state of each document if it has been locked or not. This way, the actual document wouldn’t have to be modified just for the sake of changing the locking flag - the corresponding profile can do that for the document.
There’s some pretty obvious issue with this idea - if each document has a corresponding profile document, and the database holds 10,000 documents then you could potentialy end-up with 10,000 profile documents as well (if all those documents were to be edited) which is not particularly great. Having said that, and reminded that profile documents are fast and efficient, then it doesn’t look so bad for small volumes. If you’re talking 100,000+ documents that all need to be edited (?) then this most likely seems a poor solution. Assuming that most Notes databases are much smaller than that, well, let’s see…
The concept
I wanted to use a profile document like a ‘flag’ for each actual document in the database. Hang on, ‘Help’ specifically states: “Only one profile of a given form can exist per database per person. If you create a profile without a user name, Notes assumes it’s the only profile document of that form in the database.” Normally, using the syntax for the GetProfileDocument method, we use the UserName to create a profile for each user, but what about for each document?
How about this then;
Set notesdocument = notesdatabase.GetProfileDocument ( notesdocument.NoteID, notesdocument.UniversalID )
What we’re doing here is using the document’s Unique ID as the key, instead of the UserName. This will then allow for a profile document to be created/accessed for each document in the database.
Putting it all together
There’s a slight let-down when using profiles; caching. There no option for turning it off so if we were to use profile documents in the current database (same as the actual documents to be edited) then this would miserably fail. As soon as we saved a profile document (say we ‘lock’ a document) then that is cached for the remainder of the session; Notes would still think the document is locked even if we ‘unlocked’ (saved the profile) since the values on profiles are natively cached. Not particularly good, but there’s a workaround (always is with Notes
All we need to do is store the profiles in a separate database - the act of opening a profile in another database, effectively creates a new session and therefore the changes on the values of the profile work as expected.
I’ve used SERVER_NAME and DB_NAME here as constants placed in the Global declarations in a Notes form; meaning the server and database name of where the profiles are to be stored (it can simply be a blank database (File, Database New) since we are only storing hidden documents anyway).
Code has to be placed in multiple events on the form (simply to capture edit actions both from from a view and from within the document). Let’s start with Postmodechange.
Sub Postmodechange(Source As Notesuidocument)
If source.editMode Then
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim pdoc As NotesDocument
Dim profiledb As New NotesDatabase( SERVER_NAME, DB_NAME )
Set db = session.CurrentDatabase
Set doc = Source.Document
Set pdoc = profiledb.GetProfileDocument ( doc.NoteID, doc.UniversalID )
pdoc.locked = “1″
pdoc.save True, False
End If
End Sub
Here, when the document is placed in edit mode, a new profile is created (if it doesn’t exist) in the profile database, and a field is set to locked (1). To be complete, the same code has to be also placed in the Postopen event to capture any edit actions from the view level.
If we now look at say the Queryopen event, we would have something like:
Sub Queryopen(Source As Notesuidocument, Mode As Integer, Isnewdoc As Variant, Continue As Variant)
If IsNewdoc Then End
If source.editMode Then
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim pdoc As NotesDocument
Dim profiledb As New NotesDatabase( SERVER_NAME, DB_NAME )
Set db = session.CurrentDatabase
Set doc = Source.Document
Set pdoc = profiledb.GetProfileDocument ( doc.NoteID, doc.UniversalID )
If pdoc.locked(0) = “1″ Then
Messagebox “Sorry, this document is currently locked.”, 48, “Lotus Notes”
continue = False
End If
End If
End Sub
In here, the profile for the document is retrieved and if an attempt to edit the document is made and the document is locked by someone else, the user gets a message and is not permitted to continue ( similar code also needs to be placed in Querymodechange)
Finally, to unlock a locked document, we put this code in the Queryclose event:
Sub Queryclose(Source As Notesuidocument, Continue As Variant)
If source.editMode Then
Dim session As New NotesSession
Dim db As NotesDatabase
Dim doc As NotesDocument
Dim pdoc As NotesDocument
Dim profiledb As New NotesDatabase( SERVER_NAME, DB_NAME )
Set db = session.CurrentDatabase
Set doc = Source.Document
Set pdoc = profiledb.GetProfileDocument ( doc.NoteID, doc.UniversalID )
pdoc.locked = “”
pdoc.save True, False
End If
End Sub
Here, the locked flag is simply cleared (providing the document was in edit mode).
Conclusion
Even though this concept does not offer a complete solution to manage locking of documents, it does demonstrate a unique way of using profile documents. Through the use of the profiles, the actual documents themselves are never tampered with (to either store or clear locking flags) unless an intended edit action has taken place. The profiles only exist as a ’semaphore’ mechanism and as long as the document volumes are low, it can be efficient and robust. There is naturally some performance degradation having to open profile documents in a separate database (please Lotus, add a ‘nocache’ option!) but as long as the server is not accessed through dial-up, it should be alright.
Now, just a thought, if the database storing the locking profiles was to be replicated (as frequently as possible) then this may just work across replicas too (it is ambitious I know…).
chris on October 7th 2003 in Uncategorized
5 Responses to “Record locking”
Rock responded on 13 Oct 2003 at 2:15 pm #
Good idea Chris - but maybe I have an idea to improve upon it. What if, instead of creating a profile document for each document as you stated, we instead create an ITEM for each document in the same general profile for ALL documents? Here’s my idea:
Name the field the uniqueID of the document being locked. In the “check” code, do a pdoc.hasItem(doc.UniversalID); if you get a True, then the doc should be locked. Then, in the code that you use to set the flag back to an empty string, instead use a pdoc.RemoveItem(doc.UniversalID) to remove the item from the profile document.
Now you don’t have thousands of profile documents; heck, you don’t even have thousands of items on the one profile document, since they’re being cleaned up.
It’s funny you should be blogging about this. I am in the midst of a project where I am probably going to need some document locking, and I was looking at just such a technique. You’ve contributed one missing link I had - the storage of the profile doc in a different database to clear the caching. I think between the two of our ideas, we may have a nice, scalable solution.
Oh, and a couple of points:
First, for those who say you’ll create replication/save conficts because now you’ll have multiple people writing to the same profile doc - no you won’t. In this sense profile documents are design documents, and last one in wins. And since each item is unique, you won’t even overwrite someone else’s items.
Second, about the username/key thing with profile documents. This has been one of the largest sources of confusion for developers for many, many years. The second parameter does NOT have to be a user name - and it is not tied to users whatsoever. The second parameter is simply a unique key to identify the profile. It can be any string.
If you test this modified idea out, let me know how it goes - I am curious to see how it performs against your current idea.
Thanks for sharing!
Rock
http://www.LotusGeek.com
chris responded on 13 Oct 2003 at 11:28 pm #
Now why didn’t I think of that? (because I haven’t published a book on LotusScript
That’s a great idea - and the key here being that profile documents act just like design elements; so we can have a single profile document (oh joy). I will definately give it a shot - Thanks for contributing Rocky!
Jason responded on 22 Oct 2003 at 12:56 pm #
Is there a limit on the number of ITEMS that can be stored in one document (Profile doc) ? Just thinking of large scale apps, i.e. 10,000 records plus.
Does that annoying 16,584 bytes or whatever size of text limitation apply ?
Is the idea that one finished with (i.e. unlocked) the Id of the Document is removed from the Profile doc ? How would ONE Profile doc cope with MANY users locking/unlocking their docs.. specifically many rapid lock and unlocks, would the Profile become corrupt maybe ?
Am i asking too many questions ? lol
Jerry Carter responded on 13 Dec 2003 at 5:54 am #
I think the limit you’re talking about applies to total number of charachters used to specify fields on forms within a database and that (if memory serves) is tied to available memory. I don’t think there is a per-form cap or a per-document cap that is less than this.
An interesting question then, would be, do ITEM’s added to a profile count as fields on a form? They’re two different things.
Incidentally, my solution (web only):
Create a profile document on document open, use a javascript timer (in an iframe) to update it every 90 seconds.
When any document is opened, check for the profile document.
IF the last modified date is more than 100 seconds old, stamp a new user name on it. If it’s not, display “so and so is editing this document, please try again in 90 seconds.”.
Each document succesfully edited displays “Locked by you, name of person, for editing as of lastprofiledocupdatetime.”
This was the easiest way I could come up with after abandoning unreliable exit / closing events with the web.
Mike Kinder responded on 10 Jan 2004 at 6:03 am #
I have a document locking tool that is very flexible. The one for download on my site is out of date, but, if after reading the info on http://www.sliktool.com you like the idea, email me and I will get you an evaluation copy.