Using the Locking Facility [ TurboIMAGE/XL Database Management System Reference Manual ] MPE/iX 5.0 Documentation
TurboIMAGE/XL Database Management System Reference Manual
Using the Locking Facility
The DBLOCK procedure applies a logical lock to a database or one or more
data sets or data entries. The DBUNLOCK procedure releases these locks.
Locking can be viewed as a means of communication and control to be used
by mutually cooperating users. The locking facility provides a method
for protecting the logical integrity of the data shared in a database.
With the DBLOCK procedure, application programs can isolate temporarily a
subsection of the database in order to perform a transaction against the
isolated data. Locking is not required to protect the structure of the
database. TurboIMAGE/XL has internal mechanisms that do this.
If a program opens the database in access mode 1 and locks a part of the
database, it can perform the transaction with the certain knowledge that
no other user will modify the data until the application program issues a
DBUNLOCK call. This is because TurboIMAGE/XL does not allow changes in
access mode 1 unless a lock covers the data to be changed. If one
process has the database opened in access mode 1, TurboIMAGE/XL requires
that all other processes that modify the database must also operate in
access mode 1.
The DBLOCK procedure operates in one of six modes. Modes 1 and 2 can be
used for locking the database and modes 3 and 4 for locking a data set.
In modes 5 and 6, you describe the database entity or entities to be
locked using lock descriptors.
At the data entry level, locking is performed on the basis of data item
values. For example, suppose a customer requests a change in an order
the customer has placed. The data entries for the customer's account
that are in the SALES data set could be locked while the order is changed
and other database activity can continue concurrently.
Lock Descriptors
A lock descriptor is used to specify a group of data entries that are to
be locked. It consists of a data set name or number, a relational
operator, and an associated value. For purposes of this discussion, the
notation dset : ditem relop value is used. For example, the lock
descriptor SALES:ACCOUNT = 89393899 requests locking of all the data
entries in the SALES data set with an ACCOUNT data item equal to
89393899. Note that the result of specifying a single lock descriptor
can be that none, one, or many entries are locked depending on how many
entries qualify.
The following relational operators can be used:
* less than or equal (<=)
* greater than or equal (>=)
* equal (= _ or _ =), where _ indicates a space character
The value must be specified exactly as it is stored in the database. A
lock will succeed even if no data item with the specified value exists in
the data set; no check is made to determine the existence of a particular
data item value. This allows you to use techniques such as issuing a
lock to cover a data entry before you actually add it to a data set.
With the exception of compound items, any data item can be used in a lock
descriptor; that is, the lock item need not be a search item.
TurboIMAGE/XL does not require that you have read or write access to a
data set or data item in order to specify it in a lock request.
A process can specify any number of lock descriptors with a single DBLOCK
call. For example, the following lock descriptors can be specified in
one DBLOCK call:
CUSTOMER: ACCOUNT = 89393899
SALES: ACCOUNT = 89393899
SUP-MASTER: STATE = AZ
INVENTORY: ONHANDQTY <= 100
INVENTORY: ONHANDQTY >= 1500
NOTE Multiple calls to DBLOCK without intervening calls to DBUNLOCK are
not allowed unless the program has Multiple RIN (MR) capability.
Refer to "Issuing Multiple Calls to DBLOCK" later in this chapter.
How Locking Works
The internal implementation of locking does not involve reading or
writing to the database element to be locked. TurboIMAGE/XL keeps a
table of everything that is locked by all processes that have the
database opened. One table is associated with each database. This table
serves as a global list of lock descriptors. In locking mode 5 or 6, a
database lock is specified with the descriptor @:@ and a data set lock
with dset:@. If you call DBLOCK in locking mode 1, 2, 3, or 4,
TurboIMAGE/XL sets up the appropriate lock descriptor and puts it in the
lock descriptor table. Figure 4-3 illustrates the contents of this
list in a situation where one process has locked all SALES data entries
with ACCOUNT equal to 12121212 or equal to 33334444. Another process has
locked all INVENTORY data entries with STOCK# equal to 6650D22S. A third
process has locked the whole SUP-MASTER data set. The figure illustrates
what the table represents, not the actual internal format.
When a lock request is made, TurboIMAGE/XL compares the newly specified
lock descriptors with those that are currently in the list. If a
conflict exists, TurboIMAGE/XL notifies the calling process that the
entity cannot be locked or, if the process has requested unconditional
locking, it is placed in a waiting state until the entity can be locked.
If there are no conflicts, TurboIMAGE/XL adds the new lock descriptors to
the list.
Figure 4-3. Lock Descriptor List
Conditional and Unconditional Locking
You can request conditional or unconditional locking. If you request
unconditional locking, TurboIMAGE/XL returns control to your calling
program only after the specified entity has been locked. If you request
conditional locking, TurboIMAGE/XL returns immediately. In this case,
the condition code must be examined to determine whether or not the
requested locks have been applied. If multiple lock descriptors are
specified, the status area indicates the numbers that have been applied.
The calling program should call DBUNLOCK if only a subset of the
requested locks succeeded.
Access Modes and Locking
It is anticipated that access mode 1 will typically be used by
applications implementing a locking scheme. In this mode, TurboIMAGE/XL
enforces the following rules:
* To modify (DBPUT, DBDELETE, or DBUPDATE) a data entry, you must
first issue a successful lock covering the affected data entry.
It can be a data entry, data set, or database lock.
* To add to or delete from (DBPUT or DBDELETE) a manual master data
set, you must first successfully lock the data set or database.
To update (DBUPDATE) a master data set, data entry level locks are
sufficient.
If your application opens the database in access mode 2, it is
recommended that you use locking to coordinate updates with other users.
TurboIMAGE/XL does not prevent any process from reading data even though
another process holds a lock on it. If you want to ensure that no
modifications are in progress while you are reading from the database,
you should place an appropriate lock on the data before starting.
Therefore, you may want to use locking in access modes 2, 4, 5, and 6 to
coordinate the reading and modifying sequences and ensure that they do
not occur concurrently.
Because access mode 3 and 7 users have exclusive control of the database
and access mode 8 users allow concurrent reading only, locking need not
be used in these modes.
Automatic Masters
When adding or deleting entries from a detail data set, you need not have
locks covering the implicit additions or deletions that occur in any
associated automatic masters.
Locking Levels
Locking can be viewed as operating on three levels: the whole database,
whole data sets, or data entries. TurboIMAGE/XL allows mixed levels of
locking. For example, one user could be locking data entries and another
locking the data set. In this situation, a request to lock the data set
cannot succeed until all the currently locked data entries have been
released. Subsequent requests to lock data entries, those that are made
while the data set lock is pending, are placed in a queue behind the data
set lock.
This principle is followed for database locks also. If data set or data
entry locks are in effect at the time a database lock is requested, the
database lock must wait until they are released and all subsequent
locking requests must wait behind the pending database lock.
In either case, if the request is for a conditional lock, an exceptional
condition is generated. (Refer to the "Locking Mode Options" table in
chapter 5.)
Deciding on a Locking Strategy
It is important, especially for on-line interactive applications, to
establish a locking strategy at application design time. In general,
locking is related to the transaction, the basic unit of work performed
against a database. TurboIMAGE/XL transactions are either single or
logical, and logical transactions can be static, multiple database, or
dynamic. Refer to "User Logging and Logical Transactions" later in this
chapter for more details and to chapter 5 for additional information.
Typically a transaction consists of several calls to TurboIMAGE/XL
intrinsics to locate and modify data. For example, a transaction to add
a new order with three line items could require several reads to locate
customer information and several DBPUT calls to add the order detail
records.
One characteristic of a transaction is that the data in the database is
consistent both before and after the transaction, but not while it is in
progress. For example, a user reading the detail data set being modified
by the above order transaction may only see some of the line items and
may get no indication that the transaction is incomplete. This type of
problem is referred to as logical inconsistency of data and can be
prevented by using the locking facilities.
The general principle that should be applied for any transaction in a
shared-access environment is: At the start of any transaction, establish
locks that cover all data entries that you intend to modify (with DBPUT,
DBDELETE, or DBUPDATE) and/or all data entries which must not be changed
by other processes during the transaction.
Choosing a Locking Level
Because TurboIMAGE/XL needs more information to lock data entries than to
lock the whole database, program complexity tends to increase as locks
are employed at lower and lower levels. Locking the whole database or a
single data set is the simplest operation, followed in increasing order
of complexity by locking multiple data sets and locking data entries. At
system design time, a compromise must be made between the benefits of
low-level locking and the extra programming effort required.
Data entry locking should give the best concurrency; however, there are
situations in which the extra programming effort for data entry locking
is not worthwhile. Concurrency is least optimum at the higher level of
the lock. Concurrency and programming effort should be considered; some
other considerations that could affect your choice of locking level are
discussed below.
Locking at the Same Level.
All programs concurrently accessing a database should lock at the same
level most of the time. For example, one process locking a data set will
hold up all other processes that are attempting to lock entries in that
set. Therefore, the attempt by the process locking at the data entry
level to allow other processes to share the database is nullified by the
process locking at the data set level and the effect is as if all
processes were locking at the data set level. The rule of locking at the
same level can be violated for infrequent operations such as exception
handling or rare transactions.
Length of Transactions.
Generally, the longer the lock is to be held, the lower the level it
should be. In other words, if you are performing lengthy transactions,
you should probably lock at the entry level. For shorter transactions,
you can use locks at either the database or data set level with
satisfactory results.
An extreme case of a long transaction is one in which user dialog takes
place while a lock is held. For example, a program can read some data
entries, interact with a terminal operator, and modify some or all of the
entries. A lock to cover this transaction can last several minutes which
is an unacceptable amount of time to stop all database or data set
activity. In this situation, data entry level locking should be used.
Because the length of different transactions varies, the longest
transaction (that is also frequently used) should guide the choice of
locking level.
Locking During User Dialog.
In the situation described above, where a lock is held during interactive
dialog with a terminal operator, the terminal time-out feature of MPE/iX
can be used to avoid having the locked entity inaccessible when the
terminal operator is interrupted in the middle of the dialog. The
time-out feature can be used to cause the terminal read to terminate
automatically if no response is received within a certain time period.
Refer to the discussion of "FCONTROL" in the MPE/iX Intrinsics Manual.
Strong Locking and Dynamic Transactions.
Dynamic transactions, which are described later in this chapter, are only
allowed with a database access mode that enforces locking, because strong
locking is required for this type of transaction. TurboIMAGE/XL requires
that dynamic transactions be independent of all other types of
transactions. This is guaranteed when the database access mode is 3 or
4, because the mode guarantees exclusive modify access.
When a database is opened in access mode 1, the programmer must ensure
that strong locks are in place. In other words, any call to DBUNLOCK
must occur after the call to DBXEND, or the dynamic transaction is
aborted.
NOTE A call to DBXUNDO must be processed if an error occurs or if the
transaction needs to be rolled back for any other reason, because
TurboIMAGE/XL will not go on to the next transaction in the event
of a transaction abort unless an intervening call to DBXUNDO
occurs. Furthermore, if DBXUNDO was used to roll back a
transaction, the program logic should ensure that the subsequent
call to DBXEND is not processed.
Choosing an Item for Locking
An important convention to follow in designing a locking scheme is that
all programs sharing the database concurrently use the same data item to
lock data entries in a particular data set. At any one time,
TurboIMAGE/XL allows no more than one data item per data set to be used
for locking purposes. However, several values of the data item can be
locked at the same time. For example, if one process has successfully
locked SALES:ACCOUNT = 54321000, another process could lock SALES:ACCOUNT
= 11111111. If a request is made to unconditionally lock SALES:STOCK# =
8888X22R, the requesting process will be made to wait until all entries
locked by ACCOUNT number are unlocked. Furthermore, any new requests for
locking other SALES:ACCOUNT values will wait until SALES:STOCK# =
8888X22R is successfully locked and unlocked again.
With this in mind, it is apparent that it is more efficient if all
processes locking data entries in the SALES data set use the same data
item because it is much less likely that one process will have to wait
until another process finishes using the data. Therefore, at system
design time, decide which item will be used in each data set for lock
specification purposes. It can be useful to add comments in the schema
indicating which item is the locking item for each set. If a chain is
used heavily for chained reads, its search item is a prime candidate for
a lock item.
Examples of Locking
The examples in this section show the order in which TurboIMAGE/XL
intrinsics can be called when locking is used. The ORDERS database is
used in the examples. (Refer to the ORDERS database schema in chapter
3.) For descriptions of the procedures used in these examples, refer to
chapter 5.
Table 4-3 contains guidelines that can be helpful in designing
locking schemes for shared-access environments which include users who
might modify the database. Although data entry level locks are
recommended in this table and illustrated in the following examples, data
set or database locks could be more appropriate for similar tasks
depending upon other application requirements.
Table 4-3. Locking in Shared-Access Environments
--------------------------------------------------------------------------------------------
| | |
| Action | Recommended Locks |
| | |
--------------------------------------------------------------------------------------------
| | |
| Chained DBGET calls | Lock all data entries in the chain. This usually |
| | requires one lock descriptor. |
| | |
| Serial DBGET calls | Lock the data set. |
| | |
| Update a data entry (DBUPDATE) | Lock the data entry before calling DBGET to read |
| | the data entry. Unlock after the update. |
| | |
| Directed reads (DBGET calls) | These are not recommended in a shared environment. |
| | Lock the data set before determining which data |
| | entry is needed. |
| | |
| Add a data entry to a detail data | Any lock which covers this data entry, but |
| set (DBPUT) | preferably uses the data item that was decided on |
| | as the "lock item" for the data set. |
| | |
| Add to or delete from a master | Lock the data set or database. This is mandatory |
| data set (DBPUT and DBDELETE) | if the database is open in access mode 1. |
| | |
--------------------------------------------------------------------------------------------
Add a New Customer.
1. DBLOCK the CUSTOMER data set or the whole database.
2. DBPUT new data entry in CUSTOMER data set.
3. DBUNLOCK.
Note that TurboIMAGE/XL requires a data set or database lock to cover the
addition of an entry to a master data set.
Update Inventory Information.
1. DBLOCK INVENTORY: STOCK# = 6650D22S. (Alternatively, the INVENTORY
set or the whole database can be locked.)
2. DBFIND and DBGET the data entry that is locked in step 1.
3. Compute new UNIT-COST = UNIT-COST + .12 * UNIT-COST.
4. DBUPDATE the data entry that is locked.
5. DBUNLOCK.
Insert a New Product with a New Supplier.
1. DBLOCK the PRODUCT master data set, the SUP-MASTER data set, and
the data item STOCK# = 4444A33B in the INVENTORY detail data set.
(This can be done in one DBLOCK mode 5 call.)
2. DBBEGIN.
3. DBPUT a new data entry in PRODUCT master data set. (For example:
4444A33B CALIPER).
4. DBPUT a new data entry in SUP-MASTER data set.
5. DBPUT a new data entry in INVENTORY data set for STOCK# =
4444A33B.
6. DBEND.
7. DBUNLOCK.
The locking in the above example was done around the entire transaction
to maintain data consistency during the multistep transaction.
Interactively Modify a Customer Account Order.
1. DBLOCK SALES: ACCOUNT = 89393899.
2. DBFIND the CUSTOMER master data set entry with ACCOUNT = 89393899
in order to prepare to read the chain of SALES data entries with
the same ACCOUNT value.
3. DBGET each entry in the chain and display it to the user until the
correct order is located.
4. DBUPDATE the contents of the data entry according to the user's
request.
5. DBUNLOCK.
In the last example, all data entries for ACCOUNT 89393899 in the SALES
data set are locked. Note that these locks are held while a dialog takes
place with the terminal operator; therefore, the lock could be held for
several minutes. For this type of transaction, it may be best to first
perform a conditional lock to determine if the records are accessible.
For example, when a mode 6 DBLOCK is called with lock descriptor SALES:
ACCOUNT = 89393899 and the lock does not succeed, the following message
is displayed:
RECORDS BEING MODIFIED. WANT TO WAIT?
If the response is NO, then proceed with other processing. If the answer
is YES, call DBLOCK again with mode 5.
Issuing Multiple Calls to DBLOCK
In order to guarantee that two processes cannot deadlock, TurboIMAGE/XL
does not allow two DBLOCK calls to be made without a DBUNLOCK between the
DBLOCK calls. Two exceptions to this rule are stated here:
* A redundant call can be made to lock the whole database with
DBLOCK mode 1 or 2 provided the call relates to the same access
path. The redundant call will have no effect. (This is allowed
in order to maintain compatibility with earlier versions of
IMAGE.)
* More than one DBLOCK call can be made if the program from which
multiple DBLOCK calls are issued has the MPE/iX Multiple RIN (MR)
capability.
The DBLOCK procedure is similar to the MPE/iX FLOCK procedure in that
DBLOCK can put a process into a waiting state and thus can cause a
deadlock to occur. For example, a deadlock can occur if process A is
waiting for an MPE/iX file to be freed by process B, and process B is
waiting for a database entity to be unlocked by process A. Therefore,
issuing a DBLOCK in conjunction with a lock applied by an MPE/iX
intrinsic, such as FLOCK, or by the COBOLLOCK procedure requires MR
capability. The use of MR capability is not recommended unless
absolutely necessary.
Users whose programs have MR capability and issue multiple DBLOCK calls
are responsible for deadlock prevention. This type of locking must be
done very carefully. Recovery from a deadlock requires a restart of the
operating system. No matter how many descriptors are listed in a single
DBLOCK call, TurboIMAGE/XL guarantees that deadlocks will never occur
provided that no executing program that accesses the database has MR
capability. Programs that execute successfully using TurboIMAGE/XL locks
in a single process environment will not execute in a process-handling
environment without MR (Multiple RIN) capability. (Refer to appendix D
for more information on the MR capability.)
Releasing Locks
The locks held by a process for a particular access path of a database
are relinquished when the process calls DBUNLOCK; they are automatically
relinquished when the process closes the database, terminates, aborts, or
is aborted by an operator. Failure of a program to release locks will
result in other programs waiting indefinitely for any conflicting locks.
These programs, while in a waiting state, cannot be aborted by the
operating system. An attempt to abort such a waiting process will result
in the abort taking effect as soon as the process obtains the lock for
which it was waiting.
NOTE Any program that executes a DBGET in mode 5 or 6 should lock the
chain in the detail data set. This prevents the execution of any
DBPUTs or DBDELETEs to the detail data set from modifying the
current chain, thereby preventing a status 18 (broken chain) error.
MPE/iX 5.0 Documentation