open-insight/LSL2/STPROC/REPLICATION_SERVICES.txt
Infineon\StieberD 7762b129af pre cutover push
2024-09-04 20:33:41 -07:00

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