|
Functional menu-level blocking An often-ignored aspect of multi-user programming is the functional clash between two business operations. For example, when a month-end processing module is being executed, it should not be so that the same month-end processing module is started on another terminal. This is a simple precaution but has a tremendous business impact.
Another example involves a situation where data upload is in progress. Although a bulk data upload can be undertaken concurrently, while other users are using the database, it is important to ensure that no user generates any type of MIS report that may require all the uploaded data. In such cases the report may be generated without an error, but the contents will be incorrect.
Another problem is the clash between locks. If a lengthy program is running, and locks entire tables for a substantial amount of time, other transactions based upon this table will wait for the lock to be released. The user, at his end, feels that the program has hung or crashed, but in actuality the program cannot proceed until the locks are released. This situation could have been avoided simply by preventing the user from beginning the transaction till such a lengthy operation was over. The solution--functional blocking.
Here is what is needed to implement functional blocking. We will implement all the following requirements using innovative data design and coding. We will also need to design our application menus to utilise this feature.
We need a method of specifying which modules or processes clash with other processes. A given process may clash with itself, i.e. it can run only from one machine at a time. We need a mechanism to create a runtime log of the modules currently running. On completion of the execution, the entry for that module will be removed from the log accordingly. We need to check for possible clashing conditions before executing any module or functionality. All the above must be user-definable.
Creating a table to capture the exclusion information
Create a table that lists all the programs and all clashing programs.
Prgmask( Baseprg C(10), Clashprg C(10) , Checkid C(10) )
Baseprg is the module name of the base module. Clashprg is the name of the module that is not allowed to run with Baseprg.
Multiple Baseprg entries are needed if one program clashes with multiple programs.
Here is a sample table:
Baseprg Clashprg Checkid InvoiceModify InvoiceDelete 1 InvoiceModify InvoiceCancel 1 EndOfMonth * 0 What is this Checkid parameter?
In the above table, we do not want an invoice that is being modified to be deleted by another user. However this is specific to the invoice. Note that we are talking about a particular invoice here. Invoice X can be deleted if Invoice Y is being modified. Therefore, we need an additional parameter to indicate that we do not want to permit the clash of modules if these are referring to the same item (same invoice id). Therefore, the Checkid flag is required.
If the Checkid flag is zero then both the modules will not be permitted to run simultaneously. If the Checkid is 1 then the modules will be blocked only if they are working on the same id.
The meaning of *
If a base program cannot start if any other option is being used by any user on any terminal, you can use an asterisk (*) notation.
When an asterisk is encountered in the Clashprg field, it has two meanings:
The program listed in Baseprg will not start if any other program is active. No other program will be allowed to run if a program with an asterisk in the Clashprg field is currently executing. The runtime execution log
The above table is a static table, created while designing your application. However, for detecting and preventing clash of modules, we need to know which user is running which module at run-time. This requires another table.
Field Type Comment Username C(30) Name of the user using the module. DateTime Datetime When did the user initiate this module. Moduleid C(10) Name of the module being executed. Itemid C(10) ID of the item being used by the module. For example, in case of an invoice modify program, this would be the invoice id. Online Integer A flag used for runtime activity detection (explained below). A sample table in a running multi-user application could be as follows:
Username DateTime Moduleid Itemid Online Admin 12 Jun, 12:03PM Invoicemodify 1113 1 User1 12 Jun, 12:23PM AddCustomer 1 User1 12 Jun, 12:34PM InvoiceDelete 3321 1
Adding an entry to the runtime execution log
This logic will work only when each module adds an entry to the execution log while it executes. This is done either by manually adding a record to this log or by routing all the program execution using a menu launcher program.
In any case the following logic will be used:
=Addtolog(Modulename, Username, Itemid)
function Addtolog
parameters Modulename, Username, Itemid
Get the Modulename. Check if the module is clashing with programs that are already running as per the execution log. If another running program clashes with this module, show a message to the user and abandon the execution of this module. If no clash is found, execute this program. When the program finishes execution, remove this entry. Return control to the calling program. Checking for clash
The logic for checking for a clash is as follows:
function Checkforclash
parameter Moduleid, Itemid
Locate the Moduleid in the Prgmask table. Check in the Baseprg as well as the Clashprg fields as a given Moduleid may appear in either of these. For each located record, make an array containing the clashing program names. For each item in the array, check the RuntimeLog table to see if this module is already running. If it is running Check if the Itemid needs to be checked. If yes, and the Itemid is found Return -1 (do not execute this program) Else Return 1 (allow execution). If Itemid does not need to be checked Return -1 if the program is running Else return 1. If the program is mapped against *, then just check if there is even a single entry in the RuntimeLog table. If yes, return -1 Else return 1. The Online field
All the above functionality is very useful, but one problem still remains.
Suppose you start the backup routine and then add a row to the table. Now suppose the machine that is running the Backup module either hangs or is rebooted. Now the entry will remain forever and no other module will be allowed to run because our system thinks the Backup module is still running. This is a major problem.
What is needed then, is a method which tells our clash-checking algorithm whether a module which is running as per the RuntimeLog table entry is actually running or not.
The solution to this problem is the Online field. This is a simple integer field. To start with, it has no value in it. We need to implement a simple timer based routine in our application which does the following:
1. The timer will fire at a convenient interval (5 to 10 seconds).
2. It will locate its own row in the RuntimeLog table and increment the value of Online by 1. When the value reaches some number, say 1000, it will be rolled back to 0.
How do we use this value?
The clash detection program will use the value of the Online field to detect whether a program which is supposed to be running is actually running or hung.
When the clash detection program detects that a clashing module is running, it just has to wait for a little while longer than the update interval. Now it can easily check the value of the Online field before and after the wait.
If the value has not changed, the clashing program is actually non-functional.
Now the entry presumed to be invalid and can be removed. The current program can then be allowed to run without a problem.
Username DateTime Moduleid Itemid Online Admin 12 Jun, 12:03PM Backup 1 Functional logging
Whenever a program is actually run by the Launcher (using Do &Progname), the current date, time, username, environment information can be logged to a separate table. This will provide a functional audit trail indicating who used which option and when. When the user finishes using the option, Launcher can also insert the time of completion for each entry. This information can be used to analyse the actual usage of the application and understand the peak usage, idling statistics, and so on.
|