Description
Loop, and in each iteration check whether there is work to do.
Parameters
| oiReturnStatus | output | integer | Return status of the method. |
Internal usage
BLF
program code (program1/bbasedaemonprocessor.p)
/* =================================================================================================== */
/* Find the Daemon it's debug specifications */
/* Hold them in class data-items and pass the as parameters to the methods called from PerformWorkItem */
/* =================================================================================================== */
assign viExternalDebugLevel = 0
vlAutoRecoverDaemons = false
vcConfigFile = search(vcFcDaemonName + ".config":U)
no-error.
if vcConfigFile <> ? and
error-status:error = false
then do :
input from value(vcConfigFile).
repeat on error undo, leave: /* cannot use throw */
import unformatted vcLine.
if vcLine begins "DebugLevel=":U
then assign viExternalDebugLevel = integer(substring(vcLine,LENGTH ("DebugLevel=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) no-error.
/* backward compatibility */
else if vcLine begins "BLDebugLevel=":U
then assign viExternalDebugLevel = integer(substring(vcLine,LENGTH ("BLDebugLevel=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) no-error.
else if vcLine begins "AutoRecoverDaemons=":U
then assign vlAutoRecoverDaemons = (trim(substring(vcLine,LENGTH ("AutoRecoverDaemons=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) = "TRUE":U or
trim(substring(vcLine,LENGTH ("AutoRecoverDaemons=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U)) = "YES":U) no-error.
else if vcLine begins "AutoRecoverDaemonsIntervalSec=":U
then assign viAutoRecoverDaemonsIntervalSec = integer(trim(substring(vcLine,LENGTH ("AutoRecoverDaemonsIntervalSec=":U,"CHARACTER":U) + 1,-1,"CHARACTER":U))) no-error.
end. /* repeat */
input close.
end. /* if vcConfigFile <> ? */
/* ========================================================================== */
/* By default all the Daemon processes will be stopped at the end. */
/* The large-interval for calling the submethod will be 20 minutes by default */
/* ========================================================================== */
assign vlStopAllDaemonProcesses = true
viAutoRecoverDaemonsIntervalSec = (if viAutoRecoverDaemonsIntervalSec = 0 or
viAutoRecoverDaemonsIntervalSec = ? or
viAutoRecoverDaemonsIntervalSec < 60
then 1200
else viAutoRecoverDaemonsIntervalSec).
/* ================= */
/* Start the Looping */
/* ================= */
daemon-loop-block:
repeat on error undo, throw:
assign viLoop = 1.
/* ================================================================= */
/* Check whether the daemon needs to be stopped. */
/* ================================================================= */
<Q-18 run DaemonInfoLimited (all) (Read) (NoCache)
(input 0, (DaemonId)
input vcFcDaemonName, (DaemonName)
input '':U, (DaemonStatus)
input '':U, (DaemonStatusDiffersFrom)
output dataset tqDaemonInfoLimited) in BBaseDaemon >
find first tqDaemonInfoLimited where
tqDaemonInfoLimited.tcDaemonName = vcFcDaemonName no-error.
if not available tqDaemonInfoLimited
or tqDaemonInfoLimited.tcDaemonStatus = {&DAEMONSTATUS-STOPPING}
or tqDaemonInfoLimited.tcDaemonStatus = {&DAEMONSTATUS-INACTIVE}
then leave daemon-loop-block.
/* In case the PID would not be in the list held in fcDaemon, we stop this process (but only this process) */
if lookup (string (viFcDaemonProcessId), tqDaemonInfoLimited.tcDaemonProcessIDs) = 0
and lookup (vcFcDaemonProcessHostName + ":" + string (viFcDaemonProcessId), tqDaemonInfoLimited.tcDaemonProcessIDs) = 0
then do:
<M-70 run SetMessageInDaemonLog
(input 'DaemonProcessor.LoopForWork; ':U + #T-78'The daemon process was stopped as it did not match with the daemon info in the database:':255(322033527)T-78# + string (viFcDaemonProcessId) + ' * ' + tqDaemonInfoLimited.tcDaemonProcessIDs (icMessage),
output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
assign vlStopAllDaemonProcesses = no.
leave daemon-loop-block.
end.
assign viFcDaemonInterval = tqDaemonInfoLimited.tiDaemonInterval
viRequestsNR = tqDaemonInfoLimited.tiDaemonNrOfRequestsInLoop.
/* ================================================================= */
/* Update the SessionTimeStamps each 30 min (1800 sec) - use UTC */
/* ================================================================= */
assign session:timezone = 0.
if ((today - vtLastUpdateDateStamp) * 86400 + (time - viLastUpdateTimeStampSec) > 1800)
or viLastUpdateTimeStampSec = 0
then do:
<M-77 run UpdateSessionTimeStamps
(input viSessionID (iiSessionID),
output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
assign session:timezone = 0.
assign vtLastUpdateDateStamp = today
viLastUpdateTimeStampSec = time.
end.
assign session:timezone = viTimeOffset.
/* ================================================================= */
/* Load external work to be done */
/* ================================================================= */
<M-7 run LoadExternalWork (output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
if viFcReturnSuper <> 0
then do :
<M-9 run SetMessageInDaemonLog
(input 'DaemonProcessor.LoopForWork; ':U + substitute('***LoadExternalWork failed with (&1)':U,string(vifcreturnSuper)) (icMessage),
output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
/* ================================================================= */
/* We do not leave the loop, producing the effort should give */
/* enough feedback. */
/* ================================================================= */
end.
/* ================================================================= */
/* Read the not-locked work queue records. */
/* ================================================================= */
assign vcDaemonQueueStatus = {&DAEMONQUEUESTATUS-WAITING}.
<Q-3 run GetNextDaemonQueue (all) (Read) (NoCache)
(input viFcDaemonId, (DaemonId)
input vcDaemonQueueStatus, (DaemonQueueStatus)
output dataset tqGetNextDaemonQueue) in BBaseDaemonQueue >
queue-loop-block:
for each tqGetNextDaemonQueue where
tqGetNextDaemonQueue.tiDaemonQueueId <> 0
by tqGetNextDaemonQueue.tiDaemonQueuePriority
on error undo, throw:
/* Use date and time in UTC */
session:timezone = 0.
vlNext = (tqGetNextDaemonQueue.ttDaemonQueueReqStartDate > today
or (tqGetNextDaemonQueue.ttDaemonQueueReqStartDate = today and
tqGetNextDaemonQueue.tiDaemonQueueReqStartTime > TIME)) and
tqGetNextDaemonQueue.ttDaemonQueueReqStartDate <> ?.
session:timezone = viTimeOffset.
if vlNext then next.
/* ================================================================= */
/* Try to lock the daemonqueue record. */
/* ================================================================= */
<M-20 run LockWorkItem
(input tqGetNextDaemonQueue.tiDaemonQueueId (iiDaemonQueueID),
input viFcDaemonProcessId (iiProcessID),
output vcMessage (ocMessage),
output vlLocked (olLocked),
output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
if viFcReturnSuper <> 0 and viFcReturnSuper ne -111 /* because we don't want to see in the log file that another instance is processing this item */
then do :
<M-5 run SetMessageInDaemonLog
(input 'DaemonProcessor.LoopForWork; ':U + substitute(#T-95'Unable to lock work item (&1) (&2).':255(613638236)T-95#,string(tqGetNextDaemonQueue.tiDaemonQueueId),vcMessage) (icMessage),
output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
end.
if vlLocked
then do :
/* ================================================================= */
/* The daemonqueue record is locked, now the desired action can be */
/* performed. */
/* ================================================================= */
<M-6 run PerformWorkItem
(input tqGetNextDaemonQueue.tiDaemonQueueId (iiDaemonQueueId),
output vlSuccess (olSuccess),
output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
/* Delete dynamic datasets created during processing */
run DeleteDSInPool.
/* Move error and warning messages */
empty temp-table tPassMessages.
for each tFcMessages on error undo, throw:
create tPassMessages.
raw-transfer tFcMessages to tPassMessages.
delete tFcMessages.
end.
if viFcReturnSuper <> 0
then do:
assign vcMessage = "DaemonProcessor.LoopForWork: " +
if viFcReturnSuper > 0
then trim(subst(#T-8'Warning processing work item &1, return status &2.':255(757252153)T-8#, string(tqGetNextDaemonQueue.tiDaemonQueueId), string(viFcReturnSuper)))
else trim(subst(#T-89'***Error processing work item &1, return status &2.':255(68296975)T-89#, string(tqGetNextDaemonQueue.tiDaemonQueueId), string(viFcReturnSuper))).
if viFcReturnSuper < 0
then assign vlSuccess = false.
<M-11 run SetMessageInDaemonLog
(input vcMessage (icMessage),
output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
create tPassMessages.
assign tPassMessages.tcFcMessage = trim(subst(#T-13'The work item processing has returned return status &1.':255(443782020)T-13#, string(viFcReturnSuper))).
end.
<M-21 run SetWorkResult
(input vlSuccess (ilSucces),
input tqGetNextDaemonQueue.tiDaemonQueueId (iiDaemonQueueID),
input tqGetNextDaemonQueue.tiDaemonQueuePriority (iiDaemonQueuePriority),
output vcMessage (ocErrorMessage),
output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
/* JLA - BTS 10365 : Catch possible errors which are returned from the SetWorkResult method. */
if viFcReturnSuper <> 0
then do :
<M-16 run SetMessageInDaemonLog
(input 'DaemonProcessor.LoopForWork; ':U + substitute('***SetWorkResult failed with (&1)':U,string(vifcreturnSuper)) + chr(10) + vcMessage (icMessage),
output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
end.
/* JLA - BTS 10365 : Catch possible errors which are returned from the SetWorkResult method. */
end.
/* ================================================================= */
/* Did we process enough requests ? */
/* ================================================================= */
assign viLoop = viLoop + 1.
if viLoop > viRequestsNR
then leave queue-loop-block.
end. /* queue-loop-block */
if vlDaemonNeedsToEnd
then leave daemon-loop-block.
/* ============================================================================== */
/* Clean up the non-persistent widget-pool to avoid build-up of dynamic objects. */
/* ============================================================================== */
delete widget-pool "non-persistent".
create widget-pool "non-persistent" persistent no-error.
/* ================================================================= */
/* Sleep for a moment */
/* ================================================================= */
if viFcDaemonInterval > 0 and
(viLoop < 100 and viLoop <= viRequestsNR)
then pause viFcDaemonInterval no-message.
else pause 2 no-message. /* Go sleeping for 2 seconds so the CPU can be given to another process */
/* ================================================================= */
/* Call the hook to enable customized code required at the end of */
/* each iteration of the loop. */
/* ================================================================= */
assign vlPerformedWork = viLoop > 1.
<M-22 run IterationEnd
(input vlPerformedWork (ilIterationPerformedWorkItems),
output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
if viFcReturnSuper < 0
then do:
assign oiReturnStatus = viFcReturnSuper.
leave daemon-loop-block.
end.
/* ============================================================================================================ */
/* Call a submethod that will only be called after a longer time (viAutoRecoverDaemonsIntervalSec). This method */
/* can start some other daemons that should be running but that aren't or some other actions that can take up */
/* quite some time and that shouldn't be called very frequently (only after viAutoRecoverDaemonsIntervalSec) */
/* When we switch to another date (pass midnight), then we call the submethod straight away */
/* ============================================================================================================ */
if vlAutoRecoverDaemons = true
then do :
/* Switch to timezone UTC */
assign session:timezone = 0.
/* Initialise the date-time-reference when not yet done once - make sure the first time we pass here it gets executed in 30 seconds or simply the next time we pass here */
if vtLastStartDateForStartOtherDmn = ? or
viLastStartTimeForStartOtherDmn = 0 or
viLastStartTimeForStartOtherDmn = ?
then if time > viAutoRecoverDaemonsIntervalSec
then assign vtLastStartDateForStartOtherDmn = today
viLastStartTimeForStartOtherDmn = time - viAutoRecoverDaemonsIntervalSec + 30.
else assign vtLastStartDateForStartOtherDmn = today - 1
viLastStartTimeForStartOtherDmn = 1.
/* Check if the interval is passed compared against the date-time-reference */
if today > vtLastStartDateForStartOtherDmn or
time > viLastStartTimeForStartOtherDmn + viAutoRecoverDaemonsIntervalSec
then do :
/* Call the sub-method that will do the actual processing - do not check for errors as we want to keep this daemon running even if starting other daemons failed */
<M-31 run LoopForWorkAutoRecoverDaemons (output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
/* Reset the date-time-reference */
assign vtLastStartDateForStartOtherDmn = today
viLastStartTimeForStartOtherDmn = time.
end. /* if today > vtLastStartDateForStartOtherDmn or */
/* Switch from timezone UTC to the current timezone*/
assign session:timezone = viTimeOffset.
end. /* if vlAutoRecoverDaemons = true */
/* ======================================== */
/* Check if the daemon has to stop its loop */
/* ======================================== */
if vlDaemonNeedsToEnd
then leave daemon-loop-block.
end. /* daemon-loop-block */
finally:
if vlStopAllDaemonProcesses
then do:
<M-14 run StopDaemon (output viFcReturnSuper (oiReturnStatus)) in BBaseDaemonProcessor>
if viFcReturnSuper <> 0
then do :
<M-15 run SetMessageInDaemonLog
(input 'DaemonProcessor.LoopForWork; ':U + #T-55'The daemon processor instance was unable to update the daemon status when stopping.':255(132822179)T-55# (icMessage),
output viDummy (oiReturnStatus)) in BBaseDaemonProcessor>
end.
end. /* vlStopDaemon */
end finally.