Entity cache versioning, locking, and entity refreshing
The application caches entity data for faster
access. The gw.transaction.Transaction
API methods that refer to original entity instances check against the
database version as of the time the bundle loaded that entity. The time
of loading might not have been immediately before your code checks the
original entity data. The application may have loaded the database row
long before the recent application change. Although the database data
may have changed since the time of loading, some safeguards prevent concurrent
data access in most cases. For typical entity access, the server prevents
committing an entity instance if the instance changed in the database
after the entity loaded in the bundle and before the bundle commits.
Entity instance versioning and the entity touch API
PolicyCenter protects entity instances from concurrent access through a version property that exists on all versionable entities. Almost all entity types in the system are versionable. If you load a versionable entity instance into a bundle, the application loads the version number and stores it with the data.
If a bundle commits, PolicyCenter checks this version number property in each entity instance with the latest version in the database. PolicyCenter confirms that the cached entity instance is up to date. If the version numbers match, the commit can proceed.
If the version numbers do not match, the entire commit attempt fails for the entire bundle. In other words, if the recent change for an object relies on an out-of-date database row, it is unsafe to commit this recent change. Gosu throws an exception during the database commit.
During the final commit, the version number is increased by one from its previous value. In typical code, you need to add entity instances to a bundle and then modify them. PolicyCenter automatically increments the version number.
In rare cases, it may be desirable to force the version number of an entity to increment even if there is no known change to the entity yet.
For example, suppose your code reads values from four entity instances (A, B, C, and D) that have a tight conceptual relationship. Your code reads the values and changes most or all of the objects in one database transaction. In one case, the code makes changes to only B, C, and D. There remains one object (A) for which you did not change properties. Therefore, A is not in the same bundle as B, C, and D. In this case, neither does the database row for A change nor does its version number increment, which will not be a problem in some cases.
However, suppose the three objects you change are related to the current properties on A. The default behavior may be undesirable compared to updating A, B, C, and D together:
- You might want to protect against other threads on the current server, including the user interface, from making concurrent changes on A that make the other changes make no sense.
- You might want to protect against other servers in the PolicyCenter cluster from making concurrent changes on A that make the other changes make no sense.
- You might want the last modified time of A to match the last modified time of B, C, and D.
- You might want to force preupdate rules to run for object A.
To force PolicyCenter
to increment the entity version number (obj.BeanVersion), update the modified
time, and force preupdate rules to run, call the touch method on the entity instance.
The method takes no arguments:
obj.touch()Record locking for concurrent data access
In addition to version protections, the system locks the database briefly during a commit. The server throws concurrent data change exceptions if two different threads or two different servers in a cluster simultaneously modify the same entity at the same time.
Concurrent data change exceptions always occur if two application users attempt to change the same record from the application user interface.
If all of the following conditions are true, a concurrent data change exception does not occur:
- A batch process or messaging plugin made the update that conflicts with a change from the user interface.
- The user that made the update is a
systemuser. In the base configuration, in theUserrecord of a system user, theSystemUserTypeproperty issysadminorsysservices. - The
BeanVersionfield in the database record differs only by 1. In this case, only the batch process or messaging plugin has updated the record. - The changes that the concurrent change makes are not in fields that the batch process or messaging plugin updated.
User interface bundle refreshes
In some cases, the PolicyCenter user interface internally refreshes bundle entity data with the latest version from the database. For example, PolicyCenter refreshes the data when changing from view-only to edit mode.
Gosu does not provide a public API for you to programmatically refresh a bundle’s entity data.
