Previous Topic: Case Study 15: Clustered MetricsNext Topic: Case Study 17: Built-in Functionality


Case Study 16: Business Logic Design Patterns

There are several “Design Patterns” which can be used in many business logics. Those design patterns are tested, and using them when applicable can save a lot of time and in most cases create more efficient business logic. This case study focuses on one such design pattern.

Update Counters Design Pattern

This design pattern is useful, in almost every business logic, which is intended to measure time between certain events. Examples of such business logics are business logics for measuring availability, downtime, mean time between failures, mean time to restore, average response time, average resolution time, percent of components with availability less then X, number of cases not resolved on time, etc.

The common part for all those business logics is that their result depends on the timestamp of various events they receive.

A rule of thumb for deciding if your business logic can benefit from this design pattern would be: if your business logics depends on the timestamp of the various events it receives it most probably should use this design pattern.

Skeleton of this Design Pattern

The code of a business logic that utilizes this pattern can be split into two parts: a framework and an implementation. The framework contains code which in most cases is fixed and does not change for the various business logics. This part is the same for calculation of availability and number of tickets not resolved on time. The implementation contains code which is specific to each business logic.

It is recommended to put those two parts of the code into separate business logic modules and reuse the module of the framework in different metrics.

Here is the code of the framework:

Dim g_PrevEventTimestamp

Sub OnLoad(time)
     g_PrevEventTimestamp = time
     InitializeStatusVariables
End Sub

Sub OnRegistration(Dispatcher)
     ' If there is a separate copy of status variables 
     ' for each registered resource depend on the registered
     ' resources you should set initial values to the
     ' status variables of the newly added resources here
End Sub

Sub OnPeriodStart(time)
     InitializeCounters
End Sub

Sub OnPeriodEnd(time, completePeriod)
     HandleEvent (time)
End Sub

Sub OnTimeslotEnter(time)
     HandleEvent (time)
End Sub

Sub OnTimeslotExit(time)
     HandleEvent (time)
End Sub


Function Result()
     Result = CalculateResult()
End Function

Sub HandleEvent(Time)
     Dim diff
     diff = TimeDiff( "s",Time,g_PrevEventTimestamp)
     UpdateCounters(diff)
     g_PrevEventTimestamp = Time
End Sub

Here is a skeleton of the implementation module:

' Define your status variables here. This can be one 
' simple global variable or many complex global variables
' depending on the business logic
Dim g_StatusVar_1, g_StatusVar_2, ... ,g_StatusVar_n

' Define your counters here. 
' This can be one simple global variable or many
' complex global variables depending on the business logic
Dim g_Counter_1, g_Counter_2, ... , g_Counter_n

Sub InitializeStatusVariables ()
     ' Set initial values to the various status variables
End Sub

Sub InitializeCounters ()
     ' Set initial values to the various counters
     g_Counter_1 = 0
     g_Counter_2 = 0
     '…
     g_Counter_n = 0
End Sub

Function CalculateResult ()
     ' Calculate the result. The result should depend on
     ' the values of the counters. It should not depend on
     ' the value of the status variables. It should not 
     ' change the values of the counters or the status 
     ' variables
End Function

Sub UpdateStatus(method, eventDetails)
     ' Update the value of the status variables based on
     ' the parameters (and posibly on the old value of the 
     ' status variables)
End Sub

Sub UpdateCounters(diff)
     ' Update the values of the counters based on their
     ' previous value, on the value of the status variables
     ' and on the value of the diff parameter.
     ' In many cases this calculation is also based on the
     ' value of Context.IsWithinTimeslot
End Sub

Sub OnEvent_1(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnEvent_1”,eventDetails)
End Sub

Sub OnEvent_2(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnEvent_2”,eventDetails)
End Sub

'...

Sub OnEvent_n(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnEvent_n”,eventDetails)
End Sub

To better explain this design pattern, here is an example of the implementation of this pattern for calculating availability. This example assumes there are events for UP and DOWN that are handled by two separate event handlers in the business logic. The availability is defined as the percent of time during which the system is up from the total time in the timeslot. The status of the system is assumed to be the status of the last event received (UP or DOWN).

Here is the code of the implementation (the code of the framework is not changed):

' Status variable
Dim g_Status
' Counters.
Dim g_UpTime, g_TotalTime

Sub InitializeStatusVariables ()
     G_Status = “UP”
End Sub

Sub InitializeCounters ()
     g_UpTime = 0
     g_TotalTime = 0
End Sub

Function CalculateResult ()
     If g_TotalTime = 0 Then
          CalculateResult = Null
     Else
          CalculateResult = g_UpTime/g_TotalTime*100
     End If
End Function

Sub UpdateStatus(method, eventDetails)
     If method = “OnUP” Then
          G_Status = “UP”
     Else
          G_Status = “DOWN”
     End If
End Sub

Sub UpdateCounters(diff)
     If Context.IsWithinTimeslot Then
          G_TotalTime = g_TotalTime + diff
          If g_Status = “UP” Then
               G_UpTime = g_UpTime + diff
          End If
     End If
End Sub

Sub OnUp(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnUp”,eventDetails)
End Sub

Sub OnDown(eventDetails)
     HandleEvent (eventDetails.Time)
     UpdateStatus(“OnDown”,eventDetails)
End Sub

There are several variations on this pattern. One of the most common variations is when a separate time counter should be maintained for different entities. For example, when we measure solution time, a separate counter should be maintained for each open ticket. In this case when handling an event which is relevant only to one ticket it is more efficient to update only the counter of that ticket. When a common event is handled (such as OnPeriodEnd or OnTimeslotEnter) the counters of all tickets should be updated.

Note: This variation of the pattern requires maintaining a separate copy of the g_PrevEventTimestamp global variable for each ticket.

Some good examples of the usage of this pattern can be seen in our predefined content. Keep in mind though that this pattern is used a bit differently in the predefined content and the separation between the framework and the implementation is not so obvious there.