3.1 Concurrent Scenarios
When two processes attempt to update the same
record at the same time, locking, blocking, or potential failure can
occur, depending on the concurrency model. The following scenario
illustrates the behavior differences when two processes using SQL Server
2005 attempt to update two fields on the same records using pessimistic
and optimistic concurrency.
Figure 4 illustrates pessimistic concurrency, in which Process 1 selects the CustTable record with a forupdate keyword and holds an update lock on the records. When Process 2 attempts to read the same record, also with a forupdate
keyword, it is blocked behind the lock acquired by Process 1. Process 1
continues to set the new customer group and updates the record, and it
converts the update lock into an exclusive lock. But Process 1 must
commit before the locks can be released, and Process 2 can continue by
acquiring the update lock and reading the record. Process 2 can then set
the new credit maximum, update the record, and commit the transaction.
Figure 5 illustrates one possible outcome of the same two processes executing, using optimistic concurrency. Process 1 selects the same CustTable record with the forupdate
keyword, but no locks are acquired or held for the remainder of the
transaction. Process 2 can therefore select the same record in the same
way, and both processes hold a record with a RecVersion
value of 789. Process 1 again sets the customer group field to a new
value, updates the record, and acquires an exclusive lock. At the same
time, the selected RecVersion is compared to the value in the database to ensure that no other processes have updated the same record, and then the RecVersion
field is assigned a new value of 543. Process 2 takes over and assigns a
new credit maximum value and executes an update. As the database first
attempts to acquire an exclusive lock on the record, Process 2 gets
blocked behind the lock of Process 1 on the same record until Process 1
commits and releases its locks. Process 2 can then acquire the lock, but
because the selected RecVersion of 789 is not equal to the value of 543 in the database, the update fails and an update conflict is thrown.
If, however, Process 1 updates its changes
before Process 2 selects the record, the two processes complete
successfully. This scenario is shown in Figure 6, in which Process 2 reads the updated version where the RecVersion value is 543. Although Process 2 is blocked behind Process 1 when it tries to update the record, the RecVersion
check does not fail when Process 1 commits and releases its locks
because Process 2 has read the uncommitted version from Process 1.
The examples shown in Figures 14-5 and 14-6
illustrate how the application runtime behaves when the same record is
updated by two processes. In the following section, we describe how the
runtime behaves when the same record is updated more than once within
the same process.
3.2 Disconnected Updates of the Same Record
Consider a scenario in which two separate
pieces of application logic in the same process have copied the same
record into two separate buffers, both with the intent of updating
different fields on each buffer. Both records would have been selected
with the forupdate keyword added to the select statement. In a pessimistic concurrency scenario, both select statements would request an update lock, but because the select
statements are both executed with the same database process, they
wouldn’t lock or block each other. In an optimistic concurrency
scenario, both select statements would retrieve the same value for the RecVersion field but wouldn’t, of course, acquire any locks.
When the two pieces of application logic
consequently change and update their records, the Dynamics AX
application runtime doesn’t encounter a problem when using pessimistic
concurrency because each update
statement updates its changed fields by using the primary key to locate
the record in the database. When the application logic uses optimistic
concurrency, however, the first update statement determines whether the selected RecVersion value is equal to the value in the database and also updates the RecVersion to a new value. But when the second update statement executes, it ought to fail because the selected RecVersion
value no longer matches the value in the database. Fortunately, the
Dynamics AX application runtime manages this situation. When the update
statement is executed, the application runtime locates all other
buffers holding the same record that have been retrieved with the forupdate keyword and changes the RecVersion value on these buffers to the value in the database. The second update, therefore, doesn’t fail.
The following X++ code illustrates the behavior
of the Dynamics AX application runtime when the same record is copied
into three different buffers. Two of the select statements also include the forupdate keyword and copy the record into the custTableSelectedForUpdate and custTableUpdated buffers. When the creditMax field on the custTableUpdated buffer changes and is later updated in the database, the RecVersion field in the custTableUpdated buffer changes to the new value in the database—but now the RecVersion field in the custTableSelectedForUpdate buffer also changes to the same value. The RecVersion field in the custTableSelected buffer doesn’t change, however, because the record was retrieved without the forupdate keyword. The X++ code is shown here.
static void RecVersionChange(Args _args)
{
CustTable custTableSelected;
CustTable custTableSelectedForUpdate;
CustTable custTableUpdated;
;
ttsbegin;
select custTableSelected
where custTableSelected.AccountNum == '1101';
select forupdate custTableSelectedForUpdate
where custTableSelectedForUpdate.AccountNum == '1101';
select forupdate custTableUpdated
where custTableUpdated.AccountNum == '1101';
// At this point, instances of RecVersion in all three buffers are equal.
custTableUpdated.CreditMax = custTableUpdated.CreditMax;
custTableUpdated.update();
// At this point, RecVersion is changed in the custTableUpdated
// and custTableSelectedForUpdate buffers. CustTableSelected still
// has its original value in RecVersion.
ttscommit;
}
|
Caution
When
multiple processes want to simultaneously update the same record, the
application runtime prevents the “last writer wins” scenario by
acquiring update locks when using pessimistic concurrency and by
performing the RecVersion check when
using optimistic concurrency. However, nothing in the database or the
application runtime prevents the “last writer wins” scenario if
disconnected application logic within the same scenario and database
process changes the same field by using two different buffers. |