To convert what you have learned about the requirements for your business logic component into a detailed design, you need to be familiar with some general design principles for server components.
There are three important things you learned from your estimation of the workload:
You need to design a highly scalable solution. By definition this means that you use the same resources in a number of different environments—thus improving your efficiency.
You need to prevent multiple users updating the same account record at the same time.
The resources you have to use efficiently are:
Shared resources:
Single-user resources, which can be used by only one user at a time, such as file records being updated, scratchpad areas, and so on
Let’s examine these individually and develop some guidelines for designing and programming CICS applications from them. Remember, there’s bound to be conflict from time to time when trying to conserve one resource at the cost of another. The appropriate compromises vary from one program to the next.
You can use different techniques for locking (allowing single use of) specific types of resources. These are described in the following paragraphs:
In an online system, storage needs constantly change. Most exist only for the duration of a transaction, and so, in assessing storage needs you have to consider not only how much is used, but for how long. The trade-off between space and time requires the following storage consumers to be considered:
The internal CICS data areas associated with any transaction being processed. You should therefore minimize the duration of the transaction.
The program or programs being executed to accomplish the transaction. Don’t worry about the size of programs, because they are shared by all transactions and there is only one copy in the system at a time. It is much more important to structure the code well for ease of maintenance.
Keep the size of working storage areas as small as possible; for example, WORKING STORAGE in COBOL or large stack arrays in Java.
In general, you need to conserve processor time. Calls for CICS or operating system services take much longer, relatively speaking, than straight application code. Avoiding unnecessary commands reduces processor time much more than fine tuning your code.
It is undesirable to do long calculations in an online program, because you may be sharing the process with thousands of other users. If such calculations are necessary, you need to use an application design approach that is beyond the scope of this book.
Disk space and transmission capacity is optimized in an online system by minimizing input/output by, for example, creating additional indexes instead of performing sequential searches.
It is undesirable to do complex database queries in an online program. If such queries are needed, special design approaches are needed; these are beyond the scope of this book.
There are two main principles governing community bandwidth:
Send only the necessary data.
Send in a single message as much as is likely to be needed.
Resources that can only be used sequentially by one user at a time are something that we do not want multiple transactions accessing concurrently. A file record destined for update is a perfect example of this type of resource. CICS provides a user-locking mechanism (called EXEC CICS ENQUEUE/DEQUEUE) for transactions to prevent concurrent use and CICS uses the mechanism internally to obtain a lock prior to using the resource and free it automatically at the end of the task or at a user syncpoint. While one transaction owns the lock, other transactions that wish to use this resource have to wait. Therefore, minimize the duration of transactions that require exclusive use of resources because everyone else who wants to update that resource is held in a queue until the lock is released.
CICS applications do not typically need to obtain locks explicitly, because CICS file and database operations use locks automatically to implement the Isolation property of ACID transactions. CICS locks can be held only for the duration of a single task. If a lock needs to be held for longer than this, other approaches are needed. This is discussed in more detail in the next section.
When a user works on an account, she will bring the account details onto the screen, work with the information, optionally save changes to the database and cease working with the customer account.
There is a potential problem if two users try to do this on the same account at the same time. Both users could be making changes based on the same old copy of the record. Changes made by the first user to complete his work go into the file, but then changes from the second user will overwrite the first user’s updates. You must ensure that, when one user starts to work with a customer account record, subsequent users are prevented from doing so. As discussed in Transactions in Chapter 2, ACID transactions should not wait for user input. Thus, you can’t use the Isolation property of ACID transactions to prevent multiple users working on the same account.
Our solution is to introduce an application-level locking scheme that locks out other users between the initial display of the account details on the screen and ceasing to work with the customer record. Note that you can still exploit the capabilities of ACID transactions during the step that saves the changes.
Use of an application locking scheme is better than using ACID transactions from the perspective of users, too. If you used a long-running ACID transaction to protect against double updating, the second user would be locked out waiting for the first user to complete his work. With an application lock, you can tell the second user to try again later, and you can even pass information as to who owns the lock so that the second user (who may have the actual customer waiting on the telephone) can discuss the problem with the first. Let’s look at one way this locking mechanism could work.
Suppose that as soon as a user asked to update an account number, you make a note in a scratchpad area. (CICS provides scratchpad facilities for keeping track of things between transactions.) You can leave the number there until the update is entirely completed and then erase it. In our example, this means that if you write a scratchpad record in a subsequent transaction, it erases the earlier one. Before starting any update request, you can check to see if the account number is in use. If it is, you can tell the user this and ask him or her to resubmit the request later. Furthermore, you can let the user display the record even if it is in use.
This isn’t quite all, however. Because CICS ensures that transactions are either executed completely or not at all, you have to make sure that all your protected resources get updated in what CICS regards as a single transaction to ensure the consistency of your data. In the conversational case, (see Table 10-1) the program performs all the steps in one transaction. On the other hand locks are held over two terminal output/input operations so the scalability of the system might be adversely affected by a user going to lunch in the middle of a transaction. In the pseudo-conversational case (see Table 4-2), the files are all updated in the third transaction (this is the preferred solution), but the scratchpad is updated in two different transactions (not so good). The real benefit of this design is that no locks are held while waiting for user’s terminal input, so step 2 has disappeared between 1 and 3 and step 6 between 5 and 7.
Table 4-2. Example of Pseudo-Conversational Transactions
If the second transaction is completed successfully, but something happens to the third, the scratchpad record is written but not erased. Our data files would be consistent, which is the main concern, but you’d be unable to update the record involved until you could somehow reset the scratchpad.
You can get around this by designing a slightly more sophisticated scratchpad mechanism, for instance, by putting a limit on the time for which a transaction can own an account number. Then an error in the third transaction or thoughtless behavior by a user (going to lunch in the middle of a modification) will not cause an account record to become unusable for more than a short period of time. But all this involves extra coding and complications—is it worth it?
Absolutely. You can program your own mechanism for avoiding concurrent updates. Double updating is one of those problems you can tackle in a variety of ways. We chose a scratchpad. A drawback of a scratchpad, however, is that all future (and, as yet, unknown) transactions that update the account file have to refer to this scratchpad. However you can control this by the design you have chosen to separate the presentation logic from the business logic. Since all updating automatically manages the locking, this should be less of a problem than it may appear at first.
As developers of a reusable component, you must handle errors well. Hence, your component does some or all of the following actions for each error, which correspond to the following table:
Ensures that the system continues without damage by cleaning up:
ACID transactions
Conversations
Memory
Locks
Ensures that the end user, or the calling program or component, knows what’s happened by means of an on-screen message or return code, or occasionally an abnormal end (abend).
Ensures that CICS administrators know what’s going on.
Ensures that you, the developer, can find out why the error happened by writing debugging information to a log file or providing a dump.
The errors that can occur in a CICS transaction can be divided into five categories. For each category, you need to consider which of the error-handling actions are required.