[FOLIO-2028] SPIKE: how to handle update conflicts? Created: 20/May/19  Updated: 12/Apr/21  Resolved: 12/Apr/21

Status: Closed
Project: FOLIO
Components: None
Affects versions: None
Fix versions: None

Type: Task Priority: P3
Reporter: Jakub Skoczen Assignee: Jakub Skoczen
Resolution: Done Votes: 0
Labels: platform-backlog
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original estimate: Not Specified

Issue links:
Cloners
is cloned by UXPROD-1752 Prevent update conflicts (via optimis... Closed
Relates
relates to FOLIO-2027 Data Problems from Front-End Record C... Open
relates to RMB-688 PATCH to update only some JSONB prope... Open
relates to DEBT-1 No optimistic locking/update conflict... Closed
relates to MODINVSTOR-713 Enable support for optimistic locking... Closed
relates to RMB-719 SPIKE: design protocol and implementa... Closed
relates to RMB-727 Implement support for optimistic locking Closed
Sprint:
Development Team: Core: Platform

 Description   

Problem statement

In FOLIO, most storage modules follow the "last writer wins" strategy for handling record updates. From the end-user perspective this may lead to a situation when a stale record (older version of a record) previously loaded into the UI overrides a more recent version of the record on the server. Hence relevant updates may get lost in the process.

Possible solution

Handling of updates in FOLIO should rely on more explicit semantics, both in the storage (backend) APIs and the way it is communicated to the user through the UI.

From the storage and API perspective there are two major strategies for handling conflicting updates:

  • optimistic locking – each record state is marked with a "version number" (or a timestamp, hash, etc) which is returned to the client along with the record. The client includes the version number during the update and the server checks that the version hasn't changed before it writes the record back. If the record is dirty (version doesn't match) the update is aborted. In practice for a REST API (typical FOLIO uses case) this means using ETag with a combination of If-Match conditional request and 412 (precondition failed) and 409 (conflict) error codes.
  • pessimistic locking – the record being updated is "locked" until the client completes the update. This means no concurrent updates are possible and ensures the state between the client and server is consistent. It can, however, lead to contention issues and care must be taken to avoid deadlock situation. In practice, this strategy require an explicit "locking API" exposed to client (UI) – the client will need to request and release lock for any record update explicitly.

In general, optimistic locking is used when the risk of collisions (updates to the same record) is low and when the lock granularity is high ((ie duration of any given update is short). The opposite is true for the pessimistic locking strategy.

Also, the impact on the user experience should be taken into account when selecting the appropriate strategy:

  • OL means that in certain situations the update operation will fail which needs to be communicated to the user, The UI should then allow the user to choose the next step, e.g by refreshing the state of the record in the browser and re-applying original changes.
  • PL means that a particular client (browser) owns an exclusive lock to perform any updates to the record. To avoid contention issues, the lock should include an automatic timeout after whic the lock is automatically released – this should be communicated to the user e.g in form of a timer.


 Comments   
Comment by Björn Muschall [ 24/May/19 ]

Both technicians and librarians from Leipzig prefer the optimistic looking approach. Since we cannot use the system productively without a solution (end 2019), I would like to encourage the decision making process. Does it make sense to discuss this at the Product Council since it's an overarching issue? What do you think, Cate Boerema ? I know there is a lot of other things.

Comment by Cate Boerema (Inactive) [ 05/Jun/19 ]

Hi Björn Muschall. In talking to Jakub Skoczen, it seems like OL is more commonly used, more user friendly and technically simpler. Given that, Jakub and I think we probably ought to just pursue the OL approach.

If we pursue this, it could be phased as follows:
1. Implement OL and, when there's a conflict, give users two choices: "update anyway" or "reload page". This isn't a perfect solution but it is probably better than what we have now. During this phase, our recommendation to users would be to reload the page and reapply their changes so as not to lose other updates.
2. Provide a third option to users when there is a conflict: "merge". We'd need to decide what that option would look like (there might be some UI for viewing conflicts and deciding which version to take. We'd need UX input on this, of course, but there are models out there for how this could work (e.g. GitHub)

Alternatively, we could consider implementing partial updates (PATCH) and record history. This is what JIRA seems to do. When there is a conflict, JIRA silently overrides (so it basically behaves like FOLIO does today) BUT the issue is mitigated by the fact that it retains update history so you can revert pretty easily (there's no revert button, but you can manually reapply). JIRA also has inline (field-based) editing which reduces the likelihood of conflicts.

Thoughts?

Comment by Julian Ladisch [ 06/Jun/19 ]

RMB supports updating only a specific field:
https://github.com/folio-org/raml-module-builder/blob/v24.0.0/domain-models-runtime/src/main/java/org/folio/rest/persist/PostgresClient.java#L1100-L1176
This requires a special API endpoint, though. There exists no API endpoint that makes use of this single field update capability.

Generated at Thu Feb 08 23:17:39 UTC 2024 using Jira 1001.0.0-SNAPSHOT#100246-sha1:7a5c50119eb0633d306e14180817ddef5e80c75d.