View source | Discuss this page | Page history | Printable version   

Retail:Developers Guide/Concurrent Modification and Locking



Most of the times, the WebPOS application generates new data, usually via new documents (receipts) that are created and synchronised to the backend. While creating new data, the concurrent activity of several terminals is not a problem, because each one is creating new documents which are not really related between each other, and therefore their simultaneous activity doesn't generate any sort of conflict.

However, there are some cases in which the WebPOS can modify existing data. The purpose of this document is to explain how this situation is handled, and provide some guidelines and tips for other cases in which data modification may be necessary.

Risks of concurrent modification

When data is modified, there is always the risk that two terminals may be changing the same document or record at the same time. This creates the possibility of conflict between the changes. This situation can be handled differently depending on the case.

Currently in the WebPOS application, the main flow which modifies existing data is the Layaways flow. In this flow, a layaway is initially created, and then partial payments may be subsequently added, until a final payment triggers the completion of the order, and the generation of the shipment.

As these actions involve a modification of an existing record (in this case, the layaway which was saved already in the backend), they may suffer the problems of a concurrent modification if some users mistakenly open the same document at the same time in two different terminals and execute some actions on it.

To prevent problems in this case, some locking strategy must be followed. The strategy which should be followed depends heavily on the use case: the best strategy may be different if the probability of having concurrent modification is very low, or very high.

Optimistic and Pessimistic locking

Two main strategies are commonly discussed when trying to lock records in a system: optimistic and pessimistic locking:

Optimistic is generally much easier to implement, and generates less overhead in most cases, unless the chance of having concurrent modifications is very high. Pessimistic locking requires taking care of removing the lock when its no longer applicable, and this doesn't work very well most of the times in a Web application, which the user can close at any time without much control from the developer, and besides, it's quite tricky to implement, leading to development mistakes and errors which impact the end user.

Currently in the WebPOS we use optimistic locking to handle the Layaways flow. The reason for that is that we consider that the chances of users modifying the same document concurrently are very low: most of the times, the user comes with the printed ticket that allows the cashier to identify which layaway needs to be loaded and modified. This printed ticket even comes with a barcode which can be scanned to prevent human error when introducing the document number, and even if the cashier inputs the document number manually, he will immediately notice if the document is the wrong one, as the information will not correspond with the printed document. Therefore, we consider optimistic locking the best strategy for handling this functionality.

We strongly believe that optimistic locking is the best approach in most cases. It's important to note that, if properly implemented, optimistic locking doesn't provide less safety or robustness than pessimistic locking when concurrent operations happen, it just provides a slightly different user experience. The only difference in terms of behaviour is that pessimistic locking would prevent the second user from opening the record, but optimistic locking will successfully detect and prevent the conflict with the same robustness, and technically, optimistic locking is much simpler to implement, and also causes less headache to users in many cases. An example of this would be that in case a user opens a record, using pessimistic locking the record would not be freed unless the user closes it or the lock itself expires, so if the user leaves the application open and leaves his computer without closing the record, other users won't be able to open the record in other terminals. This can be quite annoying, particularly in the case of Web applications with long valid sessions, which is quite frequently the case in point of sale terminals.

We currently use optimistic locking in the WebPOS both in the Layaways (or in general, orders) flow, and also in the Customer/Customer Address modification flows. Nowadays, there are no other flows in the WebPOS with the risk of concurrent modification

How to implement optimistic locking

The implementation of optimistic locking is quite simple, and works very well with the Openbravo audit information columns.

The main idea is to get the timestamp of the last modification in the record when it is initially loaded in the application. After that, this timestamp needs to be sent again when the changes are going to be synchronised to the backend. When synchronisation occurs, this timestamp is checked against the current timestamp of the record, and if they are no longer the same, then a concurrent modification has happened while the record was being changed in the application, and we can potentially have a conflict, so the change should be rejected.

This is the main principle: we get the timestamp, and then we use this timestamp when synchronising to verify that no changes were done in the meantime.

The way we implement this in Openbravo is using the updated column of the relevant record, which always contains the timestamp of the last modification. We load this information, and send it again, and compare it against the current value of the updated column. So, the full flow should do the following:

In practice, you will probably need to add the updated property to some ProcessHQLQuery class, for instance:

        add(new HQLProperty("ord.updated", "loaded"));

And later on, in your synchronization process, you will need to do some initial check to ensure that the record was not changed in the meantime:

          final Date loaded = POSUtils.dateFormatUTC.parse(jsonorder.getString("loaded")), updated = OBMOBCUtils
          if (loaded.compareTo(updated) != 0) {
            throw new OutDatedDataChangeException(Utility.messageBD(
                new DalConnectionProvider(false), "OBPOS_outdatedLayaway", OBContext.getOBContext()

It is good practice to generate an OutdatedDataChangeException, so that we can clearly identify exceptions which happened due to a concurrent modification of the record. You should set a clear message in the exception which explains the problem to users in a meaningful way.

Back to Concepts

Retrieved from ""

This page has been accessed 1,423 times. This page was last modified on 26 January 2018, at 13:49. Content is available under Creative Commons Attribution-ShareAlike 2.5 Spain License.