« Blogging again ... | Main| Lotus Notes improvement requests »

Create replicas with script fast

Category Tips

If anybody needs to create replicas for several databases with script , he will find out that the only available method (NotesDatabase.CreateReplica) creates a "full" replica including all documents. This can take a lot of time if the database size is large. There is no way in script how to create only a "replication stub" of a database, like you can if you do this manually form the client. However there is a workaround for this (as always:-))) ...  

Actually you have two options how to do this and both use the trick that you can change the replica-ID of a database via script easily.

1) Create a blank database and change the Replica-ID
Simply create a new blank database (notesDatabase.Create) and then change its replica-id to the same value as your original database. Thats all.
However this method has some serious disadvantages:
Even after replication the replica will not be completly identical as you would expect.
There are several database properties that won't replicate and will be different among your replicas.

2) Create a blank database, change the Replica-ID to "00000000-00000000" and set the title
This method achieves much better results and I would recommend it as it will create "perfect" replicas without any differencies, exactly as you would expect.
However this method is undocumented and unsupported by Lotus so you have to use it at your own risk. (but it works fine in R4,R5,R6)

The whole trick is that if you create a blank database, set its replica-id to only zeros and then change the database title to a predefined format (Server+Tab+Filepath) then this database will be initiated by the next  replication with this server.
This creatin mechanism is being used by the notes client in the version R5 when you create local databases.
The only disadvantage of the function is that you have to replicate with exactly the defined server. Only this server can initalize the replica. After this has been done you can of course replicate wih any other without problems.

If you only want to test how this works you can also download this database here.
It is completly blank an has a replica id set to "00000000-00000000".
Simply copy it onto you local machine, change the title to whatever database you want (dont forget the proper format) and then trigger a replication with the server.




Example script:
Dim Session As New NotesSession
Dim DB As NotesDatabase
Dim ReplicaDB As New NotesDatabase( "", "" )
Dim ReplicaServer As String
Dim ReplicaFilePath As String
Dim ReplicaTitle As String
Dim Server As NotesName
     
Set DB = Session.CurrentDatabase                                ' this is the datbase from which a replica should be created
Set Server  = New NotesName(DB.Server)
     
ReplicaServer = "Server1/ACME"                                ' the server on which the replica should be created
ReplicaFilePath = "Test\NewReplica.nsf"                                ' the filepaht where the replica should be created
ReplicaTitle = Server.COMMON + Chr(9) + DB.FilePath                ' this is exactly how the title of the replica has to be
     
Call ReplicaDB.Create( ReplicaServer , ReplicaFilePath , True)        ' creates a new blank database
Call SetReplicaID(ReplicaDB, "0000000000000000")                 ' changes the replica ID to '00000000:00000000'
ReplicaDB.Title = ReplicaTitle                                ' changes the title of the database
     
You will need a function that changes the Replica-ID of a database. This has to be done through API.
You can use your own code, find some function in the formums on Notes.Net or use this one.
(This function has not been written by me, but I don't know where I got it from and so I can't give credits:-( . If I find out I will post it.)

Private Type ReplicaInfo
      ID(1) As Long
      Flags As Integer
      CutoffDays As Integer
      CutoffDate(1) As Long
End Type

Declare  Private  Function W32_OSPathNetConstruct Lib "nnotes.dll" Alias "OSPathNetConstruct"  ( Byval Z As Long, Byval S As  Lmbcs String, Byval F As  Lmbcs String, Byval P As  Lmbcs String) As Integer
Declare Private Function NSFDbReplicaInfoGet Lib "nnotes.dll" ( Byval H As Long, R As ReplicaInfo) As Integer
Declare Private Function NSFDbReplicaInfoSet Lib "nnotes.dll" ( Byval H As Long, R As ReplicaInfo) As Integer
Declare Private Function NSFDbOpen Lib "nnotes.dll" Alias "NSFDbOpen" ( Byval P As String, H As Long) As Integer
Declare Private Function NSFDbClose Lib "nnotes.dll" Alias "NSFDbClose" ( Byval H As Long) As Integer

Sub SetReplicaID(db As NotesDatabase, replicaid As String)
      Dim l1 As Long
      Dim l0 As Long
      Dim v1 As Variant
      Dim v0 As Variant
      Dim p As String*1024
      Dim hDB As Long
      Dim R As ReplicaInfo
      p$ = String(1024, " ")
     
      Call W32_OSPathNetConstruct (0, db.Server, db.FilePath, p$)

      NSFDbOpen p$, hDB
      Call NSFDbReplicaInfoGet( hDB, R)

      v1=HexStringToLong(Mid(replicaid,1,8))
      v0=HexStringToLong(Mid(replicaid,9,8))
     
      If v1>2147483648 Then
              l1=v1-4294967296
      Else
              l1=v1
      End If
      If v0>2147483648 Then
              l0=v0-4294967296
      Else
              l0=v0
      End If
     
      R.ID(1)=l1
      R.ID(0)=l0
     
      Call NSFDbReplicaInfoSet(hDB, R)        
      Call NSFDbClose(hDB)        
End Sub

Function HexStringToLong(hexstring As String) As Variant
      Dim dez As Variant        
      Dim y As Integer
      Dim x As Long
     
      For x=Len(hexstring)-1 To 0 Step -1
              y=Asc(Mid(hexstring, x+1, 1))
              Select Case y
              Case 48 To 57 '0..9
                      dez=dez+(y-48)*16^(Len(hexstring)-1-x)
              Case 65 To 70 'A..F
                      dez=dez+(y-55)*16^(Len(hexstring)-1-x)
              Case 97 To 102 'a..f
                      dez=dez+(y-87)*16^(Len(hexstring)-1-x)
              End Select
      Next
      hexstringtolong=dez        
End Function  

Comments

1 - This is a code sub from the open source DomainPatrol application that creates replica stubs.

Sub doDatabase(database As NotesDatabase, targetservers As Variant, pathname As String)

On Error Goto errhandler

Dim index As Integer

Dim server As String
Forall targetserver In targetservers

index = index + 1
Call runner.updateProgressBar(targetserver & "!!" & pathname, index , Ubound(targetservers) + 1)

server = targetserver

' Check if there already is a database at the destination
Dim dbr As NotesDatabase
Set dbr = New NotesDatabase(targetserver, pathname)
If dbr.IsOpen Then
Call runner.logError("Database " & targetserver & "!!" & pathname & " could not be created. The target filename is used.")
Delete dbr
Else

' now create the replica stub
Call createReplicaStub(database, server, pathname)

End If

nextServer:
End Forall

Goto eos
errhandler:

If Err = 4005 Then
Call runner.logError("Database " & server & "!!" & pathname & " could not be created. The target server does not respond.")
Resume nextServer
Else
Call runner.logError("Err: " & Err & " Error: " & Error & " Erl: " & Erl & " : " & server & "!!" & pathname)

Resume eos
End If
eos:

End Sub

2 - Sorry - wrong sub - here is the actual code that does something:

Const DBCOPY_REPLICA = &H0001&
Const NOTE_CLASS_NONE = &H0000&

Declare Function W32NSFDbCreateAndCopy Lib "nnotes.dll" Alias "NSFDbCreateAndCopy" _
(Byval srcDb As Lmbcs String,_
Byval dstDb As Lmbcs String,_
Byval NoteClass As Integer,_
Byval limit As Integer,_
Byval flags As Long,_
retHandle As Long) As Integer

Declare Function W32NSFDbClose Lib "nnotes.dll" Alias "NSFDbClose" _
(Byval hDB As Long) As Integer


Function createReplicaStub(sourceDatabase As NotesDatabase, targetServer As String, targetFilePath As String) As Boolean

On Error Goto errHandler

Dim srcDb As String
srcDb = sourceDatabase.Server & "!!" & sourceDatabase.FilePath

Dim dstDb As String
dstDb = targetServer & "!!" & targetFilePath

Dim hDB As Long

Dim CAPIErr As Integer

CAPIErr = W32NSFDbCreateAndCopy (srcDb, dstDb, NOTE_CLASS_NONE, 0, DBCOPY_REPLICA, hDB)

If (CAPIErr = 0) Then
CAPIErr = W32NSFDbClose(hDB)
If (CAPIErr <> 0) Then
Error 1001, "Unable to close database " & dstDb & "; Error: " & Hex$(CAPIErr)
End If
Else
Error 1001, "Unable to create replica at " & dstDb & "; Error: " & Hex$(CAPIErr)
End If

getOut:
Exit Function
errHandler:
On Error Goto 0
Error Err, Error$ & " on line " & Erl & " [in " & Lsi_info(2) & "]"
Resume getOut
End Function

3 - Check if there already is a database at the destination
Dim dbr As NotesDatabase
Set dbr = New NotesDatabase(targetserver, pathname)
If dbr.IsOpen Then
Call runner.logError("Database " & targetserver & "!!" & pathname & " could not be created. The target filename is used.")
Delete dbr
Else [url={<a href="{ Link } watches</a>}
[url={ <a href="{ Link } rel="nofollow" target="blank">Link</a> }

4 - Laughter and tears are both responses to frustration and exhaustion. I myself prefer to laugh, since there is less cleaning up to do afterward.

5 - You know that children are growing up when they start asking questions that have answers.

6 - The solution is to gradually become free of societal rewards and learn how to substitute for them rewards that are under one's own powers. This is not to say that we should abandon every goal endorsed by society; rather, it means that, in addition to or instead of the goals others use to bribe us with, we develop a set of our own.

7 - Keep away from people who try to belittle your ambitions. Small people always do that, but the really great make you feel that you, too, can become great.

8 - I am a firm believer in the people. If given the truth, they can be depended upon to meet any national crises. The great point is to bring them the real facts.

Post A Comment

:-(:-):-D:-o:-p:-x:-\:angry::cool::cry::emb::grin::huh::laugh::lips::rolleyes:;-)