<!-- 
RSS generated by JIRA (1001.0.0-SNAPSHOT#100246-sha1:7a5c50119eb0633d306e14180817ddef5e80c75d) at Fri Feb 09 00:29:01 UTC 2024

It is possible to restrict the fields that are returned in this document by specifying the 'field' parameter in your request.
For example, to request only the issue key and summary add field=key&field=summary to the URL of your request.
-->
<rss version="0.92" >
<channel>
    <title>FOLIO Jira</title>
    <link>https://folio-org.atlassian.net</link>
    <description>This file is an XML representation of an issue</description>
    <language>en-us</language>    <build-info>
        <version>1001.0.0-SNAPSHOT</version>
        <build-number>100246</build-number>
        <build-date>07-02-2024</build-date>
    </build-info>

<item>
            <title>[UXPROD-3058] Optimistic Locking</title>
                <link>https://folio-org.atlassian.net/browse/UXPROD-3058</link>
                <project id="10000" key="UXPROD">UX Product</project>
                    <description>&lt;p&gt;Bringing together all optimistic locking features and combining their points...&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;UXPROD-1752&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UXPROD-1752&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Prevent update conflicts (via optimistic locking): platform support for detection&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10322?size=medium&quot; /&gt;
            UXPROD-1752
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
 (Closed, so not included in pointing)&lt;/li&gt;
	&lt;li&gt;
    &lt;span class=&quot;jira-issue-macro&quot; data-jira-key=&quot;UXPROD-2994&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UXPROD-2994&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Optimistic Locking: coordinate rollout of &amp;quot;failOnConflict&amp;quot; to selected modules and APIs&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10322?size=medium&quot; /&gt;
            UXPROD-2994
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-complete jira-macro-single-issue-export-pdf&quot;&gt;Open&lt;/span&gt;
            &lt;/span&gt;
 (65 points)&lt;/li&gt;
	&lt;li&gt;
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;UXPROD-2798&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UXPROD-2798&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Prevent update conflicts (two automated processes acting on the same record)&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10322?size=medium&quot; /&gt;
            UXPROD-2798
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
 (28 points)&lt;/li&gt;
	&lt;li&gt;
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;UXPROD-2797&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UXPROD-2797&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Prevent update conflicts (1 user and system trying to act on the same record)&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10322?size=medium&quot; /&gt;
            UXPROD-2797
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
 (18 points)&lt;/li&gt;
	&lt;li&gt;
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;UXPROD-2796&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UXPROD-2796&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Prevent update conflicts when doing manual edits (User A and User B)&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10322?size=medium&quot; /&gt;
            UXPROD-2796
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
 (38 points)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Total points = 149&lt;/p&gt;

&lt;p&gt;&lt;b&gt;NOTE:&lt;/b&gt; Optimistic locking is the solution described in this feature. Libraries ranked this based on the idea of preventing update conflicts, not necessarily based on the specific solution of optimistic locking.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Problem statement&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;In FOLIO, most storage modules follow the &quot;last writer wins&quot; strategy for handling record updates. From the UI perspective this may lead to a situation when a stale record (older version of a give record) previously loaded into the UI may override a more recent version on the server. Hence relevant updates may get lost in the process and the user is not made aware of what has happened.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Scope:&lt;/b&gt;  The scope of this issue is to create platform support for optimistic locking which modules can make use of on a case-by-case basis (opt-in).  Focus of this feature is on simple &quot;detection&quot; and &quot;prevention&quot; (identifying when a collision has occurred and preventing it).  Additional tools and mechanisms for handling collisions when they occur (e.g. diffs, merges etc.)).  There are 3 phases, two of which are in scope for this feature:&lt;/p&gt;

&lt;ol&gt;
	&lt;li&gt;Detect collisions but do not prevent them. Just log in the system log that a mid-air collision has occurred. There is no instant benefit from this behaviour &#8212; the platform remains susceptible to collisions. But, once detection is deployed,  we can review the logs to evaluate how often collisions occur and which APIs are at risk.&lt;/li&gt;
	&lt;li&gt;Prevent an update when a collision gets detected. This builds on detection and additionally prevents the update from taking place. This is a &#8220;breaking&#8221; change from the API point of view: clients (end-users or batch processes alike) will start seeing an error returned (409 Conflict) when their update collides with another update. The immediate benefit is that we &#8220;protect&#8221; the system from collisions but we also create a fairly terrible user-experience  and probably &#8220;break&#8221; a lot of batch processes that right now happily update records because FOLIO is so forgiving.  This will be implemented as an opt-in feature so functional apps can implement when ready.&lt;/li&gt;
	&lt;li&gt;&lt;b&gt;(Out of Scope):&lt;/b&gt; Tools. Built tools for handling the &#8220;409 Conflict&#8221; errors. It could be a simple &#8220;resubmit my changes anyway&#8221; button in the UI that lets the user to force their changes (risk for messing up is with the user) and a way to &#8220;retry&#8221; for batch processes.  It could also be something fancy when end-users can review the conflict and choose which changes to keep and which to drop, etc.&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;&lt;b&gt;Proposed solution&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;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.  &lt;/p&gt;

&lt;p&gt;From the storage and API perspective, optimistic locking is the proposed strategy to handle conflicts:&lt;/p&gt;

&lt;ul&gt;
	&lt;li&gt;&lt;b&gt;optimistic locking&lt;/b&gt; &amp;#8211; each record state is marked with a &quot;version number&quot; (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&apos;t changed before it writes the record back. If the record is dirty (version doesn&apos;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.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In general, &lt;em&gt;optimistic locking&lt;/em&gt; 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).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Use cases collected from community&lt;/b&gt; &lt;font color=&quot;#d04437&quot;&gt;add others that seem likely&lt;/font&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Not frequent: 2 users editing the same record at the same time
	&lt;ul&gt;
		&lt;li&gt;User A and User B editing the same record at the same time (not frequent) &#8211; users, orders, instances, holdings, items, requests&lt;/li&gt;
		&lt;li&gt;User A editing an item and User B creating a request for that item&lt;/li&gt;
		&lt;li&gt;User A editing and item and User B putting that item on course reserve at the same time&lt;/li&gt;
		&lt;li&gt;User A editing an invoice and User B trying to approve the same invoice at the same time&lt;/li&gt;
		&lt;li&gt;User A editing an item and User B deleting the item before User A&apos;s edits are saved (see  
    &lt;span class=&quot;jira-issue-macro&quot; data-jira-key=&quot;UIIN-730&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UIIN-730&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Error message when item has been deleted from another window&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10300?size=medium&quot; /&gt;
            UIIN-730
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-complete jira-macro-single-issue-export-pdf&quot;&gt;Blocked&lt;/span&gt;
            &lt;/span&gt;
)&lt;/li&gt;
		&lt;li&gt;User A editing a request and User B cancelling the request before User A&apos;s edits are saved (see 
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;UIREQ-344&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/UIREQ-344&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Deleting already-deleted request causes ungraceful error&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10300?size=medium&quot; /&gt;
            UIREQ-344
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
)&lt;/li&gt;
		&lt;li&gt;When attempting to update holdings and their items concurrently the holdings updates will ever so often interfere with the item updates, effectively nullifying the latter (see 
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;MODINVSTOR-516&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/MODINVSTOR-516&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;Cannot safely update holdings and items concurrently for any given instance&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10303?size=medium&quot; /&gt;
            MODINVSTOR-516
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
). This particular item is being addressed via 
    &lt;span class=&quot;jira-issue-macro resolved&quot; data-jira-key=&quot;RMB-388&quot; &gt;
                &lt;a href=&quot;https://folio-org.atlassian.net/browse/RMB-388&quot; class=&quot;jira-issue-macro-key issue-link&quot;  title=&quot;PostgresClient.getById with transaction, with &amp;quot;SELECT &#8230; FOR UPDATE&amp;quot;&quot; &gt;
            &lt;img class=&quot;icon&quot; src=&quot;https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10322?size=medium&quot; /&gt;
            RMB-388
        &lt;/a&gt;
                                                    &lt;span class=&quot;aui-lozenge aui-lozenge-subtle aui-lozenge-success jira-macro-single-issue-export-pdf&quot;&gt;Closed&lt;/span&gt;
            &lt;/span&gt;
.&lt;/li&gt;
		&lt;li&gt;User A and User B generating a new number using the number generator for call number or accession number (number generator runs separate queries for selecting and incrementing the number (GBV); not relevant if FOLIO combines select and increment into one query) (not a challenge in FOLIO because the functionality does not exist)&lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;More frequent: 1 user and system trying to act on the same record, either individual records or batch
	&lt;ul&gt;
		&lt;li&gt;User A editing a user and system batch process is updating lots of users&lt;/li&gt;
		&lt;li&gt;User A editing an instance/holding/item and data import updating the same record (consider the DI redesign that is taking place now)&lt;/li&gt;
		&lt;li&gt;User A editing an item and checkout trying to update the item status&lt;/li&gt;
		&lt;li&gt;User A editing an item and bulk renewal trying to update the item&lt;/li&gt;
		&lt;li&gt;User A editing a budget and system applying a transaction to that budget at the same time&lt;/li&gt;
		&lt;li&gt;User A editing an instance/holdings/item after data import ran in Preview mode but before the data import changes were committed&lt;/li&gt;
		&lt;li&gt;User A editing a request while the request is being expired (request expiration date or hold shelf expiration date) - &lt;em&gt;rare&lt;/em&gt;&lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
	&lt;li&gt;Two automated processes acting on the same record
	&lt;ul&gt;
		&lt;li&gt;Checkout happening and updating status on an item record at the same time as import updating the item&lt;/li&gt;
		&lt;li&gt;Data import happening at 2 libraries within the same tenant, affecting the same record (e.g. 5 Colleges processing new cataloging records)&lt;/li&gt;
	&lt;/ul&gt;
	&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;b&gt;User impact&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;This approach will not prevent collisions, but it would notify the user when they happen and offer them a choice.  Something like, &quot;Sorry AgentB has already updated the record and your working copy might not be up-to-date? Would you like to:&quot; (a) Update anyway (b) Reload.  &lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This situation can happen when multiple data imports are happening at the same time (or data import and a user acting on the same record at the same time) and can affect many records at the same time. Cleanup can then be very time-consuming and confusing.&lt;/p&gt;

&lt;p&gt;&#160;&lt;/p&gt;</description>
                <environment></environment>
        <key id="13637">UXPROD-3058</key>
            <summary>Optimistic Locking</summary>
                <type id="10006" iconUrl="https://folio-org.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10307?size=medium">Umbrella</type>
                            <parent id="11697">UXPROD-1814</parent>
                                    <priority id="10005" iconUrl="https://dev.folio.org/assets/jira-priority/tbd.svg">TBD</priority>
                        <status id="3" iconUrl="https://folio-org.atlassian.net/images/icons/statuses/inprogress.png" description="This issue is being actively worked on at the moment by the assignee.">In Progress</status>
                    <statusCategory id="4" key="indeterminate" colorName="yellow"/>
                                    <resolution id="-1">Unresolved</resolution>
                                                        <assignee accountid="557058:b8e64633-1f7c-402d-9caf-9959a5ba5d0d">Jakub Skoczen</assignee>
                                                                <reporter accountid="557058:a957226f-df85-4fc8-97f4-8b27a26029ed">Holly Mistlebauer</reporter>
                                    <labels>
                    </labels>
                <created>Tue, 4 May 2021 15:51:48 +0000</created>
                <updated>Tue, 14 Feb 2023 17:41:07 +0000</updated>
                                                                                <due></due>
                            <votes>0</votes>
                                    <watches>5</watches>
                                                                    <issuelinks>
                            <issuelinktype id="10008">
                    <name>Defines</name>
                                                                <inwardlinks description="is defined by ">
                                        <issuelink>
            <issuekey id="11829">UXPROD-2994</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11895">UXPROD-3161</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11897">UXPROD-3163</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11899">UXPROD-3165</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="12057">UXPROD-3089</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11061">UXPROD-1752</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11581">UXPROD-2796</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11582">UXPROD-2797</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="11584">UXPROD-2798</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="13348">UXPROD-3164</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="13295">UXPROD-3534</issuekey>
        </issuelink>
                            </inwardlinks>
                                    </issuelinktype>
                            <issuelinktype id="10006">
                    <name>Gantt End to Start</name>
                                            <outwardlinks description="has to be done before">
                                        <issuelink>
            <issuekey id="12732">UXPROD-3700</issuekey>
        </issuelink>
                            </outwardlinks>
                                                        </issuelinktype>
                            <issuelinktype id="10003">
                    <name>Relates</name>
                                            <outwardlinks description="relates to">
                                        <issuelink>
            <issuekey id="66458">MODINVOICE-297</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="38117">MODINVUP-10</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="48542">UIIN-1245</issuekey>
        </issuelink>
            <issuelink>
            <issuekey id="13710">UXPROD-3666</issuekey>
        </issuelink>
                            </outwardlinks>
                                                                <inwardlinks description="relates to">
                                        <issuelink>
            <issuekey id="12135">UXPROD-3173</issuekey>
        </issuelink>
                            </inwardlinks>
                                    </issuelinktype>
                    </issuelinks>
                <attachments>
                    </attachments>
                <subtasks>
                    </subtasks>
                <customfields>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    <customfield id="customfield_10000" key="com.atlassian.jira.plugins.jira-development-integration-plugin:devsummarycf">
                        <customfieldname>Development</customfieldname>
                        <customfieldvalues>
                            
                        </customfieldvalues>
                    </customfield>
                                                                <customfield id="customfield_10057" key="com.atlassian.jira.plugin.system.customfieldtypes:select">
                        <customfieldname>Development Team</customfieldname>
                        <customfieldvalues>
                                <customfieldvalue key="10144"><![CDATA[Core: Platform]]></customfieldvalue>

                        </customfieldvalues>
                    </customfield>
                                                                <customfield id="customfield_10014" key="com.pyxis.greenhopper.jira:gh-epic-link">
                        <customfieldname>Epic Link</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue key="$xmlutils.escape($text)">Platform, DevOps and Release Management</customfieldvalue>
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                            <customfield id="customfield_10062" key="com.atlassian.jira.plugin.system.customfieldtypes:float">
                        <customfieldname>Kiwi Planning Points (DO NOT CHANGE)</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>149.0</customfieldvalue>
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                            <customfield id="customfield_10063" key="com.atlassian.jira.plugin.system.customfieldtypes:float">
                        <customfieldname>PO Rank</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>0.0</customfieldvalue>
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                            <customfield id="customfield_10019" key="com.pyxis.greenhopper.jira:gh-lexo-rank">
                        <customfieldname>Rank</customfieldname>
                        <customfieldvalues>
                            <customfieldvalue>0|i02o87:</customfieldvalue>

                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    <customfield id="customfield_10020" key="com.pyxis.greenhopper.jira:gh-sprint">
                        <customfieldname>Sprint</customfieldname>
                        <customfieldvalues>
                            
                        </customfieldvalues>
                    </customfield>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    </customfields>
    </item>
</channel>
</rss>