Concurrency access modes
A concurrency access conflict occurs when a two users attempt to save changes made to the same entity at the same time. While the first user who saves has no problem, the second user faces a concurrency conflict.
There are several options for dealing with this situation.
Pessimistic concurrency
Pessimistic concurrency involves locking database records to prevent other users from being able to access and modify them until the lock is released. This is similar to the case where two users attempt to open the same file on a network share.
However, the ability to lock records is not supported by all databases, and can be complex to set up as well as highly resource intensive. It is not practical in disconnected scenarios such as web applications.
Entity Framework Core does not support pessimistic concurrency control.
Optimistic concurrency
Optimistic concurrency implies that database records are not locked. Concurrency checks can employ several mechanisms.
Entity Framework Core provides support for optimistic concurrency management.
Last write wins
In many cases, it does not matter if the second user unintentionally overwrites changes made by the first user if we consider that any changes reflect the correct state of the data.
For example, there is no need for concurrency checks when two users simultaneously update a sports match result with the final score.
In this case, we can safely assume that the last user to save wins and overwrites the first user's changes.
Concurrency token
A concurrency token can be used to check if the second user is trying to overwrite changes made by the first user.
When a user retrieves an entity from the database, they get the entity with a concurrency token. The concurrency token represents the current state of the entity.
If the two users retrieve the same entity in the same state, the concurrency token is identical.
When a user saves changes, the concurrency token of the entity (which reflects the state of the entity when it was initially retrieved from the database) is compared to the concurrency token currently in the database :
- The concurrency token is identical :
It means that no other user has saved changes to the entity between the moment it was retrieved from the database and the save.
In this case, the save is allowed and the concurrency token is updated in the database to reflect the new state of the entity. - The concurrency token is different :
It means another user has made changes to the entity between the moment it was retrieved from the database and the save.
In this case, the save is forbidden.
Concurrency access modes in Neos
Entity Framework Core supports two approaches to handling concurrency conflict detection:
- configuring existing properties as concurrency tokens
- adding an additional
RowVersionproperty to act as a concurrency token
RowVersion mode (concurrency token)
Note
The RowVersion concurrency access mode is enabled by default in Neos when using a database persistence other than Oracle.
When activated, Neos automatically manages a RowVersion property on each entity.
When a web API returns a result from an entity view, the RowVersion is provided in the response.
When saving in a UI view or calling a PUT web API, the RowVersion is sent back from the client in the request.
The backend compares this RowVersion from the one in database. If they are different then a concurrency access exception is raised and the API responds with an error.
In SQL Server
Neos automatically creates a rowversion column in each table.
This column is mapped to the RowVersion property on the entity.
In PostgreSQL
A system column for concurrency checks exists by default.
This column is mapped to the RowVersion property on the entity.
In Oracle
Oracle does not natively support concurrency tokens.
Warning
You cannot use the RowVersion concurrency access mode with an Oracle persistence.
Disabled mode (last write wins)
Note
The Disabled concurrency access mode is enabled by default when using a YAML or an Oracle persistence.
It cannot be changed for these types of persistence.
Depending on your specifications, you may need to be in a last write wins configuration. This can be the case when using a legacy database that does not use a concurrency token.
Setting up the Disabled mode
To disable concurrency checks, you need to change the default concurrency access mode in the ASP.NET Core application configuration.
Example of appsettings.Development.json :
{
"PersistenceSettings": {
"DefaultConcurrencyAccessMode": "Disabled"
}
}
Ignoring concurrency access checks for a specific PUT request
It is possible to tell Neos to ignore concurrency access checks for a specific PUT request.
This can be done by passing a neos-concurrency-mode HTTP header with the value Disabled.
Concurrency token in image URLs
When an entity view contains an image property, Neos generates a link in this format by default:
https://technicaldemos.neos.groupeisagri.com/AGIS/webapi/imggalleryview/15/thumbnail?v=+fENAAAAAAA=
The RowVersion is added to the URL in a v parameter. This parameter is ignored by the server but allows you to have a URL that will be modified when the image is modified.
This makes it possible to take advantage of the browser cache and still have the new image when it is modified on the server.
In Oracle and in Disabled mode, RowVersion is always empty. The auto-generated URL will then look like this:
https://technicaldemos.neos.groupeisagri.com/AGIS/webapi/imggalleryview/15/thumbnail?v=
The URL will therefore not change automatically when the image changes on the server and you will need to force the browser cache to be refreshed if necessary.