3759 lines
206 KiB
Plaintext
3759 lines
206 KiB
Plaintext
Function Replication_Services(@Service, @Params)
|
|
/***********************************************************************************************************************
|
|
|
|
This program is proprietary and is not to be used by or disclosed to others, nor is it to be copied without written
|
|
permission from SRP Computer Solutions, Inc.
|
|
|
|
Name : Replication_Services
|
|
|
|
Description : Handler program for all module related services.
|
|
|
|
Notes : The generic parameters should contain all the necessary information to process the services. Often
|
|
this will be information like the data Record and Key ID.
|
|
|
|
Parameters :
|
|
Service [in] -- Name of the service being requested
|
|
Param1-10 [in/out] -- Additional request parameter holders
|
|
Response [out] -- Response to be sent back to the Controller (MCP) or requesting procedure
|
|
|
|
History : (Date, Initials, Notes)
|
|
01/11/20 dmb Original programmer.
|
|
01/11/20 dmb Update AddToReplicationQueueTable to bypass suspended tables.
|
|
01/11/20 dmb Update ReplicateTransactions to detect when new tables are being processed within the same
|
|
queue and end the process. This maintains the principle of one queue being processed at any
|
|
time by an engine.
|
|
01/11/20 dmb Add GetSRPEngineServerINI and SetSRPEngineServerINI services.
|
|
01/11/20 dmb Add CreateMasterReplicationQueue service.
|
|
01/12/16 dmb Add SetSourceREVBOOTPath, GetSourceREVBOOTPath, SetTargetREVBOOTPath, and
|
|
GetTargetREVBOOTPath services.
|
|
01/12/20 dmb Add SetDailyNotificationEmails, GetDailyNotificationEmails, SetEmergencyNotificationEmails,
|
|
GetEmergencyNotificationEmails, SetSMTPSettings, and GetSMTPSettings services.
|
|
01/12/20 dmb Obsolete SendAlert service. Add SendEmergencyAlert service.
|
|
01/12/20 dmb Update ReplicateTransactions service to replicate object code along with source code if
|
|
it exists.
|
|
01/12/20 dmb Update ReplicateTransactions service to properly alias the target SYSREPOS table and update
|
|
the Repository pointers as needed.
|
|
02/24/20 dmb Add Push.Select and Pop.Select calls to the GetReplicationQueueTableHandle service to
|
|
avoid unintentional clearing of @LIST_ACTIVE.
|
|
02/24/20 dmb If Attach_Table fails in CreatePrivateReplicationQueue, check to see if it is not an SSP280
|
|
error before assuming this means the table doesn't exist.
|
|
02/24/20 dmb If Attach_Table fails in CreateMasterReplicationQueue, check to see if it is not an SSP280
|
|
error before assuming this means the table doesn't exist.
|
|
02/24/20 dmb Swap @VM with CRLF$ in the FileError variable for the SendEmergencyAlert service.
|
|
02/24/20 dmb Add additional flag in GetReplicationQueueTableHandle to track whether a specific queue is
|
|
already known to exist or not exist. This prevents the service from attempting to attach
|
|
queues unnecessarily and slow down performance.
|
|
02/24/20 dmb Refactor entire service module using Enhanced BASIC+ syntax.
|
|
02/26/20 dmb Update AddToReplicationQueueTable to ignore %RECORDS% Key IDs.
|
|
02/26/20 dmb Add GetReplicationQueueTableStatus and GetAllReplicationQueueTableStatus services.
|
|
02/26/20 dmb Add ResetReplicationQueueCounters service.
|
|
02/27/20 dmb Update ReplicateTransactions service to allow for the possibility that a pending transaction
|
|
in a queue might not be available. Insstead of issuing a warning and aborting the process,
|
|
just flag this as an IGNORE transaction.
|
|
02/27/20 dmb Update ReplicateTransactions service to support the calling of another stored procedure
|
|
to handle the replication process. Used for alternative replication processes, such as when
|
|
the target is a SQL server.
|
|
02/27/20 dmb Update ReplicateTransactions service to detach the source volume as strored in the transaction
|
|
ID before attempting to attach the resolved source volume. This will help to avoid FS404
|
|
errors in cases where the volume was already attached by the application.
|
|
02/28/20 dmb Update ReplicateTransactions service to track source volume changes and set the NewTable
|
|
flag accordingly. This addresses the issue where a table of the same name and same
|
|
Database ID exists (albeit one is aliased). They will both write to the same private queue
|
|
so the source volume must be used to detect a change in the actual source table.
|
|
03/01/20 dmb Update the CreateMasterReplicationQueue service so the HaveQueueTable variable defaults
|
|
to false.
|
|
03/08/20 dmb Update the GetReplicationQueueTables service so Set_Status(0) is called to avoid hearing
|
|
engine dings if there are no tables in the QueuePath passed into the List_Volume_Sub
|
|
subroutine.
|
|
03/12/20 dmb Do not set an error if paths are missing from these services: SetSourceServerPath,
|
|
SetSourceREVBOOTPath, SetSourceQueuePath, SetTargetServerPath, SetTargetREVBOOTPath, and
|
|
SetTargetQueuePath.
|
|
03/12/20 dmb Do not set an error in the SetMaxTransactionsCount if the MaxTransactionsCount argument
|
|
is empty. Only set an error if the value is non-numeric.
|
|
03/12/20 dmb Allow the SetDailyNotificationEmails and SetEmergencyNotificationEmails services to pass in
|
|
an empty argument. This will clear out emails.
|
|
03/28/20 dmb Update the SetSRPEngineServerINI service to support JSON content rather than @FM/@VM content
|
|
for the INI data.
|
|
03/28/20 dmb Update the GetSRPEngineServerINI service to return JSON content rather than @FM/@VM content.
|
|
03/28/20 dmb Add the GetSRPEngineServerTitle service.
|
|
03/29/20 dmb Add the GetSRPEngineServerExeName service. Update the SetSRPEngineServerINI,
|
|
GetSRPEngineServerINI, and GetSRPEngineServerTitle services to use this service.
|
|
08/02/20 dmb Update the ReplicateTransactions service so that if the called Replication Procedure has an
|
|
error the SendEmergencyAlert service will get called.
|
|
08/02/20 dmb Update the SendEmergencyAlert service to use the new SMTP Sent From parameter.
|
|
08/31/20 dmb Update the GetReplicationQueueTableHandle service to check Get_Status after the Fix_LH
|
|
subroutine is called.
|
|
09/09/20 dmb Deprecate and remove the ProcessTransactions service.
|
|
09/09/20 dmb Update the AddToReplicationQueueTable service to not call the GetDisabledTableFlag service.
|
|
It is now the responsibility of the caller (e.g., PROMOTED_WRITE_RECORD_ACTION) to verify
|
|
that the table is allowed to replicate.
|
|
09/09/20 dmb Deprecate and remove the GetDisabledTableFlag service. This has been superceded by the
|
|
IsTableAllowedToQueue and IsTableAllowedToReplicate services.
|
|
09/09/20 dmb Deprecate and remove the AppendToPendingQueue service. This was an artifact of the logic
|
|
to manage OS file based queues.
|
|
09/09/20 dmb Deprecate and remove the SetReplicationVolumes and GetReplicationVolumes services.
|
|
09/09/20 dmb Deprecate and remove the GetInProcessQueueFiles and GetInProcessQueueFile services.
|
|
09/09/20 dmb Update the GetReplicationQueueTableStatus to include the %NumProcess% counter.
|
|
09/12/20 dmb Deprecate the IsTableIncludedForReplication service and replace it with the
|
|
IsReplicationTable service.
|
|
09/12/20 dmb Added the GetVolumeTables and HasBaseMFSInstalled services.
|
|
09/12/20 dmb Deprecate the CreateMasterReplicationQueue service and replcate it with the
|
|
CreateReplicationQueue service.
|
|
09/12/20 dmb Deprecate and remove the GetPendingQueueFiles and GetPendingQueueFile services.
|
|
09/12/20 dmb Update the GetReplicationQueueTable service to use the GetTableQueueType service.
|
|
09/12/20 dmb Deprecate the CreatePrivateReplicationQueue service and replace it with the
|
|
CreateReplicationQueue service.
|
|
09/12/20 dmb Deprecate the DeletePrivateReplicationQueue service and replace it with the
|
|
DeleteReplicationQueue service.
|
|
09/13/20 dmb Add QueueTable argument to the GetReplicationQueueTableHandle service.
|
|
09/13/20 dmb Update the ReplicateTransactions service to handle tables that are suspended from being
|
|
replicated. Introduce a new transaction ID action of SKIP. This is similar to IGNORE, but
|
|
SKIP IDs won't be deleted since they are still valid.
|
|
09/14/20 dmb Update the AddBaseMFSToTable, RemoveBaseMFSFromTable, and HasBaseMFSInstalled services
|
|
to always use SYSPROG as the database when calling Alias_Table for REVMEDIA.
|
|
09/14/20 dmb Add the SetErrorLogPath, GetErrorLogPath, and IsLoggingEnabled services.
|
|
09/14/20 dmb Add the LogError service. Update the ReplicateTransactions service to call the LogError
|
|
service (if logging is enabled) whenever the SendEmergencyAlert service is called.
|
|
09/23/20 dmb Update the ResetReplicationQueueCounters service to use the same updates that were already
|
|
added to the GetReplicationQueueTableStatus service.
|
|
09/23/20 dmb Update the ReplicateTransactions service to verify if the private queue is for a table
|
|
that is allowed to replicate before processing the transactions in the queue. This will
|
|
avoid unnecessary checks against all transactions.
|
|
10/17/20 dmb Remove all hardcoded references to the SYSENV table and use the RepConfigTable$ equate
|
|
instead. This will allow users to define the table that should be used.
|
|
10/18/20 dmb Update the LogError and SendEmergencyAlert services to retreive the current Queue Table and
|
|
Engine Name and include this in the information being submitted.
|
|
02/23/21 dmb Update the ReplicateTransactions service to attach all tables rather than just the table
|
|
being replicated so symbolics will function properly. Set a memory flag to avoid constant
|
|
attach processes.
|
|
|
|
***********************************************************************************************************************/
|
|
|
|
#pragma precomp SRP_PreCompiler
|
|
|
|
$insert LOGICAL
|
|
$insert SERVICE_SETUP
|
|
$insert SRPMAIL_INSERTS
|
|
|
|
Equ CRLF$ to \0D0A\
|
|
Equ Tab$ to \09\
|
|
|
|
|
|
// The default table to store configuration information is SYSENV, but this equate can be changed to point to another
|
|
// table.
|
|
*Equ RepConfigTable$ to 'SYSENV'
|
|
Equ RepConfigTable$ to 'REP_MAN_CONFIG'
|
|
|
|
// Equates that define the <RepConfigTable>\REPLICATION_TABLES structure.
|
|
Equ RepTabTableName$ to 1
|
|
Equ RepTabDatabaseID$ to 2
|
|
Equ RepTabVolume$ to 3
|
|
Equ RepTabHasBaseMFS$ to 4
|
|
Equ RepTabCanQueue$ to 5
|
|
Equ RepTabCanRep$ to 6
|
|
Equ RepTabQueueType$ to 7
|
|
|
|
Common /ReplicationServices/ QueueTableList@, QueueHandleList@, QueueTableExists@, Unused4@, Unused5@, Unused6@, Unused7@, Unused8@
|
|
|
|
Declare function Replication_Services, Memory_Services, GetTickCount, RTI_OS_Directory, SRP_Path, Get_LH_Info
|
|
Declare function SRP_Encode, SRP_Decode, List_Volume_Sub, SRP_Sort_Array, GetCommandLine, UCase, SRP_Stopwatch
|
|
Declare function Environment_Services
|
|
Declare function SRP_Rotate_Array, SRP_Send_Mail, Get.RecCount, SRP_JSON, Database_Services, SRP_Array, Logging_Services
|
|
Declare subroutine Replication_Services, Memory_Services, Fix_LH, Set_Status, Alias_Table, Attach_Table, Detach_Volume
|
|
Declare subroutine SRP_Stopwatch, Clear_Table, Detach_Table, Create_Table, Delete_Table, Send_Info, Repos_Attach_Trans
|
|
Declare subroutine Repos_Detach_Trans, Push.Select, Pop.Select, SRP_JSON, Database_Services, Logging_Services
|
|
|
|
Declare subroutine WritePrivateProfileString, obj_Notes
|
|
Declare subroutine GetPrivateProfileSection, GetPrivateProfileString
|
|
|
|
// SRP HashTable declarations.
|
|
Declare function SRP_HashTable_Create, SRP_HashTable_Contains, SRP_HashTable_Count, SRP_HashTable_Get
|
|
Declare function SRP_HashTable_GetKeys, SRP_HashTable_GetValues, SRP_HashTable_GetKeyValuePairs
|
|
Declare subroutine SRP_HashTable_Set, SRP_HashTable_Release, SRP_HashTable_Remove
|
|
|
|
// SRP List declarations.
|
|
Declare function SRP_List
|
|
Declare subroutine SRP_List
|
|
|
|
LogPath = Environment_Services('GetApplicationRootPath') : '\LogFiles\Replication'
|
|
LogDate = Oconv(Date(), 'D4/')
|
|
LogTime = Oconv(Time(), 'MTS')
|
|
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' Delete Error Log.csv'
|
|
Headers = 'Logging DTM' : @FM : 'Lookup Key ID' : @FM : 'Queue Table Handle'
|
|
objLog = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
|
|
LoggingDTM = LogDate : ' ' : LogTime ; // Logging DTM
|
|
|
|
GoToService else
|
|
Error_Services('Set', Service : ' is not a valid service request within the ' : ServiceModule : ' services module.')
|
|
end
|
|
|
|
Return Response else ''
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Service Parameter Options
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
Options BOOLEAN = True$, False$
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Services
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddToReplicationQueueTable
|
|
//
|
|
// TransactionID - The transaction ID that needs to be added to the queue. Note, this is not a Key ID to a database row.
|
|
// Rather, it is a meaningful identifier. See the notes below. - [Required]
|
|
//
|
|
// Adds the specified transaction ID to the replication queue table for eventual processing. The transaction ID must
|
|
// contain all necessary components for a replication action to succeed and follow this format:
|
|
//
|
|
// <1> - Action (i.e., WRITE, DELETE, CLEARFILE)
|
|
// <2> - Application (OpenInsight application that owns the database table.)
|
|
// <3> - Volume (Volume path, mapped drive or UNC, where the database table live.)
|
|
// <4> - Table (Database table that received the action.)
|
|
// <5> - KeyID (If application, the database row Key ID related to the action.)
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddToReplicationQueueTable(TransactionID)
|
|
|
|
// Verify that this is a source (i.e., production) server.
|
|
ServerType = Replication_Services('GetServerType')
|
|
If ServerType _EQC 'Source' then
|
|
Action = TransactionID[1, @FM]
|
|
Application = TransactionID[Col2() + 1, @FM]
|
|
Volume = TransactionID[Col2() + 1, @FM]
|
|
Table = TransactionID[Col2() + 1, @FM]
|
|
KeyID = TransactionID[Col2() + 1, 999]
|
|
LookupKeyID = Action:'*':Table:'*':KeyID
|
|
|
|
QueueTableName = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
|
|
AddTransaction = True$ ; // Assume this is a valid transaction to add to the queue for now.
|
|
|
|
Begin Case
|
|
Case (Table _EQC 'SYSPROCS' AND (KeyID[1, 15] _EQC 'SRP_EDITOR_TEMP' OR KeyID _EQC 'VARDECL_'))
|
|
// Data row is either a temp record created by the SRP Editor needed to support inherited stored
|
|
// procedures or it is the special VARDECL_ record created by the compiler.
|
|
AddTransaction = False$
|
|
|
|
Case IndexC(Volume, Replication_Services('GetTargetServerPath'), 1)
|
|
// Volume for this table is located on the target server. This occurs with the ReplicateTransactions
|
|
// service is writing a row to the target table.
|
|
AddTransaction = False$
|
|
|
|
Case IndexC(Volume, Replication_Services('GetTargetREVBOOTPath'), 1)
|
|
// Volume for this table is located on the target server. This occurs with the ReplicateTransactions
|
|
// service is writing a row to the target table.
|
|
AddTransaction = False$
|
|
|
|
Case KeyID _EQC '%RECORDS%'
|
|
// Do not replicate QuickDex records.
|
|
AddTransaction = False$
|
|
|
|
Case TransactionID EQ ''
|
|
// This is an invalid TransactionID.
|
|
AddTransaction = False$
|
|
|
|
Case RowExists(QueueTableName, LookupKeyID)
|
|
// This is a duplicate transaction.
|
|
AddTransaction = False$
|
|
|
|
End Case
|
|
|
|
If AddTransaction then
|
|
hQueueTable = Replication_Services('GetReplicationQueueTableHandle', Application, Table)
|
|
|
|
If hQueueTable NE '' then
|
|
CounterKeyID = '%NextPendingSK%'
|
|
If Replication_Services('LockReplicationQueueCounter', hQueueTable, CounterKeyID) then
|
|
// The counter value represents the next available sequence. Do not increase it until it is successfully
|
|
// used.
|
|
Read Counter from hQueueTable, CounterKeyID then
|
|
If Num(Counter) else
|
|
Counter = 1
|
|
end
|
|
end else
|
|
Counter = 1
|
|
end
|
|
|
|
DummyLookupRec = ''
|
|
Write DummyLookupRec to hQueueTable, LookupKeyID then
|
|
Write TransactionID to hQueueTable, Counter then
|
|
Counter += 1
|
|
Write Counter to hQueueTable, CounterKeyID else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, Volume, 'ReplicationQueue for ' : Table, CounterKeyID, StatusCode, @File_Error)
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, Volume, 'ReplicationQueue for ' : Table, Counter, StatusCode, @File_Error)
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, Volume, 'ReplicationQueue for ' : Table, CounterKeyID, StatusCode, @File_Error)
|
|
end
|
|
|
|
Replication_Services('UnlockReplicationQueueCounter', hQueueTable, CounterKeyID)
|
|
end else
|
|
// Unable to lock pending queue. Send an alert.
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Replication_Services('SendEmergencyAlert', Service, 'LOCK', Application, Volume, 'ReplicationQueue for ' : Table, CounterKeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// LockReplicationQueueCounter
|
|
//
|
|
// QueueTableHandle - Handle to the database table holding the replication queue transactions. - [Required]
|
|
// CounterKeyID - Key ID to the counter for the indicated queue table. - [Required]
|
|
//
|
|
// Returns a True if the pending queue counter is properly locked. Since locks on the queue file should be very short,
|
|
// this routine will only attempt to lock a queue file for a couple of seconds before sending out an alert that
|
|
// something unexpected has occured.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service LockReplicationQueueCounter(QueueTableHandle, CounterKeyID)
|
|
|
|
ReplicationQueueCounterLocked = False$ ; // Assume no lock for now.
|
|
|
|
If QueueTableHandle NE '' AND CounterKeyID NE '' then
|
|
// Use GetTickCount to track the duration of the lock attempt in milliseconds. After 2 seconds then the
|
|
// lock attempt should be aborted.
|
|
StartAttempt = GetTickCount()
|
|
Loop
|
|
Lock QueueTableHandle, CounterKeyID then ReplicationQueueCounterLocked = True$
|
|
CurrentAttempt = GetTickCount()
|
|
AttemptDuration = CurrentAttempt - StartAttempt
|
|
Until ReplicationQueueCounterLocked OR (AttemptDuration GT 2000)
|
|
Repeat
|
|
If Not(ReplicationQueueCounterLocked) then Error_Services('Add', 'Unable to lock ' : CounterKeyID : ' in the ' : Service : ' service.')
|
|
end else
|
|
Error_Services('Add', 'QueueTableHandle or CounterKeyID arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ReplicationQueueCounterLocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// LockPendingQueue
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table whose queue file needs to be locked. - [Required]
|
|
//
|
|
// Returns a True if the pending queue file is properly locked. Since locks on the queue file should be very short,
|
|
// this routine will only attempt to lock a queue file for a couple of seconds before sending out an alert that
|
|
// something unexpected has occured.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service LockPendingQueue(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
PendingQueueLocked = False$ ; // Assume no lock for now.
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
hRepConfigTable = Memory_Services('GetValue', 'hRepConfigTable')
|
|
If Len(hRepConfigTable) else
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
Memory_Services('SetValue', 'hRepConfigTable', hRepConfigTable)
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
// Use GetTickCount to track the duration of the lock attempt in milliseconds. After 2 seconds then the
|
|
// lock attempt should be aborted.
|
|
StartAttempt = GetTickCount()
|
|
LockKeyID = Application : '*' : Table : '*' : 'Pending'
|
|
Loop
|
|
Lock hRepConfigTable, LockKeyID then PendingQueueLocked = True$
|
|
CurrentAttempt = GetTickCount()
|
|
AttemptDuration = CurrentAttempt - StartAttempt
|
|
Until PendingQueueLocked OR (AttemptDuration GT 2000)
|
|
Repeat
|
|
If Not(PendingQueueLocked) then Error_Services('Add', 'Unable to lock the pending queue for ' : Application : '*' : Table : ' in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = PendingQueueLocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UnlockReplicationQueueCounter
|
|
//
|
|
// QueueTableHandle - Handle to the database table holding the replication queue transactions. - [Required]
|
|
// CounterKeyID - Key ID to the counter for the indicated queue table. - [Required]
|
|
//
|
|
// Returns a True if the pending queue counter is properly unlocked.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service UnlockReplicationQueueCounter(QueueTableHandle, CounterKeyID)
|
|
|
|
ReplicationQueueCounterUnlocked = False$ ; // Assume not unlocked for now.
|
|
|
|
If QueueTableHandle NE '' AND CounterKeyID NE '' then
|
|
Unlock QueueTableHandle, CounterKeyID then
|
|
ReplicationQueueCounterUnlocked = True$
|
|
end else
|
|
Error_Services('Add', 'Unable to unlock ' : CounterKeyID : ' in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'QueueTableHandle or CounterKeyID arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ReplicationQueueCounterUnlocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UnlockPendingQueue
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table whose queue file needs to be unlocked. - [Required]
|
|
//
|
|
// Returns a True if the pending queue file is properly unlocked.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service UnlockPendingQueue(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
PendingQueueUnlocked = False$ ; // Assume not unlocked for now.
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
hRepConfigTable = Memory_Services('GetValue', 'hRepConfigTable')
|
|
If Len(hRepConfigTable) else
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
Memory_Services('SetValue', 'hRepConfigTable', hRepConfigTable)
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
LockKeyID = Application : '*' : Table : '*' : 'Pending'
|
|
Unlock hRepConfigTable, LockKeyID then
|
|
PendingQueueUnlocked = True$
|
|
end else
|
|
Error_Services('Add', 'Unable to unlock the pending queue for ' : Application : '*' : Table : ' in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = PendingQueueUnlocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// LockInProcessQueue
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table whose queue file needs to be locked. - [Required]
|
|
//
|
|
// Returns a True if the in-process queue file is properly locked. Since locks on the queue file should be very short,
|
|
// this routine will only attempt to lock a queue file for a couple of seconds before sending out an alert that
|
|
// something unexpected has occured.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service LockInProcessQueue(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
InProcessQueueLocked = False$ ; // Assume no lock for now.
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
hRepConfigTable = Memory_Services('GetValue', 'hRepConfigTable')
|
|
If Len(hRepConfigTable) else
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
Memory_Services('SetValue', 'hRepConfigTable', hRepConfigTable)
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
// Use GetTickCount to track the duration of the lock attempt in milliseconds. After 2 seconds then the
|
|
// lock attempt should be aborted.
|
|
StartAttempt = GetTickCount()
|
|
LockKeyID = Application : '*' : Table : '*' : 'InProcess'
|
|
Loop
|
|
Lock hRepConfigTable, LockKeyID then InProcessQueueLocked = True$
|
|
CurrentAttempt = GetTickCount()
|
|
AttemptDuration = CurrentAttempt - StartAttempt
|
|
Until InProcessQueueLocked OR (AttemptDuration GT 2000)
|
|
Repeat
|
|
If Not(InProcessQueueLocked) then Error_Services('Add', 'Unable to lock the in-process queue for ' : Application : '*' : Table : ' in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = InProcessQueueLocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// UnlockInProcessQueue
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table whose queue file needs to be unlocked. - [Required]
|
|
//
|
|
// Returns a True if the in-process queue file is properly unlocked.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service UnlockInProcessQueue(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
InProcessQueueUnlocked = False$ ; // Assume not unlocked for now.
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
hRepConfigTable = Memory_Services('GetValue', 'hRepConfigTable')
|
|
If Len(hRepConfigTable) else
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
Memory_Services('SetValue', 'hRepConfigTable', hRepConfigTable)
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
If Error_Services('NoError') then
|
|
LockKeyID = Application : '*' : Table : '*' : 'InProcess'
|
|
Unlock hRepConfigTable, LockKeyID then
|
|
InProcessQueueUnlocked = True$
|
|
end else
|
|
Error_Services('Add', 'Unable to unlock the in-process queue for ' : Application : '*' : Table : ' in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = InProcessQueueUnlocked
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetMaxTransactionsCount
|
|
//
|
|
// MaxTransactionsCount - The maximum number of transactions that will be processed in one sesion. - [Required]
|
|
//
|
|
// Sets the maximum number of transactions that should be processed at one time.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetMaxTransactionsCount(MaxTransactionsCount)
|
|
|
|
If MaxTransactionsCount GT 0 AND Num(MaxTransactionsCount) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_MAX_TRANSACTION_COUNT'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write MaxTransactionsCount to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing max transaction count in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking max transaction count in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
If Num(MaxTransactionsCount) else Error_Services('Add', 'MaxTransactionsCount argument is not a valid number in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetReplicationProcedure
|
|
//
|
|
// ReplicationProcedure - the name of the custom stored procedure that will handle replications. - [Optional]
|
|
//
|
|
// Sets the the name of the custom stored procedure that will handle replications.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetReplicationProcedure(ReplicationProcedure)
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_REPLICATION_PROCEDURE'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write ReplicationProcedure to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing replication procedure in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking replication procedure in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ResetReplicationQueueCounters
|
|
//
|
|
// QueueTable - Name of the replication queue table. - [Required]
|
|
//
|
|
// Resets the replication queue counters based on the transaction rows in the queue table. Returns the same information
|
|
// in the same format as the GetReplicationQueueTableStatus service (%EngineName% value, %NextInProcessSK% value,
|
|
// %NextPendingSK% value, and total estimated number of rows in the indicated queue table).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ResetReplicationQueueCounters(QueueTable)
|
|
|
|
Convert @Lower_Case to @Upper_Case in QueueTable
|
|
|
|
QueueTableStatus = ''
|
|
|
|
If QueueTable NE '' then
|
|
QueuePath = Replication_Services('GetSourceQueuePath')
|
|
If Error_Services('NoError') then
|
|
Set_Status(0)
|
|
ShouldDetach = False$
|
|
// First attempt to open the private replication queue table.
|
|
Open QueueTable to hQueueTable else
|
|
// Since the Open statement didn't work, attempt to attach the private replication queue table.
|
|
Set_Status(0)
|
|
Attach_Table(QueuePath : '', QueueTable, 'GLOBAL', '')
|
|
If Get_Status(StatusCode) then
|
|
Error_Services('Add', 'Unable to attach ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end else
|
|
// Make sure table is detached since it was not attached already.
|
|
ShouldDetach = True$
|
|
Set_Status(0)
|
|
Open QueueTable to hQueueTable else
|
|
Set_Status(0)
|
|
Error_Services('Add', 'Unable to open ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end
|
|
If Error_Services('NoError') then
|
|
Select hQueueTable
|
|
EndOfCursor = False$
|
|
NextInProcessSK = 9999999
|
|
NextPendingSK = 1
|
|
Loop
|
|
Readnext TransactionID then
|
|
If Num(TransactionID) then
|
|
If TransactionID GT NextPendingSK then NextPendingSK = TransactionID
|
|
If TransactionID LT NextInProcessSK then NextInProcessSK = TransactionID
|
|
end
|
|
end else
|
|
EndOfCursor = True$
|
|
end
|
|
Until EndOfCursor EQ True$
|
|
Repeat
|
|
|
|
If NextInProcessSK EQ 9999999 then NextInProcessSK = 1
|
|
|
|
If NextPendingSK GT 1 then NextPendingSK += 1
|
|
|
|
TotalRows = Get.RecCount(hQueueTable, Status, False$)
|
|
// If any of the queue counters have values, adjust the TotalRows count accordingly.
|
|
Read OldNextInProcessSK from hQueueTable, '%NextInProcessSK%' then
|
|
TotalRows -= 1
|
|
end
|
|
Write NextInProcessSK to hQueueTable, '%NextInProcessSK%' else
|
|
Error_Services('Add', 'Error updating the %NextInProcessSK% counter in ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end
|
|
Read OldNextPendingSK from hQueueTable, '%NextPendingSK%' then
|
|
TotalRows -= 1
|
|
end
|
|
Write NextPendingSK to hQueueTable, '%NextPendingSK%' else
|
|
Error_Services('Add', 'Error updating the %NextPendingSK% counter in ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end
|
|
Read EngineName from hQueueTable, '%EngineName%' then
|
|
TotalRows -= 1
|
|
end else
|
|
EngineName = ''
|
|
end
|
|
Read NumProcesses from hQueueTable, '%NumProcesses%' then
|
|
TotalRows -= 1
|
|
end else
|
|
NumProcesses = 0
|
|
end
|
|
QueueTableStatus = EngineName : @VM : NextInProcessSK : @VM : NextPendingSK : @VM : TotalRows : @VM : NumProcesses
|
|
Set_Status(0)
|
|
If ShouldDetach = True$ then Detach_Table(QueueTable)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'QueueTable argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = QueueTableStatus
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetMaxTransactionsCount
|
|
//
|
|
// Returns the maximum number of transactions that should be processed at one time.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetMaxTransactionsCount()
|
|
|
|
MaxTransactionsCount = 0
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_MAX_TRANSACTION_COUNT'
|
|
Read MaxTransactionsCount from hRepConfigTable, RepConfigKeyID else MaxTransactionsCount = 0
|
|
If MaxTransactionsCount EQ 0 then
|
|
Error_Services('Add', 'The max transaction count has not been setup in the ' : RepConfigTable$ : ' table.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = MaxTransactionsCount
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetReplicationProcedure
|
|
//
|
|
// Returns the name of the custom stored procedure that will handle replications.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetReplicationProcedure()
|
|
|
|
ReplicationProcedure = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_REPLICATION_PROCEDURE'
|
|
Read ReplicationProcedure from hRepConfigTable, RepConfigKeyID else ReplicationProcedure = ''
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ReplicationProcedure
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// ReplicateTransactions
|
|
//
|
|
// Primary service to take transactions from the replication queue tables and process them. This service will be called
|
|
// multiple times by the SRP Engine Server. Therefore, it does not need to run in a loop. See the comments inline below
|
|
// to get the basic work flow.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service ReplicateTransactions()
|
|
|
|
// Verify if error logging is enabled.
|
|
LoggingEnabled = Replication_Services('IsLoggingEnabled')
|
|
|
|
// Verify that this is a source (i.e., production) server.
|
|
ServerType = Replication_Services('GetServerType')
|
|
|
|
// Verify that the system is enabled and not stopped before replication any transactions.
|
|
SystemEnabledFlag = Replication_Services('GetSystemEnabledFlag')
|
|
SystemStopFlag = Replication_Services('GetSystemStopFlag')
|
|
|
|
If SystemEnabledFlag AND Not(SystemStopFlag) then
|
|
|
|
Begin Case
|
|
Case ServerType _EQC 'Source'
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
//
|
|
// 1. Check for replication queue tables that need to be processed. This will be determined based on the
|
|
// NextInProcess counter value and the NextPending counter value. If the NextInProcess counter is less
|
|
// than the NextPending counter then there are transactions in this replication queue table that are ready
|
|
// to be process.
|
|
//
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
CommandLine = GetCommandLine()
|
|
Swap '/S=' with @FM in CommandLine
|
|
EngineName = CommandLine<2> ; // This is the NamedPipe identifier for the engine processing this service.
|
|
HaveQueueTable = False$ ; // Assume there is no queue table that is ready to be processed for now.
|
|
QueueTableStatuses = Replication_Services('GetAllReplicationQueueTableStatus')
|
|
If Len(QueueTableStatuses) then
|
|
// There are replication queue tables. Sort by number of processes to prioritize and then attempt
|
|
// to processs.
|
|
QueueTableStatuses = SRP_Array('SortRows', QueueTableStatuses, 'AN6', 'LIST', @FM, @VM)
|
|
ReplicationQueueTables = SRP_Array('Rotate', QueueTableStatuses, @FM, @VM)<1>
|
|
Convert @VM to @FM in ReplicationQueueTables
|
|
QueueTableCnt = 0
|
|
NumQueueTables = DCount(ReplicationQueueTables, @FM)
|
|
Loop
|
|
QueueTableCnt += 1
|
|
QueueTable = ReplicationQueueTables<QueueTableCnt>
|
|
hQueueTable = Replication_Services('GetReplicationQueueTableHandle', '', '', QueueTable)
|
|
PendingSKID = '%NextPendingSK%'
|
|
InProcessSKID = '%NextInProcessSK%'
|
|
HaveQueueTable = Replication_Services('LockReplicationQueueCounter', hQueueTable, InProcessSKID)
|
|
// Increment the process count for this queue table. This will help prioritize queue tables that
|
|
// haven't been processed.
|
|
Read NumProcesses from hQueueTable, '%NumProcesses%' then
|
|
If NumProcesses GT 10000 then NumProcesses = 0
|
|
end else
|
|
NumProcesses = 0
|
|
end
|
|
NumProcesses += 1
|
|
Write NumProcesses to hQueueTable, '%NumProcesses%' else Null
|
|
If HaveQueueTable then
|
|
// Verify that this table has a Next In Process counter with a lower value than the Next Pending
|
|
// counter.
|
|
Read NextPendingSK from hQueueTable, PendingSKID else NextPendingSK = 1
|
|
Read FirstInProcessSK from hQueueTable, InProcessSKID else FirstInProcessSK = 1
|
|
If FirstInProcessSK EQ NextPendingSK then
|
|
HaveQueueTable = False$
|
|
Replication_Services('UnlockReplicationQueueCounter', hQueueTable, InProcessSKID)
|
|
end else
|
|
If (QueueTable NE 'REPLICATION_QUEUE_GLOBAL_PUBLIC') AND (QueueTable NE 'REPLICATION_QUEUE_GLOBAL_MASTER') then
|
|
// This is a private queue. Verify the table name using this private queue and then
|
|
// check to see if it is permitted to replicate.
|
|
Read TransactionID from hQueueTable, FirstInProcessSK then
|
|
Action = TransactionID[1, @FM]
|
|
Application = TransactionID[Col2() + 1, @FM]
|
|
SourceVolume = TransactionID[Col2() + 1, @FM]
|
|
Table = TransactionID[Col2() + 1, @FM]
|
|
KeyID = TransactionID[Col2() + 1, 999]
|
|
AllowedToReplicate = Replication_Services('IsTableAllowedToReplicate', Table, Application)
|
|
If Not(AllowedToReplicate) then
|
|
HaveQueueTable = False$
|
|
Replication_Services('UnlockReplicationQueueCounter', hQueueTable, InProcessSKID)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Until (QueueTableCnt GE NumQueueTables) OR (HaveQueueTable)
|
|
Repeat
|
|
end
|
|
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
//
|
|
// 2. Loop through the next group of un-processed transactions and replicate them.
|
|
//
|
|
//--------------------------------------------------------------------------------------------------------------
|
|
If HaveQueueTable then
|
|
// Get the number of pending transactions.
|
|
NumPendingTransactions = NextPendingSK - FirstInProcessSK
|
|
If NumPendingTransactions GT 0 then
|
|
// Note the engine that is managing this queue.
|
|
Write EngineName to hQueueTable, '%EngineName%' else Null
|
|
// Get the maximum number of transactions to process.
|
|
TransactionCount = Replication_Services('GetMaxTransactionsCount')
|
|
If NumPendingTransactions GT TransactionCount then NumPendingTransactions = TransactionCount
|
|
LastInProcessSK = FirstInProcessSK + (NumPendingTransactions - 1)
|
|
ReplicationProcedure = Replication_Services('GetReplicationProcedure')
|
|
|
|
hHashTable = SRP_HashTable_Create(False$, NumPendingTransactions)
|
|
If hHashTable NE '' then
|
|
SourceServerPath = '' ; // Initialize the variable. Only call the GetSourceServerPath if this is empty.
|
|
NewTable = False$ ; // Flag indicating if a new table in the queue is encountered.
|
|
// This will cause the current replication process to abort since
|
|
// only one table can be processed at a time.
|
|
FirstTable = '' ; // Records the name of the first table in the queue.
|
|
FirstVolume = '' ; // Records the name of the first volume in the queue.
|
|
FirstSkippedSK = '' ; // The first transaction ID that was skipped. This will be used to reset the next in-process SK.
|
|
|
|
// Run through the list of tranactions twice. The first pass is to read any tranasctions that need
|
|
// to be written to the target table and store them in a hash table. This is so only one
|
|
// attach/detach needs to be done against the source and target servers for optimum performance.
|
|
For InProcessSK = FirstInProcessSK to LastInProcessSK
|
|
// Update the counter with the current value. If the process aborts, the counter will be set to
|
|
// the last value that has not yet been processed. This allows any future processing to
|
|
// correctly pick up where this left off.
|
|
Write InProcessSK to hQueueTable, InProcessSKID then
|
|
Read TransactionID from hQueueTable, InProcessSK then
|
|
Action = TransactionID[1, @FM]
|
|
Application = TransactionID[Col2() + 1, @FM]
|
|
SourceVolume = TransactionID[Col2() + 1, @FM]
|
|
Table = TransactionID[Col2() + 1, @FM]
|
|
KeyID = TransactionID[Col2() + 1, 999]
|
|
|
|
AllowedToReplicate = Replication_Services('IsTableAllowedToReplicate', Table, Application)
|
|
If AllowedToReplicate EQ True$ then
|
|
SRP_HashTable_Set(hHashTable, 'TransactionID*' : InProcessSK, TransactionID)
|
|
|
|
// Copy the SourceVolume variable since it can be updated by the following
|
|
// processes.
|
|
OriginalSourceVolume = SourceVolume
|
|
|
|
// Check to see if the queue is presenting a new table. If so, then end the loop so
|
|
// only transactions connected to the same table are processed. A new table is
|
|
// evident if the table name changes or the volume changes (i.e., the table name
|
|
// might be the same but it is from a different volume).
|
|
If (FirstTable NE '') AND (Table NE FirstTable) then NewTable = True$
|
|
If (FirstVolume NE '') AND (SourceVolume NE FirstVolume) then NewTable = True$
|
|
|
|
If NewTable then
|
|
// Set the end of the counter to the previous value so the current loop will
|
|
// end. The next loop sequence will pick up the new table.
|
|
LastInProcessSK = InProcessSK - 1
|
|
end else
|
|
If SourceServerPath EQ '' then
|
|
// This must be the first iteration in the loop. Attempt to open the table that the
|
|
// source row resides in. However, make sure that any paths are resolved to the
|
|
// official absolute path (which should normally be in UNC format). The SourceVolume
|
|
// variable will likely contain a mapped drive and this might not be available to
|
|
// the replication service.
|
|
SourceServerPath = UCase(Replication_Services('GetSourceServerPath'))
|
|
FirstTable = Table ; // Record the first table in the queue for future reference.
|
|
FirstVolume = SourceVolume ; // Record the first volume in the queue for future reference.
|
|
SourceVolumeType = ''
|
|
Begin Case
|
|
Case SRP_Path('IsUNC', SourceVolume)
|
|
// This path is already in UNC format so nothing needs to be
|
|
// done.
|
|
Transfer SourceVolume to ResolvedSourceVolume
|
|
SourceVolumeType = 'UNC'
|
|
|
|
Case SourceVolume _EQC 'REVBOOT'
|
|
// This is the REVBOOT volume which probably means this is a
|
|
// system table. Use REVBOOT rather than a resolved to the source
|
|
// REVBOOT path. Otherwise, an SSP280 error will result.
|
|
ResolvedSourceVolume = SourceVolume
|
|
SourceVolumeType = 'REVBOOT'
|
|
|
|
Case SRP_Path('IsRelative', SourceVolume)
|
|
// This is a relative path so it needs to be converted to a full
|
|
// UNC path. These paths are relative to REVBOOT, so the source
|
|
// REVBOOT path needs to be retrieved.
|
|
SourceREVBOOTPath = Replication_Services('GetSourceREVBOOTPath')
|
|
ResolvedSourceVolume = SRP_Path('Combine', SourceREVBOOTPath, SourceVolume)
|
|
SourceVolumeType = 'Relative'
|
|
|
|
Case Otherwise$
|
|
// This must be a local or remote drive. If this is remote then convert
|
|
// to UNC using the source server path.
|
|
If SRP_Path('IsNetworkPath', SourceVolume) then
|
|
SourceVolume[1, 3] = '' ; // Remove the mapped drive
|
|
ResolvedSourceVolume = SRP_Path('Combine', SourceServerPath, SourceVolume)
|
|
SourceVolumeType = 'UNC'
|
|
end else
|
|
Transfer SourceVolume to ResolvedSourceVolume
|
|
SourceVolumeType = 'Local'
|
|
end
|
|
End Case
|
|
// Attempt to detach the original source volume as a precaution against potential FS404 errors.
|
|
// Even if it is unsuccessful don't do anything as it might be that the volume was never attach.
|
|
IsAttached = Memory_Services('GetValue', Service : '*TABLES_ATTACHED')
|
|
If Not(IsAttached) then
|
|
Set_Status(0)
|
|
Detach_Volume(OriginalSourceVolume : '', Success)
|
|
Set_Status(0)
|
|
Attach_Table(ResolvedSourceVolume : '', '', Application, '')
|
|
If Get_Status(StatusCode) then
|
|
// Error handling goes here.
|
|
end else
|
|
Memory_Services('SetValue', Service : '*TABLES_ATTACHED', True$)
|
|
end
|
|
end
|
|
If Get_Status(StatusCode) then
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'ATTACH', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'ATTACH', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end else
|
|
Open Table to hSourceTable then
|
|
If Table _EQC 'SYSPROCS' then
|
|
// The data row is coming from SYSPROCS, so it is likely to be
|
|
// source code. The object code should also be pulled and replicated
|
|
// with the source.
|
|
Open 'SYSOBJ' to hSourceSysObj else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'OPEN', Application, ResolvedSourceVolume, 'SYSOBJ', KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'OPEN', Application, ResolvedSourceVolume, 'SYSOBJ', KeyID, StatusCode, @File_Error)
|
|
end
|
|
Open 'SYSREPOS' to hSourceSysRepos else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'OPEN', Application, ResolvedSourceVolume, 'SYSREPOS', KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'OPEN', Application, ResolvedSourceVolume, 'SYSREPOS', KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'OPEN', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'OPEN', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end
|
|
|
|
If Action _EQC 'WRITE' then
|
|
Read SourceRow from hSourceTable, KeyID then
|
|
SRP_HashTable_Set(hHashTable, KeyID, SourceRow)
|
|
If Table _EQC 'SYSPROCS' then
|
|
Read SourceRowObj from hSourceSysObj, '$' : KeyID then
|
|
SourceRowObj64 = SRP_Encode(SourceRowObj, 'BASE64')
|
|
SRP_HashTable_Set(hHashTable, '$' : KeyID, SourceRowObj64)
|
|
end
|
|
SourceApplication = Field(KeyID, '*', 2, 1)
|
|
If SourceApplication EQ '' then SourceApplication = 'SYSPROG'
|
|
SysReposSourceKeyID = SourceApplication : '*STPROC**' : KeyID[1, '*']
|
|
Read SourceReposStprocRow from hSourceSysRepos, SysReposSourceKeyID then
|
|
SRP_HashTable_Set(hHashTable, SysReposSourceKeyID, SourceReposStprocRow)
|
|
end
|
|
SysReposObjKeyID = SourceApplication : '*STPROCEXE**' : KeyID[1, '*']
|
|
Read SourceReposStprocExeRow from hSourceSysRepos, SysReposObjKeyID then
|
|
SRP_HashTable_Set(hHashTable, SysReposObjKeyID, SourceReposStprocExeRow)
|
|
end
|
|
end
|
|
end else
|
|
If @File_Error<1> EQ 100 then
|
|
// This means the row did not exist in the table. However, this is
|
|
// quite possible if there has been more activity with this row
|
|
// that deleted it prior to this transaction being processed.
|
|
SRP_HashTable_Set(hHashTable, 'TransactionID*' : InProcessSK, 'IGNORE')
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'READ', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'READ', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
// SKIP is different than IGNORE in that it should be kept in the queue for future
|
|
// processing. This is likely when the table has been temporarily set so that
|
|
// it shouldn't be replicated even though queue transactions exist.
|
|
SRP_HashTable_Set(hHashTable, 'TransactionID*' : InProcessSK, 'SKIP')
|
|
If FirstSkippedSK EQ '' then FirstSkippedSK = InProcessSK
|
|
end
|
|
end else
|
|
// On some occassions it is possible for gaps to occur in the transaction ID sequence within a queue table.
|
|
// Since the transaction ID cannot be read, flag it as a SKIP item but do not set the FirstSkippedSK variable
|
|
// since this is not a real transaction ID.
|
|
Set_Status(0)
|
|
SRP_HashTable_Set(hHashTable, 'TransactionID*' : InProcessSK, 'SKIP')
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, InProcessSKID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, InProcessSKID, StatusCode, @File_Error)
|
|
end
|
|
Until NewTable
|
|
Next InProcessSK
|
|
TargetServerPath = '' ; // Initialize the variable. Only call the GetTargetServerPath if this is empty.
|
|
ResolvedTargetVolume = '' ; // Initialize the variable.
|
|
|
|
// Make a second pass through the list of transactions. This time to apply the actions against the
|
|
// target table.
|
|
For InProcessSK = FirstInProcessSK to LastInProcessSK
|
|
// Update the counter with the current value. If the process aborts, the counter will be set to the last
|
|
// value that has not yet been processed. This allows any future processing to correctly pick up where
|
|
// this left off.
|
|
ActionCompleted = False$
|
|
TransactionID = SRP_HashTable_Get(hHashTable, 'TransactionID*' : InProcessSK)
|
|
Action = TransactionID[1, @FM]
|
|
Application = TransactionID[Col2() + 1, @FM]
|
|
SourceVolume = TransactionID[Col2() + 1, @FM]
|
|
Table = TransactionID[Col2() + 1, @FM]
|
|
KeyID = TransactionID[Col2() + 1, 999]
|
|
|
|
If Action _NEC 'SKIP' then
|
|
If ReplicationProcedure then
|
|
// There is a replication procedure. Call it with the TransactionID. Check Error_Services
|
|
// to see if there were any issue. If not, assume the action was completed.
|
|
If Action _EQC 'IGNORE' then
|
|
// If a normal action (e.g., WRITE) cannot be processed due to normal reasons, the
|
|
// action is dynamically changed to IGNORE so that the service can continue without
|
|
// erroring out. The transaction ID will also be deleted.
|
|
ActionCompleted = True$
|
|
end else
|
|
Call @ReplicationProcedure(TransactionID)
|
|
If Error_Services('NoError') then
|
|
ActionCompleted = True$
|
|
end else
|
|
Message = Error_Services('GetMessage')
|
|
If IndexC(Message, 'The OITableName, OIKeyID, or OIRow argument was missing', 1) then
|
|
Error_Services('Clear')
|
|
// Remove the transaction from the queue
|
|
Delete hQueueTable, InProcessSK else
|
|
// The transaction ID might not exist due to SKIPing. Just clear the error and move on.
|
|
Set_Status(0)
|
|
end
|
|
// Remove the lookup key from the queue
|
|
LookupKeyID = Action:'*':Table:'*':KeyID
|
|
Delete hQueueTable, LookupKeyID else
|
|
// The lookup key might not exist due to SKIPing. Just clear the error and move on.
|
|
Set_Status(0)
|
|
// Log failure
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = LookupKeyID
|
|
LogData<3> = hQueueTable
|
|
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
|
|
end
|
|
// Send email alert
|
|
Message := ' Table: ':Table:' Key: ':KeyID
|
|
Replication_Services('SendAlert', Service, ReplicationProcedure, Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error, '', Message)
|
|
// Send an internal OI message to OI admins
|
|
Recipients = Xlate('SEC_GROUPS', 'OI_ADMIN', 'USER', 'X')
|
|
SentFrom = 'SYSTEM'
|
|
Subject = 'Replication Manager Error'
|
|
Message = 'The OITableName, OIKeyID, or OIRow argument was missing in the ' : Service : ' service. Table: ':Table:' Key: ':KeyID
|
|
AttachWindow = ''
|
|
AttachKey = ''
|
|
SendToGroup = ''
|
|
Parms = Recipients:@RM:SentFrom:@RM:Subject:@RM:Message:@RM:AttachWindow:@RM:AttachKey:@RM:SendToGroup
|
|
obj_Notes('Create',Parms)
|
|
end else
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, ReplicationProcedure, Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error, '', Message)
|
|
Replication_Services('SendEmergencyAlert', Service, ReplicationProcedure, Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error, '', Message)
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
// There is no replication procedure specified. Perform the replication procedure using
|
|
// normal OpenInsight to OpenInsight logic.
|
|
If TargetServerPath EQ '' then
|
|
// This must be the first iteration in the loop. Attempt to open the target table. However,
|
|
// make sure that any paths are resolved to the official source server path (which should be
|
|
// in UNC format).
|
|
Set_Status(0)
|
|
If SourceVolumeType _EQC 'REVBOOT' then
|
|
Success = True$
|
|
end else
|
|
Detach_Volume(ResolvedSourceVolume : '', Success)
|
|
end
|
|
If Success EQ True$ then
|
|
Set_Status(0)
|
|
TargetServerPath = UCase(Replication_Services('GetTargetServerPath'))
|
|
Begin Case
|
|
Case SourceVolumeType _EQC 'UNC'
|
|
ResolvedTargetVolume = ResolvedSourceVolume
|
|
Swap SourceServerPath with TargetServerPath in ResolvedTargetVolume
|
|
|
|
Case SourceVolumeType _EQC 'REVBOOT'
|
|
ResolvedTargetVolume = SRP_Path('RemoveBackslash', Replication_Services('GetTargetREVBOOTPath'))
|
|
|
|
Case SourceVolumeType _EQC 'Relative'
|
|
TargetREVBOOTPath = Replication_Services('GetTargetREVBOOTPath')
|
|
ResolvedTargetVolume = ResolvedSourceVolume
|
|
Swap SourceREVBOOTPath with TargetREVBOOTPath in ResolvedTargetVolume
|
|
|
|
Case SourceVolumeType _EQC 'Local'
|
|
// Local tables have no distintion between source and target. Just
|
|
// use the ResolvedSourceVolume.
|
|
ResolvedTargetVolume = ResolvedSourceVolume
|
|
|
|
Case Otherwise$
|
|
StatusFlag = Get_Status(StatusCode)
|
|
AdditionalMessage = 'An unknown SourceVolumeType, ' : SourceVolumeType : ', was encountered.'
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, '', Application, '', '', '', StatusCode, @File_Error, '', AdditionalMessageMessage)
|
|
Replication_Services('SendEmergencyAlert', Service, '', Application, '', '', '', StatusCode, @File_Error, '', AdditionalMessage)
|
|
|
|
End Case
|
|
|
|
// If the table is SYSPROCS, alias the target table rather than attach. Otherwise, the target's
|
|
// copy of the source code will appear in the debugger.
|
|
If Table _EQC 'SYSPROCS' then
|
|
Alias_Table(ResolvedTargetVolume : '', Application, 'SYSPROCS', 'TARGET_SYSPROCS')
|
|
end else
|
|
Attach_Table(ResolvedTargetVolume : '', Table, Application, '')
|
|
end
|
|
|
|
If Get_Status(StatusCode) then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'ATTACH', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'ATTACH', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end else
|
|
OpenSuccessful = True$ ; // Assume the Open statement will be successful for now.
|
|
If Table _EQC 'SYSPROCS' then
|
|
Open 'TARGET_SYSPROCS' to hTargetTable else OpenSuccessful = False$
|
|
end else
|
|
Open Table to hTargetTable else OpenSuccessful = False$
|
|
end
|
|
If OpenSuccessful EQ True$ then
|
|
If Table _EQC 'SYSPROCS' then
|
|
// Since source code is being replicated, alias the target SYSOBJ table to
|
|
// replicate object code as well.
|
|
Set_Status(0)
|
|
Alias_Table(ResolvedTargetVolume : '', Application, 'SYSOBJ', 'TARGET_SYSOBJ')
|
|
If Get_Status(StatusCode) then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'ALIAS', Application, ResolvedTargetVolume, 'SYSOBJ', '', StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'ALIAS', Application, ResolvedTargetVolume, 'SYSOBJ', '', StatusCode, @File_Error)
|
|
end else
|
|
Open 'TARGET_SYSOBJ' to hTargetSysObj else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'OPEN', Application, ResolvedTargetVolume, 'SYSOBJ', KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'OPEN', Application, ResolvedTargetVolume, 'SYSOBJ', KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
|
|
// Use Repos_Attach_Trans to alias the target SYSREPOS table to replicate
|
|
// entity pointers.
|
|
Set_Status(0)
|
|
Repos_Attach_Trans(ResolvedTargetVolume : '', 1)
|
|
If Get_Status(StatusCode) then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'ALIAS', Application, ResolvedTargetVolume, 'SYSREPOS', '', StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'ALIAS', Application, ResolvedTargetVolume, 'SYSREPOS', '', StatusCode, @File_Error)
|
|
end else
|
|
Open 'SYSREPOS_TEMP' to hTargetSysRepos else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'OPEN', Application, ResolvedTargetVolume, 'SYSREPOS_TEMP', KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'OPEN', Application, ResolvedTargetVolume, 'SYSREPOS_TEMP', KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'OPEN', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'OPEN', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'DETACH', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'DETACH', Application, ResolvedSourceVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
|
|
// Perform the action as needed.
|
|
Begin Case
|
|
Case Action _EQC 'WRITE'
|
|
SourceRow = SRP_HashTable_Get(hHashTable, KeyID)
|
|
Write SourceRow to hTargetTable, KeyID then
|
|
If Table _EQC 'SYSPROCS' then
|
|
// Check to see if object was found that needs to be replicated.
|
|
If SRP_HashTable_Contains(hHashTable, '$' : KeyID) then
|
|
SourceRowObj64 = SRP_HashTable_Get(hHashTable, '$' : KeyID)
|
|
SourceRowObj = SRP_Decode(SourceRowObj64, 'BASE64')
|
|
Write SourceRowObj to hTargetSysObj, '$' : KeyID else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedTargetVolume, 'SYSOBJ', '$' : KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedTargetVolume, 'SYSOBJ', '$' : KeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
// Check to see if repository entity pointers were found that needs to be replicated.
|
|
SourceApplication = Field(KeyID, '*', 2, 1)
|
|
If SourceApplication EQ '' then SourceApplication = 'SYSPROG'
|
|
SysReposSourceKeyID = SourceApplication : '*STPROC**' : KeyID[1, '*']
|
|
If SRP_HashTable_Contains(hHashTable, SysReposSourceKeyID) then
|
|
SourceReposStprocRow = SRP_HashTable_Get(hHashTable, SysReposSourceKeyID)
|
|
Write SourceReposStprocRow to hTargetSysRepos, SysReposSourceKeyID else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedTargetVolume, 'SYSREPOS', SysReposSourceKeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedTargetVolume, 'SYSREPOS', SysReposSourceKeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
SysReposObjKeyID = SourceApplication : '*STPROCEXE**' : KeyID[1, '*']
|
|
If SRP_HashTable_Contains(hHashTable, SysReposObjKeyID) then
|
|
SourceReposStprocExeRow = SRP_HashTable_Get(hHashTable, SysReposObjKeyID)
|
|
Write SourceReposStprocExeRow to hTargetSysRepos, SysReposObjKeyID else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedTargetVolume, 'SYSREPOS', SysReposObjKeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedTargetVolume, 'SYSREPOS', SysReposObjKeyID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
end
|
|
ActionCompleted = True$
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end
|
|
|
|
Case Action _EQC 'DELETE'
|
|
Delete hTargetTable, KeyID then
|
|
ActionCompleted = True$
|
|
end else
|
|
Read TargetRow from hTargetTable, KeyID then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'DELETE', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'DELETE', Application, ResolvedTargetVolume, Table, KeyID, StatusCode, @File_Error)
|
|
end else
|
|
// This means the row did not exist in the table. However, this is
|
|
// quite possible if there has been more activity with this row
|
|
// that deleted it prior to this transaction being processed.
|
|
ActionCompleted = True$
|
|
end
|
|
end
|
|
|
|
Case Action _EQC 'CLEARFILE'
|
|
// Performing a ClearFile statement can have disastorous consequences if performed
|
|
// against a non-intended table, such as a system table. Make sure only valid tables
|
|
// are being cleared.
|
|
PerformClearFile = True$ ; // Assume the table will be cleared for now.
|
|
Begin Case
|
|
Case SourceVolume _EQC 'REVBOOT'
|
|
PerformClearFile = False$
|
|
AdditionalMessage = 'An attempt was made to try and clear a table in the ' : SourceVolume : ' volume.'
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', '', '', '', AdditionalMessage)
|
|
Replication_Services('SendEmergencyAlert', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', '', '', '', AdditionalMessage)
|
|
|
|
Case Table _EQC 'SYSPROCS'
|
|
PerformClearFile = False$
|
|
AdditionalMessage = 'An attempt was made to try and clear the ' : Table : ' table.'
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', '', '', '', AdditionalMessage)
|
|
Replication_Services('SendEmergencyAlert', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', '', '', '', AdditionalMessage)
|
|
|
|
Case Table[1, 8] _EQC 'SYSREPOS'
|
|
PerformClearFile = False$
|
|
AdditionalMessage = 'An attempt was made to try and clear the ' : Table : ' table.'
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', '', '', '', AdditionalMessage)
|
|
Replication_Services('SendEmergencyAlert', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', '', '', '', AdditionalMessage)
|
|
End Case
|
|
|
|
If PerformClearFile EQ True$ then
|
|
ClearFile hTargetTable then
|
|
ActionCompleted = True$
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'CLEARFILE', Application, ResolvedTargetVolume, Table, '', StatusCode, @File_Error)
|
|
end
|
|
end
|
|
|
|
Case Action _EQC 'IGNORE'
|
|
// If a normal action (e.g., WRITE) cannot be processed due to normal reasons, the
|
|
// action is dynamically changed to IGNORE so that the service can continue without
|
|
// erroring out. The transaction ID will also be deleted.
|
|
ActionCompleted = True$
|
|
|
|
End Case
|
|
end
|
|
|
|
// If the action completed successfully (and the action is not SKIP) then delete the transaction
|
|
// from the queue table so it will not be processed again.
|
|
If ActionCompleted EQ True$ then
|
|
* Replication_Services('PostToDailyLog')
|
|
Delete hQueueTable, InProcessSK else
|
|
// The transaction ID might not exist due to SKIPing. Just clear the error and move on.
|
|
Set_Status(0)
|
|
end
|
|
LookupKeyID = Action:'*':Table:'*':KeyID
|
|
Delete hQueueTable, LookupKeyID else
|
|
// The lookup key might not exist due to SKIPing. Just clear the error and move on.
|
|
Set_Status(0)
|
|
// Log failure
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = LookupKeyID
|
|
LogData<3> = hQueueTable
|
|
Logging_Services('AppendLog', objLog, LogData, @RM, @FM)
|
|
end
|
|
end
|
|
end else
|
|
// SKIP actions are always completed.
|
|
ActionCompleted = True$
|
|
end
|
|
// Only continue to process transactions if there are no unexpected problems that would cause
|
|
// an action to not complete.
|
|
While ActionCompleted EQ True$
|
|
Next InProcessSK
|
|
|
|
If ResolvedTargetVolume NE '' then
|
|
// Check to see if the target SYSREPOS table was opened. If so, run Repos_Detach_Trans to
|
|
// properly detach the REPOS_BFS volume.
|
|
Open 'SYSREPOS_TEMP' to hTargetSysRepos then
|
|
Repos_Detach_Trans(ResolvedTargetVolume : '', 1)
|
|
end
|
|
|
|
Set_Status(0)
|
|
Detach_Volume(ResolvedTargetVolume : '', Success)
|
|
end else
|
|
// There was no resolved target volume (probably due to a custom replication procedure
|
|
// being called instead. So just set the Success flag to true.
|
|
Success = True$
|
|
end
|
|
|
|
If Success EQ True$ then
|
|
// InProcessSK is now 1 greater. Save this as the NextInProcessSK value.
|
|
If FirstSkippedSK NE '' then Transfer FirstSkippedSK to InProcessSK
|
|
Write InProcessSK to hQueueTable, InProcessSKID then
|
|
If Replication_Services('LockReplicationQueueCounter', hQueueTable, PendingSKID) then
|
|
// Read the Next Pending counter again since it might have changed. But now it is locked so this
|
|
// value won't change until it is unlocked.
|
|
Read NextPendingSK from hQueueTable, PendingSKID else NextPendingSK = 1
|
|
If InProcessSK EQ NextPendingSK then
|
|
Write 1 to hQueueTable, PendingSKID else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, PendingSKID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, PendingSKID, StatusCode, @File_Error)
|
|
end
|
|
|
|
Write 1 to hQueueTable, InProcessSKID else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, InProcessSKID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, InProcessSKID, StatusCode, @File_Error)
|
|
end
|
|
end
|
|
Replication_Services('UnlockReplicationQueueCounter', hQueueTable, PendingSKID)
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, InProcessSKID, StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'WRITE', Application, ResolvedSourceVolume, 'ReplicationQueue for ' : Table, InProcessSKID, StatusCode, @File_Error)
|
|
end
|
|
end else
|
|
StatusFlag = Get_Status(StatusCode)
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, 'DETACH', Application, ResolvedTargetVolume, Table, '', StatusCode, @File_Error)
|
|
Replication_Services('SendEmergencyAlert', Service, 'DETACH', Application, ResolvedTargetVolume, Table, '', StatusCode, @File_Error)
|
|
end
|
|
end
|
|
SRP_HashTable_Release(hHashTable)
|
|
end
|
|
Replication_Services('UnlockReplicationQueueCounter', hQueueTable, InProcessSKID)
|
|
Write '' to hQueueTable, '%EngineName%' else Null
|
|
end
|
|
|
|
Case ServerType _EQC 'Target'
|
|
// Reserved for future use.
|
|
|
|
Case Otherwise$
|
|
// This is an invalid Server Type. Send an alert and stop the service.
|
|
AdditionalMessage = Quote(ServerType) : ' is an invalid server type.'
|
|
If LoggingEnabled EQ True$ then Replication_Services('LogError', Service, '', '', '', '', '', '', '', '', AdditionalMessage)
|
|
Replication_Services('SendEmergencyAlert', Service, '', '', '', '', '', '', '', '', AdditionalMessage)
|
|
|
|
End Case
|
|
|
|
end else
|
|
// Either the replication system has been disabled or stopped, or this is a replication server. Abort this
|
|
// service and tell the SRP Engine Server to quit. If the system has been stopped, reset the SystemStopFlag
|
|
// to False since this should only be a temporary.
|
|
If SystemStopFlag EQ True$ then
|
|
Replication_Services('SetSystemStopFlag', False$)
|
|
end
|
|
|
|
Send_Info('SRPENGINESERVERQUIT')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// WriteDatabaseRow
|
|
//
|
|
// TransactionID - The transaction ID that contains the relevant information for the replication action.
|
|
//
|
|
// Writes the database row into the target server's database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service WriteDatabaseRow(TransactionID)
|
|
|
|
ActionCompleted = False$ ; // Assume the action did not complete for now.
|
|
|
|
If TransactionID NE '' then
|
|
Action = TransactionID[1, @FM]
|
|
Application = TransactionID[Col2() + 1, @FM]
|
|
SourceVolume = TransactionID[Col2() + 1, @FM]
|
|
Table = TransactionID[Col2() + 1, @FM]
|
|
KeyID = TransactionID[Col2() + 1, 999]
|
|
|
|
// Attempt to open the table that the source row resides in. However, make sure that any paths are resolved to
|
|
// the official source server path (which should be in UNC format). The SourceVolume variable will likely
|
|
// contain a mapped drive and this might not be available to the replication service.
|
|
SourceServerPath = UCase(Replication_Services('GetSourceServerPath'))
|
|
If Index(SourceVolume, ':\', 1) then
|
|
// Source volume has a mapped drive. Replace with the source server path.
|
|
MappedDrive = SourceVolume[1, 3]
|
|
Swap MappedDrive with SourceServerPath in SourceVolume
|
|
end
|
|
Set_Status(0)
|
|
Attach_Table(SourceVolume, Table, Application, '')
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
end else
|
|
Open Table to hSourceTable then
|
|
// The table did not open. It could be because it is not attached.
|
|
Read SourceRow from hSourceTable, KeyID then
|
|
Set_Status(0)
|
|
Detach_Volume(SourceVolume, Success)
|
|
If Success EQ True$ then
|
|
Set_Status(0)
|
|
TargetServerPath = UCase(Replication_Services('GetTargetServerPath'))
|
|
TargetVolume = UCase(SourceVolume)
|
|
Swap SourceServerPath with TargetServerPath in TargetVolume
|
|
Attach_Table(TargetVolume, Table, Application, '')
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
end else
|
|
Open Table to hTargetTable then
|
|
Write SourceRow to hTargetTable, KeyID then
|
|
ActionCompleted = True$
|
|
end
|
|
end
|
|
end
|
|
Set_Status(0)
|
|
Detach_Volume(TargetVolume, Success)
|
|
Set_Status(0)
|
|
Attach_Table(SourceVolume, Table, Application, '')
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Unable to open the ' : Table : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TransactionID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ActionCompleted
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// DeleteDatabaseRow
|
|
//
|
|
// TransactionID - The transaction ID that contains the relevant information for the replication action.
|
|
//
|
|
// Deletes the database row from the target server's database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service DeleteDatabaseRow(TransactionID)
|
|
|
|
ActionCompleted = False$ ; // Assume the action did not complete for now.
|
|
|
|
If TransactionID NE '' then
|
|
Action = TransactionID[1, @FM]
|
|
Application = TransactionID[Col2() + 1, @FM]
|
|
SourceVolume = TransactionID[Col2() + 1, @FM]
|
|
Table = TransactionID[Col2() + 1, @FM]
|
|
KeyID = TransactionID[Col2() + 1, 999]
|
|
|
|
// Attempt to open the table that the source row resides in. However, make sure that any paths are resolved to
|
|
// the official source server path (which should be in UNC format). The SourceVolume variable will likely
|
|
// contain a mapped drive and this might not be available to the replication service.
|
|
SourceServerPath = UCase(Replication_Services('GetSourceServerPath'))
|
|
If Index(SourceVolume, ':\', 1) then
|
|
// Source volume has a mapped drive. Replace with the source server path.
|
|
MappedDrive = SourceVolume[1, 3]
|
|
Swap MappedDrive with SourceServerPath in SourceVolume
|
|
end
|
|
Set_Status(0)
|
|
Attach_Table(SourceVolume, Table, Application, '')
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
end else
|
|
Open Table to hSourceTable then
|
|
// The table did not open. It could be because it is not attached.
|
|
Read SourceRow from hSourceTable, KeyID then
|
|
Set_Status(0)
|
|
Detach_Volume(SourceVolume, Success)
|
|
If Success EQ True$ then
|
|
Set_Status(0)
|
|
TargetServerPath = UCase(Replication_Services('GetTargetServerPath'))
|
|
TargetVolume = UCase(SourceVolume)
|
|
Swap SourceServerPath with TargetServerPath in TargetVolume
|
|
Attach_Table(TargetVolume, Table, Application, '')
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
end else
|
|
Open Table to hTargetTable then
|
|
Write SourceRow to hTargetTable, KeyID then
|
|
ActionCompleted = True$
|
|
end
|
|
end
|
|
end
|
|
Set_Status(0)
|
|
Detach_Volume(TargetVolume, Success)
|
|
Set_Status(0)
|
|
Attach_Table(SourceVolume, Table, Application, '')
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Unable to open the ' : Table : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TransactionID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ActionCompleted
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetReplicationTables
|
|
//
|
|
// ReplicationTables - An @FM/@VM formatted array of database tables and related attributes. - [Optional]
|
|
//
|
|
// If the ReplicaationTables argument is empty, it will clear the current settings. The format of the ReplicationTables
|
|
// array is as follows:
|
|
//
|
|
// <x, 1> = Table name
|
|
// <x, 2> = Database ID
|
|
// <x, 3> = Volume
|
|
// <x, 4> = Boolean flag to indicate if the table has BASE_MFS installed
|
|
// <x, 5> = Boolean flag to indicate if the table is not-suspended from being queued
|
|
// <x, 6> = Boolean flag to indicate Is the table is not-suspended from being replicated
|
|
// <x, 7> = Type of queue for this table
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetReplicationTables(ReplicationTables)
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TABLES'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
ReplicationTables = SRP_Array('SortRows', ReplicationTables, 'AL2' : @FM : 'AL1', 'LIST', @FM, @VM)
|
|
Write ReplicationTables to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing the replication tables list in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking the replication tables list in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetReplicationTables
|
|
//
|
|
// Returns an @FM/@VM array of database tables that the Replication Manager is aware of.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetReplicationTables()
|
|
|
|
ReplicationTables = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TABLES'
|
|
Read ReplicationTables from hRepConfigTable, RepConfigKeyID else ReplicationTables = ''
|
|
end
|
|
|
|
Response = ReplicationTables
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetVolumeTables
|
|
//
|
|
// Volume - The volume to search for tables. - [Required]
|
|
//
|
|
// Returns an @FM/@VM array of table information for the indicated volume. Indexes and Dictionaries are excluded.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetVolumeTables(Volume)
|
|
|
|
VolumeTables = ''
|
|
|
|
If Volume NE '' then
|
|
rv = Set_Status(0)
|
|
VolumeTables = List_Volume_Sub(Volume, '', 'TABLE_NAME' : @FM : 'TABLE_DATABASE_ID', '')
|
|
If Get_Status(StatusCode) then
|
|
Error_Services('Add', 'Error calling the List_Volume_Sub function in the ' : Service : ' service. Status Code : ' : StatusCode)
|
|
end else
|
|
// Tables were found. Remove dictionaries and indexes.
|
|
NumTables = DCount(VolumeTables, @FM)
|
|
For TableCnt = NumTables to 1 Step -1
|
|
If (VolumeTables<TableCnt>[1, 1] EQ '!') OR (VolumeTables<TableCnt>[1, 5] EQ 'DICT.') then
|
|
VolumeTables = Delete(VolumeTables, TableCnt, 0, 0)
|
|
end
|
|
Next TableCnt
|
|
// Sort the list by database ID and then by table name.
|
|
VolumeTables = SRP_Array('SortRows', VolumeTables, 'AL2' : @FM : 'AL1', 'LIST', @FM, @VM)
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Volume argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = VolumeTables
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AddBaseMFSToTable
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
// Volume - Volume where the table resides. - [Required]
|
|
//
|
|
// Adds (or installs) the BASE_MFS modified filing system routine to the indicated table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AddBaseMFSToTable(TableName, DatabaseID, Volume)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') AND (Volume NE '') then
|
|
Set_Status(0)
|
|
Alias_Table(Volume : '', 'SYSPROG', 'REVMEDIA', 'REVMEDIA_TEMP')
|
|
If Get_Status(StatusCode) then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Error_Services('Add', 'Error aliasing REVMEDIA from ' : Volume : ' in the ' : Service : ' service: ' : StatusCode)
|
|
end else
|
|
TableRow = Database_Services('ReadDataRow', 'REVMEDIA_TEMP', TableName : '*' : DatabaseID)
|
|
If Error_Services('NoError') then
|
|
MFSList = TableRow<2>
|
|
Locate 'BASE_MFS' in MFSList using @VM setting Pos else
|
|
MFSList<0, -1> = 'BASE_MFS'
|
|
TableRow<2> = MFSList
|
|
Database_Services('WriteDataRow', 'REVMEDIA_TEMP', TableName : '*' : DatabaseID, TableRow)
|
|
end
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabHasBaseMFS$> = True$
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end
|
|
Error = Error_Services('GetMessage')
|
|
Set_Status(0)
|
|
Detach_Table('REVMEDIA_TEMP')
|
|
Database_Services('ClearTableHandle', 'REVMEDIA_TEMP')
|
|
Set_Status(0)
|
|
// Using the absolute path to the REVBOOT folder will confuse Attach_Table, so check for a match and then
|
|
// replace with REVBOOT if needed.
|
|
If Volume _EQC Drive() then Volume = 'REVBOOT'
|
|
Attach_Table(Volume : '', TableName : '', DatabaseID : '', '')
|
|
Error_Services('Add', Error)
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName, DatabaseID, or Volume argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// RemoveBaseMFSFromTable
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
// Volume - Volume where the table resides. - [Required]
|
|
//
|
|
// Removes the BASE_MFS modified filing system routine from the indicated table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service RemoveBaseMFSFromTable(TableName, DatabaseID, Volume)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') AND (Volume NE '') then
|
|
Set_Status(0)
|
|
Alias_Table(Volume : '', 'SYSPROG', 'REVMEDIA', 'REVMEDIA_TEMP')
|
|
If Get_Status(StatusCode) then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Error_Services('Add', 'Error aliasing REVMEDIA from ' : Volume : ' in the ' : Service : ' service: ' : StatusCode)
|
|
end else
|
|
TableRow = Database_Services('ReadDataRow', 'REVMEDIA_TEMP', TableName : '*' : DatabaseID)
|
|
If Error_Services('NoError') then
|
|
MFSList = TableRow<2>
|
|
Locate 'BASE_MFS' in MFSList using @VM setting Pos then
|
|
MFSList = Delete(MFSList, 0, Pos, 0)
|
|
TableRow<2> = MFSList
|
|
Database_Services('WriteDataRow', 'REVMEDIA_TEMP', TableName : '*' : DatabaseID, TableRow)
|
|
end
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabHasBaseMFS$> = False$
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end
|
|
Error = Error_Services('GetMessage')
|
|
Set_Status(0)
|
|
Detach_Table('REVMEDIA_TEMP')
|
|
Database_Services('ClearTableHandle', 'REVMEDIA_TEMP')
|
|
Set_Status(0)
|
|
// Using the absolute path to the REVBOOT folder will confuse Attach_Table, so check for a match and then
|
|
// replace with REVBOOT if needed.
|
|
If Volume _EQC Drive() then Volume = 'REVBOOT'
|
|
Attach_Table(Volume : '', TableName : '', DatabaseID : '', '')
|
|
Error_Services('Add', Error)
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName, DatabaseID, or Volume argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// HasBaseMFSInstalled
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Returns a Boolean flag if the indicated table has BASE_MFS already installed.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service HasBaseMFSInstalled(TableName, DatabaseID, Volume)
|
|
|
|
BaseMFSInstalled = False$ ; // Assume false for now.
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') AND (Volume NE '') then
|
|
Set_Status(0)
|
|
Alias_Table(Volume : '', 'SYSPROG', 'REVMEDIA', 'REVMEDIA_TEMP')
|
|
If Get_Status(StatusCode) then
|
|
StatusFlag = Get_Status(StatusCode)
|
|
Error_Services('Add', 'Error aliasing REVMEDIA from ' : Volume : ' in the ' : Service : ' service: ' : StatusCode)
|
|
end else
|
|
TableRow = Database_Services('ReadDataRow', 'REVMEDIA_TEMP', TableName : '*' : DatabaseID)
|
|
If Error_Services('NoError') then
|
|
MFSList = TableRow<2>
|
|
Locate 'BASE_MFS' in MFSList using @VM setting Pos then
|
|
BaseMFSInstalled = True$
|
|
end
|
|
end
|
|
Set_Status(0)
|
|
Detach_Table('REVMEDIA_TEMP')
|
|
Database_Services('ClearTableHandle', 'REVMEDIA_TEMP')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName, DatabaseID, or Volume argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = BaseMFSInstalled
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// IsReplicationTable
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Returns a Boolean flag if the indicated table should be considered for replication processing.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service IsReplicationTable(TableName, DatabaseID)
|
|
|
|
ReplicationTable = False$ ; // Assume false for now.
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTable = True$
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ReplicationTable
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SuspendTableQueue
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Turns off the flag for the indicated table so that queue processes will be suspended (disabled). The table will still
|
|
// be considered for replication and any queued transactions will still be processed, but no new transactions will be
|
|
// queued.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SuspendTableQueue(TableName, DatabaseID)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabCanQueue$> = False$
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AllowTableQueue
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Turns on the flag for the indicated table so that queue processes will be allowed (enabled).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AllowTableQueue(TableName, DatabaseID)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabCanQueue$> = True$
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// IsTableAllowedToQueue
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Returns a Boolean flag if the indicated table should be allowed to queue.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service IsTableAllowedToQueue(TableName, DatabaseID)
|
|
|
|
CanQueue = False$ ; // Assume false for now.
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
CanQueue = ReplicationTables<Pos, RepTabCanQueue$>
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = CanQueue
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SuspendTableReplication
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Turns off the flag for the indicated table so that replication processes will be suspended (disabled). The table will
|
|
// still be considered for replication and transactions will still be queued, but no queued transactions will be
|
|
// processed.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SuspendTableReplication(TableName, DatabaseID)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabCanRep$> = False$
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// AllowTableReplication
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Turns on the flag for the indicated table so that replication processes will be allowed (enabled).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service AllowTableReplication(TableName, DatabaseID)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabCanRep$> = True$
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// IsTableAllowedToReplicate
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Returns a Boolean flag if the indicated table should be allowed to replicate.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service IsTableAllowedToReplicate(TableName, DatabaseID)
|
|
|
|
CanReplicate = False$ ; // Assume false for now.
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
CanReplicate = ReplicationTables<Pos, RepTabCanRep$>
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = CanReplicate
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetTableQueueType
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
// QueueType - Queue type to set. - [Required]
|
|
//
|
|
// Sets a queue type to be associated to the indicated table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetTableQueueType(TableName, DatabaseID, QueueType)
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') AND (QueueType NE '') then
|
|
Locate QueueType in 'Public,Private' using ',' setting Pos then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
ReplicationTables<Pos, RepTabQueueType$> = QueueType
|
|
Replication_Services('SetReplicationTables', ReplicationTables)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'In invalid queue type (' : QueueType : ') was pass into the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName, DatabaseID, or QueueType argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTableQueueType
|
|
//
|
|
// TableName - Name of the table. - [Required]
|
|
// DatabaseID - Database the table belongs to. - [Required]
|
|
//
|
|
// Returns the queue type associated with the indicated table. If no queue type has been assigned, it will default to
|
|
// the Public queue type.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTableQueueType(TableName, DatabaseID)
|
|
|
|
QueueType = 'Public' ; // Assume Public for now
|
|
|
|
If (TableName NE '') AND (DatabaseID NE '') then
|
|
ReplicationTables = Replication_Services('GetReplicationTables')
|
|
If Error_Services('NoError') then
|
|
TableArray = SRP_Array('Rotate', ReplicationTables, @FM, @VM)
|
|
(TableNames, DatabaseIDs) using @FM = TableArray
|
|
TableItemKeys = SRP_Array('Rotate', TableNames : @FM : DatabaseIDs, @FM, @VM)
|
|
ThisTableItemKey = TableName : @VM : DatabaseID
|
|
Locate ThisTableItemKey in TableItemKeys using @FM setting Pos then
|
|
QueueType = ReplicationTables<Pos, RepTabQueueType$>
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'TableName or DatabaseID argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = QueueType
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSystemStopFlag
|
|
//
|
|
// System Enabled Flag - Boolean flag to determine if the system should be stopped.
|
|
//
|
|
// Sets a Boolean flag indicating that the system should be stopped.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSystemStopFlag(SystemStopFlag)
|
|
|
|
If Len(SystemStopFlag) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SYSTEM_STOPPED'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write SystemStopFlag to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing system stopped flag in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking system stopped flag in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'SystemStopFlag arguement was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSystemStopFlag
|
|
//
|
|
// Returns a Boolean flag indicating if the system should be stopped. The default is False. If the system is stopped,
|
|
// then transactions should continue to be added to the pending queue files but not processed.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSystemStopFlag()
|
|
|
|
SystemStopFlag = True$ ; // Assume True for now. Only an explicit True will stop the system.
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SYSTEM_STOPPED'
|
|
Read SystemStopFlag from hRepConfigTable, RepConfigKeyID then
|
|
If SystemStopFlag NE True$ then SystemEnabledFlag = False$
|
|
end
|
|
end
|
|
|
|
Response = SystemStopFlag
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSystemEnabledFlag
|
|
//
|
|
// System Enabled Flag. Boolean flag to determine if the system is enabled.
|
|
//
|
|
// Sets a Boolean flag indicating if the system is currently enabled.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSystemEnabledFlag(SystemEnabledFlag)
|
|
|
|
If Len(SystemEnabledFlag) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SYSTEM_ENABLED'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write SystemEnabledFlag to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing system enabled flag in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking system enabled flag in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'SystemEnabledFlag arguement was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSystemEnabledFlag
|
|
//
|
|
// Returns a Boolean flag indicating if the system is currently enabled. The default is True. If the system is not
|
|
// enabled, then transactions should continue to be added to the pending queue files but not processed.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSystemEnabledFlag()
|
|
|
|
SystemEnabledFlag = True$ ; // Assume True for now. Only an explicit False will disable the system.
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SYSTEM_ENABLED'
|
|
Read SystemEnabledFlag from hRepConfigTable, RepConfigKeyID then
|
|
If SystemEnabledFlag NE False$ then SystemEnabledFlag = True$
|
|
end
|
|
end
|
|
|
|
Response = SystemEnabledFlag
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetServerType
|
|
//
|
|
// Server Type - The server type. Valid options are 'Source' and 'Target'.
|
|
//
|
|
// Sets the server type.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetServerType(ServerType)
|
|
|
|
If Len(ServerType) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SERVER_TYPE'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write ServerType to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing server type in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking server type in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'ServerType arguement was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetServerType
|
|
//
|
|
// Returns the type of server. Valid optons are 'Source' and 'Target'. Default is 'Source'.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetServerType()
|
|
|
|
ServerType = 'Source' ; // Assume Source unless otherwise noted.
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SERVER_TYPE'
|
|
Read ServerType from hRepConfigTable, RepConfigKeyID then
|
|
If ServerType NE 'Source' AND ServerType NE 'Target' then ServerType = 'Source'
|
|
end
|
|
end
|
|
|
|
Response = ServerType
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSourceQueuePath
|
|
//
|
|
// QueuePath - Path to where the transaction queue files are maintained.
|
|
//
|
|
// Sets the OS path to where the transaction queue files are maintained.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSourceQueuePath(QueuePath)
|
|
|
|
If Len(QueuePath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SOURCE_QUEUE_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write QueuePath to hRepConfigTable, RepConfigKeyID then
|
|
ServerType = Replication_Services('GetServerType')
|
|
If ServerType _EQC 'Source' then
|
|
Replication_Services('CreateReplicationQueue', 'GLOBAL', 'PUBLIC')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error writing queue path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking queue path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSourceQueuePath
|
|
//
|
|
// Returns the OS path to where the transaction queues are maintained. This will always append a backslash to make it
|
|
// easier for calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSourceQueuePath()
|
|
|
|
QueuePath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SOURCE_QUEUE_PATH'
|
|
Read QueuePath from hRepConfigTable, RepConfigKeyID else QueuePath = ''
|
|
If SRP_Path('IsRelative', QueuePath) then
|
|
QueuePath = SRP_Path('Expand', QueuePath)
|
|
end
|
|
QueuePath = SRP_Path('AddBackslash', QueuePath)
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = QueuePath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSourceServerPath
|
|
//
|
|
// SourceServerPath - Path to the root where the source tables live.
|
|
//
|
|
// Sets the OS path to where the source tables live.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSourceServerPath(SourceServerPath)
|
|
|
|
If Len(SourceServerPath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SOURCE_SERVER_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write SourceServerPath to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing source server path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking source server path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSourceServerPath
|
|
//
|
|
// Returns the OS path to where the source tables live. This will always append a backslash to make it easier for
|
|
// calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSourceServerPath()
|
|
|
|
SourceServerPath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SOURCE_SERVER_PATH'
|
|
Read SourceServerPath from hRepConfigTable, RepConfigKeyID then
|
|
SourceServerPath = SRP_Path('AddBackslash', SourceServerPath)
|
|
end else
|
|
SourceServerPath = ''
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = SourceServerPath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSourceREVBOOTPath
|
|
//
|
|
// SetSourceREVBOOTPath - Path to the source REVBOOT volume.
|
|
//
|
|
// Sets the OS path to where the source REVBOOT volume exists.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSourceREVBOOTPath(SourceREVBOOTPath)
|
|
|
|
If Len(SourceREVBOOTPath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SOURCE_REVBOOT_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write SourceREVBOOTPath to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing source REVBOOT path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking source REVBOOT path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSourceREVBOOTPath
|
|
//
|
|
// Returns the OS path to where the source REVBOOT volume exists. This will always append a backslash to make it easier
|
|
// for calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSourceREVBOOTPath()
|
|
|
|
SourceREVBOOTPath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SOURCE_REVBOOT_PATH'
|
|
Read SourceREVBOOTPath from hRepConfigTable, RepConfigKeyID then
|
|
SourceREVBOOTPath = SRP_Path('AddBackslash', SourceREVBOOTPath)
|
|
end else
|
|
SourceREVBOOTPath = ''
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = SourceREVBOOTPath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetTargetQueuePath
|
|
//
|
|
// QueuePath - Path to where the transaction queue files are maintained.
|
|
//
|
|
// Sets the OS path to where the transaction queue files are maintained.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetTargetQueuePath(QueuePath)
|
|
|
|
If Len(QueuePath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TARGET_QUEUE_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write QueuePath to hRepConfigTable, RepConfigKeyID then
|
|
ServerType = Replication_Services('GetServerType')
|
|
If ServerType _EQC 'Target' then
|
|
Replication_Services('CreateReplicationQueue', 'GLOBAL', 'PUBLIC')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error writing queue path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking queue path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTargetQueuePath
|
|
//
|
|
// Returns the OS path to where the transaction queues are maintained. This will always append a backslash to make it
|
|
// easier for calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTargetQueuePath()
|
|
|
|
QueuePath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TARGET_QUEUE_PATH'
|
|
Read QueuePath from hRepConfigTable, RepConfigKeyID else QueuePath = ''
|
|
If SRP_Path('IsRelative', QueuePath) then
|
|
QueuePath = SRP_Path('Expand', QueuePath)
|
|
end
|
|
QueuePath = SRP_Path('AddBackslash', QueuePath)
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = QueuePath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetTargetServerPath
|
|
//
|
|
// TargetServerPath - Path to the root where the target tables live.
|
|
//
|
|
// Sets the OS path to where the target tables live.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetTargetServerPath(TargetServerPath)
|
|
|
|
If Len(TargetServerPath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TARGET_SERVER_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write TargetServerPath to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing target server path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking target server path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTargetServerPath
|
|
//
|
|
// Returns the OS path to where the target tables live. This will always append a backslash to make it easier for
|
|
// calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTargetServerPath()
|
|
|
|
TargetServerPath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TARGET_SERVER_PATH'
|
|
Read TargetServerPath from hRepConfigTable, RepConfigKeyID else TargetServerPath = ''
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = TargetServerPath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetTargetREVBOOTPath
|
|
//
|
|
// SetTargetREVBOOTPath - Path to the Target REVBOOT volume.
|
|
//
|
|
// Sets the OS path to where the target REVBOOT volume exists.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetTargetREVBOOTPath(TargetREVBOOTPath)
|
|
|
|
If Len(TargetREVBOOTPath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TARGET_REVBOOT_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write TargetREVBOOTPath to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing target REVBOOT path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking target REVBOOT path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetTargetREVBOOTPath
|
|
//
|
|
// Returns the OS path to where the target REVBOOT volume exists. This will always append a backslash to make it easier
|
|
// for calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetTargetREVBOOTPath()
|
|
|
|
TargetREVBOOTPath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_TARGET_REVBOOT_PATH'
|
|
Read TargetREVBOOTPath from hRepConfigTable, RepConfigKeyID then
|
|
TargetREVBOOTPath = SRP_Path('AddBackslash', TargetREVBOOTPath)
|
|
end else
|
|
TargetREVBOOTPath = ''
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = TargetREVBOOTPath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetErrorLogPath
|
|
//
|
|
// ErrorLogPath - Path to the where the error logs should be stored.
|
|
//
|
|
// Sets the OS path to where the error logs are stored.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetErrorLogPath(ErrorLogPath)
|
|
|
|
If Len(ErrorLogPath) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_ERROR_LOG_PATH'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write ErrorLogPath to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing error log path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking error log path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetErrorLogPath
|
|
//
|
|
// Returns the OS path to where error logs are stored. This will always append a backslash to make it easier
|
|
// for calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetErrorLogPath()
|
|
|
|
ErrorLogPath = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_ERROR_LOG_PATH'
|
|
Read ErrorLogPath from hRepConfigTable, RepConfigKeyID then
|
|
ErrorLogPath = SRP_Path('AddBackslash', ErrorLogPath)
|
|
end else
|
|
ErrorLogPath = ''
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ErrorLogPath
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// IsLoggingEnabled
|
|
//
|
|
// Returns the OS path to where error logs are stored. This will always append a backslash to make it easier
|
|
// for calling services to work with.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service IsLoggingEnabled()
|
|
|
|
LoggingEnabled = Replication_Services('GetErrorLogPath') NE ''
|
|
|
|
Response = LoggingEnabled
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetReplicationQueueTableHandle
|
|
//
|
|
// Application - Name of the database application for the table. - [Optional if QueueTable is populated]
|
|
// Table - Name of the database table for the queue file. - [Optional if QueueTable is populated]
|
|
// QueueTable - Name of the actual queue table. This is ignored if Application and Table are populated. - [Optional]
|
|
//
|
|
// Returns the handle to the database table where the pending transactions are maintained for the indicated database
|
|
// table. This service also checks the Sizelock of the table to make sure it is not less than 2. If it is, it will
|
|
// automatically set the Sizelock to 4.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetReplicationQueueTableHandle(Application, Table, QueueTable)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
If QueueTableList@ EQ '' then QueueTableList@ = SRP_List('Create')
|
|
If QueueHandleList@ EQ '' then QueueHandleList@ = SRP_List('Create')
|
|
If QueueTableExists@ EQ '' then QueueTableExists@ = SRP_List('Create')
|
|
|
|
hQueueTable = ''
|
|
|
|
If (Len(Application) AND Len(Table)) OR (Len(QueueTable)) then
|
|
If QueueTable EQ '' then QueueTable = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
Index = SRP_List('Locate', QueueTableList@, QueueTable)
|
|
If Index GT 0 then
|
|
HaveQueueTable = SRP_List('GetAt', QueueTableExists@, Index)
|
|
If HaveQueueTable then
|
|
// The handle has already been created and stored. Pull from the queue handle list.
|
|
hQueueTable = SRP_List('GetAt', QueueHandleList@, Index)
|
|
end
|
|
end else
|
|
HaveQueueTable = False$
|
|
// Attempt to open the table.
|
|
Set_Status(0)
|
|
Open QueueTable to hQueueTable then
|
|
HaveQueueTable = True$
|
|
end else
|
|
// The table did not open. Attempt to attach and then open again.
|
|
QueuePath = Replication_Services('GetSourceQueuePath')
|
|
Set_Status(0)
|
|
Push.Select(F1, F2, F3, F4)
|
|
Attach_Table(QueuePath : '', QueueTable, 'GLOBAL', '')
|
|
Pop.Select(F1, F2, F3, F4)
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
Error_Services('Add', 'Unable to get a table handle for the ' : QueueTable : ' table in the ' : Service : ' service.')
|
|
end else
|
|
Set_Status(0)
|
|
Open QueueTable to hQueueTable then
|
|
HaveQueueTable = True$
|
|
end else
|
|
Set_Status(0)
|
|
end
|
|
end
|
|
end
|
|
SRP_List('Add', QueueTableList@, QueueTable)
|
|
If HaveQueueTable then
|
|
TableInfo = Get_LH_Info(QueueTable)
|
|
If TableInfo<5> LT 2 then
|
|
Fix_LH(QueueTable, 5, True$, 4)
|
|
TableInfo = Get_LH_Info(QueueTable)
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
Error_Services('Add', 'Error attempting to set the sizelock for the ' : QueueTable : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
SRP_List('Add', QueueHandleList@, hQueueTable)
|
|
SRP_List('Add', QueueTableExists@, True$)
|
|
end else
|
|
SRP_List('Add', QueueHandleList@, '')
|
|
SRP_List('Add', QueueTableExists@, False$)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'One or more required arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = hQueueTable
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetReplicationQueueTables
|
|
//
|
|
// Returns an @FM list of replication queue tables.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetReplicationQueueTables()
|
|
|
|
QueuePath = Replication_Services('GetSourceQueuePath')
|
|
|
|
QueueTables = List_Volume_Sub(QueuePath, '', 'TABLE_NAME', '')
|
|
rv = Set_Status(0)
|
|
NumQueueTables = DCount(QueueTables, @FM)
|
|
For ItemCnt = NumQueueTables to 1 Step -1
|
|
QueueTable = QueueTables<ItemCnt>
|
|
If QueueTable[1, 18] _EQC 'REPLICATION_QUEUE_' else
|
|
QueueTables = Delete(QueueTables, ItemCnt, 0, 0)
|
|
end
|
|
Next ItemCount
|
|
QueueTables = SRP_Array('SortRows', QueueTables, 'AL1', 'LIST', @FM, @VM)
|
|
|
|
Response = QueueTables
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetReplicationQueueTable
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table for the queue file. - [Required]
|
|
//
|
|
// Returns the database table where the pending transactions are maintained for the indicated database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetReplicationQueueTable(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
QueueType = Replication_Services('GetTableQueueType', Table, Application)
|
|
If QueueType EQ 'Private' then
|
|
QueueTable = 'REPLICATION_QUEUE_' : Application : '_' : Table
|
|
end else
|
|
QueueTable = 'REPLICATION_QUEUE_GLOBAL_PUBLIC'
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = QueueTable
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetAllReplicationQueueTableStatus
|
|
//
|
|
// Returns the %NextInProcessSK% value, %NextPendingSK% value, and total estimated number of rows for all replication
|
|
// queue tables. This will be a @FM/@VM list (i.e., QueueTable1 : @VM : Status1 : @FM : QueueTable2 : @VM : Status2).
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetAllReplicationQueueTableStatus()
|
|
|
|
AllReplicationQueueTableStatus = ''
|
|
|
|
AllQueueTables = Replication_Services('GetReplicationQueueTables')
|
|
For Each QueueTable in AllQueueTables using @FM
|
|
AllReplicationQueueTableStatus := QueueTable : @VM
|
|
AllReplicationQueueTableStatus := Replication_Services('GetReplicationQueueTableStatus', QueueTable) : @FM
|
|
Next QueueTable
|
|
|
|
AllReplicationQueueTableStatus[-1, 1] = '' ; // Strip off the final @FM
|
|
|
|
Response = AllReplicationQueueTableStatus
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetReplicationQueueTableStatus
|
|
//
|
|
// QueueTable - Name of the replication queue table. - [Required]
|
|
//
|
|
// Returns the %EngineName% value, %NextInProcessSK% value, %NextPendingSK% value, and total estimated number of rows in
|
|
// the indicated queue table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetReplicationQueueTableStatus(QueueTable)
|
|
|
|
Convert @Lower_Case to @Upper_Case in QueueTable
|
|
|
|
QueueTableStatus = ''
|
|
|
|
If QueueTable NE '' then
|
|
QueuePath = Replication_Services('GetSourceQueuePath')
|
|
If Error_Services('NoError') then
|
|
Set_Status(0)
|
|
ShouldDetach = False$
|
|
// First attempt to open the private replication queue table.
|
|
Open QueueTable to hQueueTable else
|
|
// Since the Open statement didn't work, attempt to attach the private replication queue table.
|
|
Set_Status(0)
|
|
Attach_Table(QueuePath : '', QueueTable, 'GLOBAL', '')
|
|
If Get_Status(StatusCode) then
|
|
Error_Services('Add', 'Unable to attach ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end else
|
|
// Make sure table is detached since it was not attached already.
|
|
ShouldDetach = True$
|
|
Set_Status(0)
|
|
Open QueueTable to hQueueTable else
|
|
Set_Status(0)
|
|
Error_Services('Add', 'Unable to open ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
end
|
|
If Error_Services('NoError') then
|
|
TotalRows = Get.RecCount(hQueueTable, Status, False$)
|
|
// If any of the queue counters have values, adjust the TotalRows count accordingly.
|
|
Read NextInProcessSK from hQueueTable, '%NextInProcessSK%' then
|
|
TotalRows -= 1
|
|
end else
|
|
NextInProcessSK = ''
|
|
end
|
|
Read NextPendingSK from hQueueTable, '%NextPendingSK%' then
|
|
TotalRows -= 1
|
|
end else
|
|
NextPendingSK = ''
|
|
end
|
|
Read EngineName from hQueueTable, '%EngineName%' then
|
|
TotalRows -= 1
|
|
end else
|
|
EngineName = ''
|
|
end
|
|
Read NumProcesses from hQueueTable, '%NumProcesses%' then
|
|
TotalRows -= 1
|
|
end else
|
|
NumProcesses = 0
|
|
end
|
|
QueueTableStatus = EngineName : @VM : NextInProcessSK : @VM : NextPendingSK : @VM : TotalRows : @VM : NumProcesses
|
|
Set_Status(0)
|
|
If ShouldDetach = True$ then Detach_Table(QueueTable)
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'QueueTable argument was missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = QueueTableStatus
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// CreateReplicationQueue
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table. - [Required]
|
|
//
|
|
// Creates a replication queue table for the indicated database table. This is created in the designated queue table
|
|
// volume.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service CreateReplicationQueue(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
ReplicationQueueCreated = False$ ; // Assume this will not be created for now.
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
QueuePath = Replication_Services('GetSourceQueuePath')
|
|
If Error_Services('NoError') then
|
|
QueueTable = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
HaveQueueTable = False$
|
|
// First attempt to open the private replication queue table.
|
|
Set_Status(0)
|
|
Open QueueTable to hQueueTable then
|
|
HaveQueueTable = True$
|
|
end else
|
|
// Since the Open statement didn't work, attempt to attach the private replication queue table.
|
|
Set_Status(0)
|
|
Attach_Table(QueuePath : '', QueueTable, 'GLOBAL', '')
|
|
If Get_Status(StatusCode) then
|
|
If StatusCode[1, @VM] NE 'SSP280' then
|
|
Set_Status(0)
|
|
Convert @VM to @TM in StatusCode
|
|
Error_Services('Add', 'Error creating the ' : QueueTable : ' table in the ' : Service : ' service.' : @TM : @TM : 'StatusCode' : @TM : '=========' : @TM : StatusCode)
|
|
end
|
|
end else
|
|
HaveQueueTable = True$
|
|
end
|
|
end
|
|
|
|
If Error_Services('NoError') AND Not(HaveQueueTable) then
|
|
// The private replication queue table is not available, create the table using default attributes.
|
|
Attributes = ''
|
|
Attributes<1> = 1500000 ; // Estimated number of rows.
|
|
Attributes<2> = 100 ; // Average row size.
|
|
Attributes<3> = 3 ; // Estimated number of dictionary rows.
|
|
Attributes<4> = 4096 ; // Framesize.
|
|
Attributes<5> = 80 ; // Resize threshold.
|
|
Set_Status(0)
|
|
Create_Table(QueuePath, QueueTable, False$, 'GLOBAL', Attributes, False$)
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
Convert @VM to @TM in StatusCode
|
|
Error_Services('Add', 'Error creating the ' : QueueTable : ' table in the ' : Service : ' service.' : @TM : @TM : 'StatusCode' : @TM : '=========' : @TM : StatusCode)
|
|
end else
|
|
// The table has been created. Set the resize lock to 4.
|
|
Fix_LH(QueueTable, 5, True$, 4)
|
|
ReplicationQueueCreated = True$
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
// Always release the cached list for the queue tables if this service is called.
|
|
If QueueTableList@ NE '' then SRP_List('Release', QueueTableList@) ; QueueTableList@ = ''
|
|
If QueueHandleList@ NE '' then SRP_List('Release', QueueHandleList@) ; QueueHandleList@ = ''
|
|
If QueueTableExists@ NE '' then SRP_List('Release', QueueTableExists@) ; QueueTableExists@ = ''
|
|
|
|
Response = ReplicationQueueCreated
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// DeleteReplicationQueue
|
|
//
|
|
// Application - Name of the database application for the table. - [Required]
|
|
// Table - Name of the database table. - [Required]
|
|
//
|
|
// Deletes the replication queue table for the indicated database table.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service DeleteReplicationQueue(Application, Table)
|
|
|
|
Convert @Lower_Case to @Upper_Case in Application
|
|
Convert @Lower_Case to @Upper_Case in Table
|
|
|
|
ReplicationQueueDeleted = False$ ; // Assume this will not be deleted for now.
|
|
|
|
If Len(Application) AND Len(Table) then
|
|
QueuePath = Replication_Services('GetSourceQueuePath')
|
|
If Error_Services('NoError') then
|
|
QueueTable = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
HaveQueueTable = False$
|
|
// First attempt to open the private replication queue table.
|
|
Set_Status(0)
|
|
Open QueueTable to hQueueTable then
|
|
HaveQueueTable = True$
|
|
end else
|
|
// Since the Open statement didn't work, attempt to attach the private replication queue table.
|
|
Set_Status(0)
|
|
Attach_Table(QueuePath : '', QueueTable, 'GLOBAL', '')
|
|
If Get_Status(StatusCode) else
|
|
HaveQueueTable = True$
|
|
end
|
|
end
|
|
|
|
If HaveQueueTable then
|
|
// The replication queue table is available, delete using the DATA prefix as there should be
|
|
// no dictionary.
|
|
Set_Status(0)
|
|
Delete_Table('DATA.' : QueueTable, True$, Status)
|
|
If Get_Status(StatusCode) then
|
|
Set_Status(0)
|
|
Error_Services('Add', 'Error deleting the ' : QueueTable : ' in the ' : Service : ' service.')
|
|
end else
|
|
ReplicationQueueDeleted = True$
|
|
end
|
|
end
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Application or Table arguments were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
// Always release the cached list for the queue tables if this service is called.
|
|
If QueueTableList@ NE '' then SRP_List('Release', QueueTableList@) ; QueueTableList@ = ''
|
|
If QueueHandleList@ NE '' then SRP_List('Release', QueueHandleList@) ; QueueHandleList@ = ''
|
|
If QueueTableExists@ NE '' then SRP_List('Release', QueueTableExists@) ; QueueTableExists@ = ''
|
|
|
|
Response = ReplicationQueueDeleted
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSRPEngineServerINI
|
|
//
|
|
// EnginePath - Path to where the SRP Engine Server is located. - [Required]
|
|
// SRPEngineServerINI - JSON object containing the INI sections and key/values. - [Required]
|
|
//
|
|
// Creates or updates the SRP Engine Server INI file.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSRPEngineServerINI(EnginePath, SRPEngineServerINI)
|
|
|
|
If EnginePath NE '' OR SRPEngineServerINI NE '' then
|
|
If SRP_JSON(objINI, 'Parse', SRPEngineServerINI) EQ '' then
|
|
Title = SRP_JSON(objINI, 'GetValue', 'General.Title', '')
|
|
If Title EQ '' then Title = 'SRP Engine Server'
|
|
Location = SRP_JSON(objINI, 'GetValue', 'Directories.Location', '')
|
|
If Location EQ '' then Location = Drive()
|
|
WorkingDir = SRP_JSON(objINI, 'GetValue', 'Directories.WorkingDir', '')
|
|
If WorkingDir EQ '' then WorkingDir = Drive()
|
|
Port = SRP_JSON(objINI, 'GetValue', 'EnginePool.Port', '')
|
|
EngineCount = SRP_JSON(objINI, 'GetValue', 'EnginePool.EngineCount', '')
|
|
If EngineCount EQ '' then EngineCount = 1
|
|
BasePipeName = SRP_JSON(objINI, 'GetValue', 'EnginePool.BasePipeName', '')
|
|
If BasePipeName EQ '' then BasePipeName = 'EN'
|
|
ShowEngines = SRP_JSON(objINI, 'GetValue', 'EnginePool.ShowEngines', 'False')
|
|
Database = SRP_JSON(objINI, 'GetValue', 'OInsight.Database', '')
|
|
Username = SRP_JSON(objINI, 'GetValue', 'OInsight.Username', '')
|
|
Password = SRP_JSON(objINI, 'GetValue', 'OInsight.Password', '')
|
|
Command = SRP_JSON(objINI, 'GetValue', 'Autocommand.Command', '')
|
|
Interval = SRP_JSON(objINI, 'GetValue', 'Autocommand.Interval', '')
|
|
InitCommand = SRP_JSON(objINI, 'GetValue', 'Autocommand.InitCommand', '')
|
|
SRP_JSON(objINI, 'Release')
|
|
EnginePath = SRP_Path('AddBackslash', EnginePath)
|
|
ExeName = Replication_Services('GetSRPEngineServerExeName', EnginePath)
|
|
BaseName = ExeName[1, '.']
|
|
INIFile = EnginePath : BaseName : '.ini' : \00\
|
|
WritePrivateProfileString('General' : \00\, 'Title' : \00\, Title : \00\, INIFile)
|
|
WritePrivateProfileString('Directories' : \00\, 'Location' : \00\, Location : \00\, INIFile)
|
|
WritePrivateProfileString('Directories' : \00\, 'WorkingDir' : \00\, WorkingDir : \00\, INIFile)
|
|
WritePrivateProfileString('EnginePool' : \00\, 'Port' : \00\, Port : \00\, INIFile)
|
|
WritePrivateProfileString('EnginePool' : \00\, 'EngineCount' : \00\, EngineCount : \00\, INIFile)
|
|
WritePrivateProfileString('EnginePool' : \00\, 'BasePipeName' : \00\, BasePipeName : \00\, INIFile)
|
|
WritePrivateProfileString('EnginePool' : \00\, 'ShowEngines' : \00\, ShowEngines : \00\, INIFile)
|
|
WritePrivateProfileString('OInsight' : \00\, 'Database' : \00\, Database : \00\, INIFile)
|
|
WritePrivateProfileString('OInsight' : \00\, 'Username' : \00\, Username : \00\, INIFile)
|
|
WritePrivateProfileString('OInsight' : \00\, 'Password' : \00\, Password : \00\, INIFile)
|
|
WritePrivateProfileString('Autocommand' : \00\, 'Command' : \00\, Command : \00\, INIFile)
|
|
WritePrivateProfileString('Autocommand' : \00\, 'Interval' : \00\, Interval : \00\, INIFile)
|
|
WritePrivateProfileString('Autocommand' : \00\, 'InitCommand' : \00\, InitCommand : \00\, INIFile)
|
|
end else
|
|
Error_Services('Add', 'Error parsing JSON object in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'EnginePath or SRPEngineServerINI argument was missing or is not a valid number in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSRPEngineServerINI
|
|
//
|
|
// EnginePath - Path to where the SRP Engine Server is located. - [Required]
|
|
//
|
|
// Returns an @FM/@VM array of SRPEngineServer.INI names and values.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSRPEngineServerINI(EnginePath)
|
|
|
|
SRPEngineServerINI = ''
|
|
|
|
If EnginePath NE '' then
|
|
If SRP_JSON(objINI, 'New') then
|
|
EnginePath = SRP_Path('AddBackslash', EnginePath)
|
|
ExeName = Replication_Services('GetSRPEngineServerExeName', EnginePath)
|
|
BaseName = ExeName[1, '.']
|
|
INIFile = EnginePath : BaseName : '.ini' : \00\
|
|
|
|
Title = Str(\00\, 1024)
|
|
GetPrivateProfileString('General' : \00\, 'Title' : \00\, \00\, Title, Len(Title), INIFile)
|
|
Title = Title[1, \00\]
|
|
Location = Str(\00\, 1024)
|
|
GetPrivateProfileString('Directories' : \00\, 'Location' : \00\, \00\, Location, Len(Location), INIFile)
|
|
Location = Location[1, \00\]
|
|
WorkingDir = Str(\00\, 1024)
|
|
GetPrivateProfileString('Directories' : \00\, 'WorkingDir' : \00\, \00\, WorkingDir, Len(WorkingDir), INIFile)
|
|
WorkingDir = WorkingDir[1, \00\]
|
|
Port = Str(\00\, 1024)
|
|
GetPrivateProfileString('EnginePool' : \00\, 'Port' : \00\, \00\, Port, Len(Port), INIFile)
|
|
Port = Port[1, \00\]
|
|
EngineCount = Str(\00\, 1024)
|
|
GetPrivateProfileString('EnginePool' : \00\, 'EngineCount' : \00\, \00\, EngineCount, Len(EngineCount), INIFile)
|
|
EngineCount = EngineCount[1, \00\]
|
|
BasePipeName = Str(\00\, 1024)
|
|
GetPrivateProfileString('EnginePool' : \00\, 'BasePipeName' : \00\, \00\, BasePipeName, Len(BasePipeName), INIFile)
|
|
BasePipeName = BasePipeName[1, \00\]
|
|
ShowEngines = Str(\00\, 1024)
|
|
GetPrivateProfileString('EnginePool' : \00\, 'ShowEngines' : \00\, \00\, ShowEngines, Len(ShowEngines), INIFile)
|
|
ShowEngines = ShowEngines[1, \00\]
|
|
Database = Str(\00\, 1024)
|
|
GetPrivateProfileString('OInsight' : \00\, 'Database' : \00\, \00\, Database, Len(Database), INIFile)
|
|
Database = Database[1, \00\]
|
|
Username = Str(\00\, 1024)
|
|
GetPrivateProfileString('OInsight' : \00\, 'Username' : \00\, \00\, Username, Len(Username), INIFile)
|
|
Username = Username[1, \00\]
|
|
Password = Str(\00\, 1024)
|
|
GetPrivateProfileString('OInsight' : \00\, 'Password' : \00\, \00\, Password, Len(Password), INIFile)
|
|
Password = Password[1, \00\]
|
|
Command = Str(\00\, 1024)
|
|
GetPrivateProfileString('Autocommand' : \00\, 'Command' : \00\, \00\, Command, Len(Command), INIFile)
|
|
Command = Command[1, \00\]
|
|
Interval = Str(\00\, 1024)
|
|
GetPrivateProfileString('Autocommand' : \00\, 'Interval' : \00\, \00\, Interval, Len(Interval), INIFile)
|
|
Interval = Interval[1, \00\]
|
|
InitCommand = Str(\00\, 1024)
|
|
GetPrivateProfileString('Autocommand' : \00\, 'InitCommand' : \00\, \00\, InitCommand, Len(InitCommand), INIFile)
|
|
InitCommand = InitCommand[1, \00\]
|
|
|
|
// General settings.
|
|
If SRP_JSON(objGeneral, 'New') then
|
|
SRP_JSON(objGeneral, 'SetValue', 'Title', Title, 'String')
|
|
SRP_JSON(objINI, 'Set', 'General', objGeneral)
|
|
SRP_JSON(objGeneral, 'Release')
|
|
end
|
|
// Directories settings.
|
|
If SRP_JSON(objDirectories, 'New') then
|
|
SRP_JSON(objDirectories, 'SetValue', 'Location', Location, 'String')
|
|
SRP_JSON(objDirectories, 'SetValue', 'WorkingDir', WorkingDir, 'String')
|
|
SRP_JSON(objINI, 'Set', 'Directories', objDirectories)
|
|
SRP_JSON(objDirectories, 'Release')
|
|
end
|
|
// EnginePool settings.
|
|
If SRP_JSON(objEnginePool, 'New') then
|
|
SRP_JSON(objEnginePool, 'SetValue', 'Port', Port, 'String')
|
|
SRP_JSON(objEnginePool, 'SetValue', 'EngineCount', EngineCount, 'String')
|
|
SRP_JSON(objEnginePool, 'SetValue', 'BasePipeName', BasePipeName, 'String')
|
|
SRP_JSON(objEnginePool, 'SetValue', 'ShowEngines', ShowEngines)
|
|
SRP_JSON(objINI, 'Set', 'EnginePool', objEnginePool)
|
|
SRP_JSON(objEnginePool, 'Release')
|
|
end
|
|
// OInsight settings.
|
|
If SRP_JSON(objOInsight, 'New') then
|
|
SRP_JSON(objOInsight, 'SetValue', 'Database', Database, 'String')
|
|
SRP_JSON(objOInsight, 'SetValue', 'Username', Username, 'String')
|
|
SRP_JSON(objOInsight, 'SetValue', 'Password', Password, 'String')
|
|
SRP_JSON(objINI, 'Set', 'OInsight', objOInsight)
|
|
SRP_JSON(objOInsight, 'Release')
|
|
end
|
|
// Autocommand settings.
|
|
If SRP_JSON(objAutocommand, 'New') then
|
|
SRP_JSON(objAutocommand, 'SetValue', 'Command', Command, 'String')
|
|
SRP_JSON(objAutocommand, 'SetValue', 'Interval', Interval, 'String')
|
|
SRP_JSON(objAutocommand, 'SetValue', 'InitCommand', InitCommand, 'String')
|
|
SRP_JSON(objINI, 'Set', 'Autocommand', objAutocommand)
|
|
SRP_JSON(objAutocommand, 'Release')
|
|
end
|
|
SRPEngineServerINI = SRP_JSON(objINI, 'Stringify', 'Fast')
|
|
SRP_JSON(objINI, 'Release')
|
|
end else
|
|
Error_Services('Add', 'Error creating a JSON object in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'EnginePath argument was missing or is not a valid number in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = SRPEngineServerINI
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSRPEngineServerExeName
|
|
//
|
|
// ExeName - Name of the SRP Engine Server executable file.
|
|
//
|
|
// Sets the name of the SRP Engine Server executable that the SRP Replication Manager will use.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSRPEngineServerExeName(ExeName)
|
|
|
|
If Len(ExeName) then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_EXE_NAME'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write ExeName to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing source server path in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking source server path in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSRPEngineServerExeName
|
|
//
|
|
// Returns the name of the SRP Engine Server EXE file used by the SRP Replication Manager.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSRPEngineServerExeName()
|
|
|
|
ExeName = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_EXE_NAME'
|
|
Read ExeName from hRepConfigTable, RepConfigKeyID else Null
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = ExeName
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSRPEngineServerTitle
|
|
//
|
|
// Returns title used to uniquely name the SRP Engine Server.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSRPEngineServerTitle(EnginePath)
|
|
|
|
Title = ''
|
|
|
|
If EnginePath NE '' then
|
|
EnginePath = SRP_Path('AddBackslash', EnginePath)
|
|
ExeName = Replication_Services('GetSRPEngineServerExeName', EnginePath)
|
|
BaseName = ExeName[1, '.']
|
|
INIFile = EnginePath : BaseName : '.ini' : \00\
|
|
|
|
Title = Str(\00\, 1024)
|
|
GetPrivateProfileString('General' : \00\, 'Title' : \00\, \00\, Title, Len(Title), INIFile)
|
|
Title = Title[1, \00\]
|
|
end else
|
|
Error_Services('Add', 'EnginePath argument was missing or is not a valid number in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = Title
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetDailyNotificationEmails
|
|
//
|
|
// NotificationEmails - @VM list of email addresses. - [Required]
|
|
//
|
|
// Sets the list of emails used for daily notifications.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetDailyNotificationEmails(NotificationEmails)
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_DAILY_NOTIFICATION_EMAILS'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write NotificationEmails to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing daily notification emails in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking daily notification emails in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetDailyNotificationEmails
|
|
//
|
|
// Returns an @VM list of emails used for daily notifications.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetDailyNotificationEmails()
|
|
|
|
NotificationEmails = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_DAILY_NOTIFICATION_EMAILS'
|
|
Read NotificationEmails from hRepConfigTable, RepConfigKeyID else NotificationEmails = ''
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = NotificationEmails
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetEmergencyNotificationEmails
|
|
//
|
|
// NotificationEmails - @VM list of email addresses. - [Required]
|
|
//
|
|
// Sets the list of emails used for emergency notifications.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetEmergencyNotificationEmails(NotificationEmails)
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_EMERGENCY_NOTIFICATION_EMAILS'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write NotificationEmails to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing emergency notification emails in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking emergency notification emails in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetEmergencyNotificationEmails
|
|
//
|
|
// Returns an @VM list of emails used for emergency notifications.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetEmergencyNotificationEmails()
|
|
|
|
NotificationEmails = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_EMERGENCY_NOTIFICATION_EMAILS'
|
|
Read NotificationEmails from hRepConfigTable, RepConfigKeyID else NotificationEmails = ''
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = NotificationEmails
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SetSMTPSettings
|
|
//
|
|
// SMTPSettings - @FM array of SMTP settings. - [Required]
|
|
//
|
|
// Sets the SMTP settings.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SetSMTPSettings(SMTPSettings)
|
|
|
|
If SMTPSettings NE '' then
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SMTP_SETTINGS'
|
|
Lock hRepConfigTable, RepConfigKeyID then
|
|
Write SMTPSettings to hRepConfigTable, RepConfigKeyID else
|
|
Error_Services('Add', 'Error writing SMTP settings in the ' : Service : ' service.')
|
|
end
|
|
Unlock hRepConfigTable, RepConfigKeyID
|
|
end else
|
|
Error_Services('Add', 'Error locking SMTP settings in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
end else
|
|
Error_Services('Add', 'SMTPSettings argument was missing in the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetSMTPSettings
|
|
//
|
|
// Returns an @FM array of SMTP settings.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetSMTPSettings()
|
|
|
|
SMTPSettings = ''
|
|
|
|
Open RepConfigTable$ to hRepConfigTable then
|
|
RepConfigKeyID = 'REPLICATION_SMTP_SETTINGS'
|
|
Read SMTPSettings from hRepConfigTable, RepConfigKeyID else SMTPSettings = ''
|
|
end else
|
|
Error_Services('Add', 'Error opening the ' : RepConfigTable$ : ' table in the ' : Service : ' service.')
|
|
end
|
|
|
|
Response = SMTPSettings
|
|
|
|
end service
|
|
|
|
|
|
Service PostToDailyLog:
|
|
|
|
end service
|
|
|
|
|
|
Service SendDailyLog:
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// LogError
|
|
//
|
|
// OriginalService - The original service that triggered the alert condition.
|
|
// Action -
|
|
// Application -
|
|
// Volume -
|
|
// Table -
|
|
// KeyID -
|
|
// StatusCode - This is the result of Get_Status at the time the alert condition was triggered.
|
|
// FileError - This is the content of @FILE_ERROR at the time the alert condition was triggered.
|
|
// Position - Line number or other position identifier in the code that called the LogError service.
|
|
// AdditionalMessage - Additional message to include in the log.
|
|
//
|
|
// Logs the error.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service LogError(OriginalService, Action, Application, Volume, Table, KeyID, StatusCode, FileError, Position, AdditionalMessage)
|
|
|
|
ErrorLogPath = Replication_Services('GetErrorLogPath')
|
|
|
|
If ErrorLogPath NE '' then
|
|
LogDate = Oconv(Date(), 'D4/')
|
|
LogTime = Oconv(Time(), 'MTS')
|
|
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' ReplicateTransactionsError.log'
|
|
Headers = 'Logging DTM' : @FM : 'Service' : @FM : 'Action' : @FM : 'Application' : @FM : 'Volume' : @FM : 'Table' : @FM : 'KeyID' : @FM : 'QueueTable' : @FM : 'EngineName' : @FM : 'StatusCode' : @FM : 'FileError' : @FM : 'Position' : @FM : 'Additional Message'
|
|
objLog = Logging_Services('NewLog', ErrorLogPath, LogFileName, CRLF$, Tab$, Headers, '', False$, False$)
|
|
LoggingDTM = LogDate : ' ' : LogTime ; // Logging DTM
|
|
|
|
QueueTable = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
CommandLine = GetCommandLine()
|
|
Swap '/S=' with @FM in CommandLine
|
|
EngineName = CommandLine<2> ; // This is the NamedPipe identifier for the engine processing this service.
|
|
|
|
Convert @FM to ' ' in StatusCode
|
|
Convert @FM to ' ' in FileError
|
|
Convert @FM to ' ' in AdditionalMessage
|
|
|
|
LogData = ''
|
|
LogData<1> = LoggingDTM
|
|
LogData<2> = OriginalService
|
|
LogData<3> = Action
|
|
LogData<4> = Application
|
|
LogData<5> = Volume
|
|
LogData<6> = Table
|
|
LogData<7> = KeyID
|
|
LogData<8> = QueueTable
|
|
LogData<9> = EngineName
|
|
LogData<10> = StatusCode
|
|
LogData<11> = FileError
|
|
LogData<12> = Position
|
|
LogData<13> = AdditionalMessage
|
|
Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// SendEmergencyAlert
|
|
//
|
|
// OriginalService - The original service that triggered the alert condition.
|
|
// Action -
|
|
// Application -
|
|
// Volume -
|
|
// Table -
|
|
// KeyID -
|
|
// StatusCode - This is the result of Get_Status at the time the alert condition was triggered.
|
|
// FileError - This is the content of @FILE_ERROR at the time the alert condition was triggered.
|
|
// Position - Line number or other position identifier in the code that called the SendEmergencyAlert service.
|
|
// AdditionalMessage - Additional message to include in the emergency alert.
|
|
//
|
|
// Sends out an emergency alert message to the designated emergency notification email accounts.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service SendEmergencyAlert(OriginalService, Action, Application, Volume, Table, KeyID, StatusCode, FileError, Position, AdditionalMessage)
|
|
|
|
SMTPSettings = Replication_Services('GetSMTPSettings')
|
|
NotificationEmails = Replication_Services('GetEmergencyNotificationEmails')
|
|
Convert @VM to ',' in NotificationEmails
|
|
|
|
If (SMTPSettings NE '') AND (NotificationEmails NE '') then
|
|
|
|
MessageBody = ''
|
|
|
|
If OriginalService NE '' then
|
|
MessageBody := 'Service: ' : OriginalService : CRLF$
|
|
end
|
|
|
|
If Action NE '' then
|
|
MessageBody := 'Action: ' : Action : CRLF$
|
|
end
|
|
|
|
If Application NE '' then
|
|
MessageBody := 'Application: ' : Application : CRLF$
|
|
end
|
|
|
|
If Volume NE '' then
|
|
MessageBody := 'Volume: ' : Volume : CRLF$
|
|
end
|
|
|
|
If Table NE '' then
|
|
MessageBody := 'Table: ' : Table : CRLF$
|
|
end
|
|
|
|
If (Application NE '') AND (Table NE '') then
|
|
QueueTable = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
MessageBody := 'QueueTable: ' : QueueTable : CRLF$
|
|
end
|
|
|
|
CommandLine = GetCommandLine()
|
|
Swap '/S=' with @FM in CommandLine
|
|
EngineName = CommandLine<2> ; // This is the NamedPipe identifier for the engine processing this service.
|
|
If EngineName NE '' then
|
|
MessageBody := 'EngineName: ' : EngineName : CRLF$
|
|
end
|
|
|
|
If KeyID NE '' then
|
|
MessageBody := 'KeyID: ' : KeyID : CRLF$
|
|
end
|
|
|
|
If Position NE '' then
|
|
MessageBody := 'Position: ' : Position : CRLF$
|
|
end
|
|
|
|
If StatusCode NE '' then
|
|
Swap @VM with CRLF$ in StatusCode
|
|
MessageBody := CRLF$
|
|
MessageBody := 'Get_Status() Results:' : CRLF$
|
|
MessageBody := '---------------------' : CRLF$
|
|
MessageBody := StatusCode : CRLF$
|
|
end
|
|
|
|
If FileError NE '' then
|
|
Swap @FM with CRLF$ in FileError
|
|
Swap @VM with CRLF$ in FileError
|
|
MessageBody := CRLF$
|
|
MessageBody := '@FILE_ERROR Results:' : CRLF$
|
|
MessageBody := '--------------------' : CRLF$
|
|
MessageBody := FileError : CRLF$
|
|
end
|
|
|
|
If AdditionalMessage NE '' then
|
|
Swap @FM with CRLF$ in AdditionalMessage
|
|
MessageBody := CRLF$
|
|
MessageBody := 'Additional Message:' : CRLF$
|
|
MessageBody := '-------------------' : CRLF$
|
|
MessageBody := AdditionalMessage : CRLF$
|
|
end
|
|
|
|
Message = ''
|
|
Message<SRPMail_Subject$> = 'Replication Manager - Emergency Alert'
|
|
Message<SRPMail_From$> = SMTPSettings<7>
|
|
Message<SRPMail_To$> = NotificationEmails
|
|
Message<SRPMail_Cc$> = ''
|
|
Message<SRPMail_Bcc$> = ''
|
|
Message<SRPMail_ReplyTo$> = ''
|
|
Message<SRPMail_BodyType$> = 'TEXT'
|
|
Message<SRPMail_Body$> = MessageBody
|
|
Message<SRPMail_Attachments$> = ''
|
|
Message<SRPMail_Importance$> = SRPMail_Importance_High$
|
|
|
|
ConfigFile = ''
|
|
ConfigFile<SendUsing$> = SendUsing_Port$
|
|
ConfigFile<SMTPServerPickupDir$> = ''
|
|
ConfigFile<SMTPServerPort$> = SMTPSettings<2>
|
|
ConfigFile<SMTPServer$> = SMTPSettings<1>
|
|
ConfigFile<SMTPAuthenticate$> = SMTPSettings<3>
|
|
ConfigFile<SMTPSendUsername$> = SMTPSettings<4>
|
|
ConfigFile<SMTPSendPassword$> = SMTPSettings<5>
|
|
ConfigFile<SMTPUseSSL$> = SMTPSettings<6>
|
|
|
|
If Xlate('SYSOBJ', '$SRP_SEND_MAIL', '', 'X') NE '' then
|
|
Success = SRP_Send_Mail(Message, ConfigFile)
|
|
|
|
If Success NE True$ then
|
|
Error_Services('Add', 'Error sending email in the ' : Service : ' service. Error string: ' : Success)
|
|
end
|
|
end else
|
|
AdditionalMessage = 'The SRP Mail utility does not appear to be installed.'
|
|
Replication_Services('LogError', Service, Action, Application, Volume, Table, KeyID, StatusCode, FileError, Position, AdditionalMessage)
|
|
end
|
|
end else
|
|
Error_Services('Add', 'SMTP Settings or Notification Emails were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
// Set the system enabled flag to false to stop the replication. Tell the SRP Engine Server to quit.
|
|
Replication_Services('SetSystemEnabledFlag', False$)
|
|
Send_Info('SRPENGINESERVERQUIT')
|
|
Stop
|
|
|
|
end service
|
|
|
|
|
|
Service SendAlert(OriginalService, Action, Application, Volume, Table, KeyID, StatusCode, FileError, Position, AdditionalMessage)
|
|
|
|
SMTPSettings = Replication_Services('GetSMTPSettings')
|
|
NotificationEmails = Replication_Services('GetEmergencyNotificationEmails')
|
|
Convert @VM to ',' in NotificationEmails
|
|
|
|
If (SMTPSettings NE '') AND (NotificationEmails NE '') then
|
|
|
|
MessageBody = ''
|
|
|
|
If OriginalService NE '' then
|
|
MessageBody := 'Service: ' : OriginalService : CRLF$
|
|
end
|
|
|
|
If Action NE '' then
|
|
MessageBody := 'Action: ' : Action : CRLF$
|
|
end
|
|
|
|
If Application NE '' then
|
|
MessageBody := 'Application: ' : Application : CRLF$
|
|
end
|
|
|
|
If Volume NE '' then
|
|
MessageBody := 'Volume: ' : Volume : CRLF$
|
|
end
|
|
|
|
If Table NE '' then
|
|
MessageBody := 'Table: ' : Table : CRLF$
|
|
end
|
|
|
|
If (Application NE '') AND (Table NE '') then
|
|
QueueTable = Replication_Services('GetReplicationQueueTable', Application, Table)
|
|
MessageBody := 'QueueTable: ' : QueueTable : CRLF$
|
|
end
|
|
|
|
CommandLine = GetCommandLine()
|
|
Swap '/S=' with @FM in CommandLine
|
|
EngineName = CommandLine<2> ; // This is the NamedPipe identifier for the engine processing this service.
|
|
If EngineName NE '' then
|
|
MessageBody := 'EngineName: ' : EngineName : CRLF$
|
|
end
|
|
|
|
If KeyID NE '' then
|
|
MessageBody := 'KeyID: ' : KeyID : CRLF$
|
|
end
|
|
|
|
If Position NE '' then
|
|
MessageBody := 'Position: ' : Position : CRLF$
|
|
end
|
|
|
|
If StatusCode NE '' then
|
|
Swap @VM with CRLF$ in StatusCode
|
|
MessageBody := CRLF$
|
|
MessageBody := 'Get_Status() Results:' : CRLF$
|
|
MessageBody := '---------------------' : CRLF$
|
|
MessageBody := StatusCode : CRLF$
|
|
end
|
|
|
|
If FileError NE '' then
|
|
Swap @FM with CRLF$ in FileError
|
|
Swap @VM with CRLF$ in FileError
|
|
MessageBody := CRLF$
|
|
MessageBody := '@FILE_ERROR Results:' : CRLF$
|
|
MessageBody := '--------------------' : CRLF$
|
|
MessageBody := FileError : CRLF$
|
|
end
|
|
|
|
If AdditionalMessage NE '' then
|
|
Swap @FM with CRLF$ in AdditionalMessage
|
|
MessageBody := CRLF$
|
|
MessageBody := 'Additional Message:' : CRLF$
|
|
MessageBody := '-------------------' : CRLF$
|
|
MessageBody := AdditionalMessage : CRLF$
|
|
end
|
|
|
|
Message = ''
|
|
Message<SRPMail_Subject$> = 'Replication Manager - Emergency Alert'
|
|
Message<SRPMail_From$> = SMTPSettings<7>
|
|
Message<SRPMail_To$> = NotificationEmails
|
|
Message<SRPMail_Cc$> = ''
|
|
Message<SRPMail_Bcc$> = ''
|
|
Message<SRPMail_ReplyTo$> = ''
|
|
Message<SRPMail_BodyType$> = 'TEXT'
|
|
Message<SRPMail_Body$> = MessageBody
|
|
Message<SRPMail_Attachments$> = ''
|
|
Message<SRPMail_Importance$> = SRPMail_Importance_High$
|
|
|
|
ConfigFile = ''
|
|
ConfigFile<SendUsing$> = SendUsing_Port$
|
|
ConfigFile<SMTPServerPickupDir$> = ''
|
|
ConfigFile<SMTPServerPort$> = SMTPSettings<2>
|
|
ConfigFile<SMTPServer$> = SMTPSettings<1>
|
|
ConfigFile<SMTPAuthenticate$> = SMTPSettings<3>
|
|
ConfigFile<SMTPSendUsername$> = SMTPSettings<4>
|
|
ConfigFile<SMTPSendPassword$> = SMTPSettings<5>
|
|
ConfigFile<SMTPUseSSL$> = SMTPSettings<6>
|
|
|
|
If Xlate('SYSOBJ', '$SRP_SEND_MAIL', '', 'X') NE '' then
|
|
Success = SRP_Send_Mail(Message, ConfigFile)
|
|
|
|
If Success NE True$ then
|
|
Error_Services('Add', 'Error sending email in the ' : Service : ' service. Error string: ' : Success)
|
|
end
|
|
end else
|
|
AdditionalMessage = 'The SRP Mail utility does not appear to be installed.'
|
|
Replication_Services('LogError', Service, Action, Application, Volume, Table, KeyID, StatusCode, FileError, Position, AdditionalMessage)
|
|
end
|
|
end else
|
|
Error_Services('Add', 'SMTP Settings or Notification Emails were missing from the ' : Service : ' service.')
|
|
end
|
|
|
|
end service
|
|
|
|
|
|
Service RecompileEntity()
|
|
|
|
end service
|
|
|
|
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
// GetVersion
|
|
//
|
|
// Returns the version of the SRP Replication Manager being used. The response will be in the following format:
|
|
//
|
|
// x.x.x [RCx]
|
|
// mm.dd.yyyy hh:mmA/P
|
|
//
|
|
// A carriage-return/line-feed character will be used to separate the two pieces of information.
|
|
//----------------------------------------------------------------------------------------------------------------------
|
|
Service GetVersion()
|
|
|
|
Version = Database_Services('ReadDataRow', RepConfigTable$, 'SRP_REPLICATION_MANAGER_VERSION')
|
|
|
|
Swap @FM with CRLF$ in Version
|
|
|
|
Response = Version
|
|
|
|
end service
|