3.3 Using Relative Updates to Prevent Update Conflicts
Dynamics AX has always included built-in
support for relative updates. But it is in combination with optimistic
concurrency that this support is truly useful. Relative updates can be
applied only to fields of type integer and real. You apply them by changing the FieldUpdate property from the default value of Absolute to Relative, as shown in Figure 7.
The difference between an absolute update and a relative update is that an absolute update submits FIELD = <new value> in the UPDATE statement sent to the database, and a relative update submits FIELD = FIELD + <delta value>. The delta value is the difference between the originally selected value and the newly applied value. So if you change the SalesQty field on the SalesLine table from 2 to 5, the update statement contains either SALESQTY = 5 or SALESQTY = SALESQTY + 3, depending on whether you set the FieldUpdate property on the SalesQty field to Absolute or Relative.
When you use relative updates, neither the
previous value in the database nor the value it becomes is important to
the updating of the application logic. The only important thing is that
the difference is added to the value in the database. If all fields
being updated in an update statement use relative updates and the record is selected using optimistic concurrency, the RecVersion check isn’t added to the update statement. The previous value isn’t added because it isn’t important, regardless of whether any other process changes the value between the select and the update.
Using relative updates on tables combined with
pessimistic concurrency has no benefit because an update lock is
acquired when the application logic selects the record, so no other
processes can update the same record between the select and the update.
Warning
You
shouldn’t use relative updates for fields on which the application
logic is making decisions if the select is made using optimistic
concurrency. You can’t guarantee that any decision made is based on the
actual value of the field. For example, a Boolean field shouldn’t be set
to true or false based on whether a relative updated field is equal to
zero because another process could update the relative field at the same
time. The Boolean field would be set based on the value in memory,
which might not be the value that is eventually written to the database. |
3.4 Choosing a Concurrency Model During Development
When developing applications in Dynamics AX,
you can control the use of a concurrency model on two levels. The first
is at a table level, by setting a property on the table definition in
the Application Object Tree (AOT), and the second is by enforcing a
specific model in X++ code.
Figure 8 shows the table-level setting, in which the OccEnabled property can be set to either Yes (the default value) or No.
When the runtime has to execute a statement such as select forupdate custTable where custTable.AccountNum == ‘4000’, it consults the OccEnabled property on the table and translates the statement into an SQL statement with either no hint or an UPDLOCK hint added to the SELECT statement.
The concurrency model setting on the tables in
Dynamics AX is based on an assessment of whether the risk of update
conflict is minimal for the majority of the daily business scenarios in
the application in which the specific table is updated or deleted. The
scenarios can be found by using the cross-reference system in Dynamics
AX or by searching for places in the X++ code where the table is either
updated or deleted. If a table is never updated or deleted in the X++
code, the execution of the code isn’t influenced by whether the table is
OCC
enabled because the table is manipulated only from a rich client form
or a Web client form. Because the form application runtime doesn’t use
the table-level setting when updating records, the OccEnabled property is set to Yes by default on these tables.
Note
Only about 40 of the approximately 2100 tables in the SYS layer don’t use optimistic concurrency. |
If you encounter business scenarios that
require the use of a different concurrency model, you should handle them
individually by applying statement-level concurrency code.
You can apply statement-level concurrency control by exchanging the forupdate keyword with either optimisticlock or pessimisticlock.
This enforces the use of either optimistic or pessimistic concurrency
in a scenario in which the keyword is used and overrules the table-level
setting. In case of enforced pessimistic concurrency, the select statement would be written as follows: select pessimisticlock custTable where custTable.AccountNum == ‘4000’.
Note
You can also control the concurrency model with the use of a variable by calling the concurrencyModel(ConcurrencyModel concurrencyModel) method on a cursor and passing the concurrency model as the parameter. The ConcurrencyModel type is an enumeration type. A similar method is available on the QueryBuildDataSource class, and you can even specify the concurrency model in metadata when defining a query element in the AOT. |
You should enforce pessimistic concurrency when
serialization is necessary; serialization is implemented by requiring
an update lock on a record in the database. The lock prevents two
processes from entering the same scenario because entering requires an
update lock. Only the process holding the update lock can enter the
scenario, and the other process is blocked until the lock is released.
The serializing select statement should therefore include the pessimisticlock keyword.
Best Practices
Enforcing pessimistic concurrency by using the pessimisticlock
keyword is a best practice for developing serialization logic, although
you can implement the same pessimistic concurrency behavior by using
the forupdate keyword on a table where
pessimistic concurrency is chosen at the table level. The X++ code
explicitly states that an update lock is required; more important, the
scenario doesn’t fail if the table property is changed. You can change
the OccEnabled property through customization in higher layers. |
You should enforce optimistic concurrency in
situations in which it is apparent that the optimistic model would
improve concurrency and throughput compared to the pessimistic model,
especially when use of the pessimistic model would cause major blocking
because of update locks that are never converted into exclusive locks.
For example, optimistic concurrency is enforced in the Dynamics AX
consistency check classes, where you can assume that only a few records
are in an inconsistent state and therefore need to be corrected and
updated.
Best Practices
You
should explicitly state the use of optimistic concurrency in the X++
code if the scenario always qualifies for the use of this model. |