In this article I'm going to present the solution I used to implement sequential numbering of documents in a highly distributed environment.
I noticed that many inexperienced Lotus developers tend to use unreliable document numbering solutions, usually employing view counting method.
To be completely honest, the first thing that came to my mind 6 years ago was exactly that.
Why "counting view documents" approach is not reliable way to go?Let's analyze how that works:
- new document receives save request from the user (QS) and calls the function that gets next number
- the function gets a database and a view (sorted descending by doc number)
- the function sets the reference to the top view document
- the function reads the value from the document field (or counts the number of docs in a view)
- the function returns last number incremented by 1
- new document receives value from the function and stores it in a field
- new document is saved
Now, what if there are multiple new documents being saved on, lets say 3 workstations, at the same time.
Notice that steps 4 to 7 are all critical. If someone saves his new document while you're on any of those steps, his document is going to receive the same sequence number as yours.The point is that you need some kind of locking mechanism.
If you want to read more about this subject search on:
- critical section
- thread (thread safe, semaphores, monitors)
- mutex This is how the NotesDocument's Lock method works (available from version 6):
- if the document is not locked, the method places the lock and returns True
- if the document is locked and the current user is one of the lock holders, the method returns True
- if the document is locked and the current user is not one of the lock holders, the method returns False
- if the document is modified by another user before the lock can be placed, the method raises an error
This approach requires that you either:
Design prereqs and implementation1
- always have access to administration server (server that provides document locking)
- or use composite document key where one of the components would be location (OU - organizational unit), which would then produce sequential numbering on OU level, and that way globaly provide unique document keys
. In your db, on all forms that you need sequential numbering for, add hidden, text field - strDocType2
. Create new ScriptLibrary (or open an existing one) and add this function to it:
Listing 1: Function for number generation
|Function GenSeqNo( docType As String ) As
' mbonaci, 11.03.2006
' - returns next
sequence number (as String) for document of type 'docType'
' - increments counter doc
' add your custom
Dim s As New NotesSession
Dim w As New
Dim db As NotesDatabase
Dim v As
Dim doc As NotesDocument
Dim nextNum As
' db that holds counter docs - pull these
settings from a profile doc
Set db = s.GetDatabase(
"your_server_name", "database_relative_path" )
' you may want to replace msgbox with something that best suits
If db Is Nothing Then
Msgbox "Lock db
' exit if document locking is
not enabled in db properties
If Not db.IsDocumentLockingEnabled
Msgbox "Document locking not enabled"
Set v = db.GetView( "SeqNoView" )
' get the view containing the counter doc
Set doc =
v.GetDocumentByKey( docType, True ) ' locate counter doc for
Call doc.Lock( True )
nextNum = Cint(
doc.strSeqNo(0) ) + 1 ' retrieve next number
doc.strSeqNo = Cstr( nextNum ) ' increment existing number
in counter doc
Call doc.Save( True, False, False ) '
save should always return true - doc is locked by myself
GenSeqNo = Cstr( nextNum )
. On each form you want to implement numbering for:
- use the script library you just created in Form's Globals > Options (Use "library_name")
- hard-code this in PostOpen event:
Listing 2: Form's PostOpen event - adding type to a document
|Sub Postopen(Source As Notesuidocument)|
' Adding value to type field of new documents
- add this piece of code to QuerySave form event:
Listing 3: Form's QuerySave event - obtain new number when document is first saved
|Sub QuerySave( Source As NotesUIDocument,
Continue As Variant )|
Get sequence number from QuerySave, but
only if it hasn't yet been issued
Dim newSeq As String
If source.document.sRegNo(0) = "" Then
newSeq = GenSeqNo(
source.Document.sRegNo = "composite_key_components-" & newSeq
. Create new (blank) database that will hold counter docs (we'll call it "counter db")6
. On counter db:
- db Access control > Advanced tab > Administration server > select option "Server" and choose master lock server from the list
- db properties > first tab > check option "Allow document locking"
- create new form (counter doc) and add two text fields:
- docType holds document type name (value from your document's strDocType field)
- strSeqNo holds last issued number for that doc type
- create new view that would:
- show all counter docs
- have first column show docType field, categorized, ascending
7. Create new counter document for each document type
- in docType field enter the same value you hard-coded in form's PostOpen event, in strDocType field
- in strSeqNo field enter 0 (or any other number you want start counting from)
That's it, now test...
To test your solution (whichever you choose to implement) you should create an agent that requests 1000 sequence numbers (by calling getSeqNo function 1000 times).
Then start the agent on at least three workstations simultaneously.
If all the numbers are unique you're good to go.