Implementation of ETag for write services
This article details an approach for implementing ETags in OData write scenarios in SAPUI5 applications.
Traditionally SAP GUI applications have utilized pessimistic locking using lock objects to ensure data consistency in parallel usage scenarios like single material number being edited by two users concurrently, same employee’s work schedule getting updated in parallel in two sessions, etc.
This tight coupling has some disadvantages; for example, one user can block the process for long periods, non-key users can choose to discard the changes disallowing key users to make changes till the transaction is released by non-key users, etc. This also does not fit well with the modern user experience paradigms which require applications to be stateless, decoupled and scalable for large transaction volumes.
OData provides for ETag based optimistic concurrency management for write transactions such as UPDATE and DELETE. Same user can login via multiple browser sessions or on multiple devices, or multiple users can login simultaneously. In all such scenarios, the data must be consistent and no user entered data should be silently ignored or lost. Either data should be persisted consistently or the application should gracefully inform the user that the carried-out changes could not be saved.
Optimistic concurrency control is a technique where multiple transactions can frequently complete without locking objects for entire duration of transaction. ETag based optimistic concurrency control locks database objects for very small durations (typically milliseconds), which is the actual time to execute the Database write transaction).
Out of the box, ETag implementation does not allow acquisition of locks before undertaking any modifying database operation. It can happen that users simultaneously are performing UPDATE and DELETE requests on same entity, so it becomes quintessential to set locks before implementing ETags. If locks are not set, then it can happen that even though the ETag check is successful for both distinct operations, the database operation of one of those may overwrite the changes of another because till the time the second operation succeeds the first one has already modified the data and hence the results of the first operation will become inconsistent. Had we acquired locks in the first place, such an untoward situation will not arise as for the second operation the lock acquisition will fail and further processing will stop. Both approaches are illustrated diagrammatically as below –
Figure 1: Standard ETag implememtation
Figure 2 : Custom ETag implementation with Locks
I illustrate below the working of ETag with an example of OData DELETE call.
The SAPUI5 application running in the browser passes an entity’s ETag value in the “If-Match” HTTP request header.
Figure 3: OData DELETE Call with If-Match Header passing ETag value of entity to be deleted.
To realize this custom ETag implementation following steps should be performed.
A. Redefine /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_IS_CONDITIONAL_IMPLEMENTED
Redefine the framework method as shown below to enable conditional handling of ETags. The general approach of declaring a property as ETag in the gateway project did not work in our case because we needed to implement locking along with e-Tag. Scenario is no concurrent change of resource is allowed till the time the data is deleted and on error a notification should come up.
Figure 4: Conditional ETag Implementation
Run and check values as –
Figure 5: If-Match HTTP header value in debug mode
B. Extract the ETag value
Using this code snippet we can extract the timestamp value in the Delete Entityset method (Since we have enabled the conditional handling so this value will always be populated)
Data : ls_conditions TYPE /iwbep/if_mgw_appl_types=>ty_s_conditions, ls_if_match_e_tag TYPE /iwbep/if_mgw_appl_types=>ty_s_etag, lt_if_match_e_tag_value TYPE /iwbep/if_mgw_appl_types=>ty_t_etag_value, lv_mod_timestamp TYPE tzntstmps. " Fetch the e-Tag value from the header container io_tech_request_context->get_conditional_info( IMPORTING es_conditions = ls_conditions ). READ TABLE ls_conditions-if_match INTO ls_if_match_e_tag INDEX 1. IF sy-subrc IS INITIAL. lt_if_match_e_tag_value = ls_if_match_e_tag-tag_values. READ TABLE lt_if_match_e_tag_value INTO ls_if_match_e_tag_value WITH KEY name = ‘MOD_TIMESTAMP’. IF sy-subrc IS INITIAL. lv_mod_timestamp = ls_if_match_e_tag_value-value. ENDIF. ENDIF.
Code Snippet 1: ETag Implementation
PS: Another way is to obtain the modified timestamp from the Header but then it would require parsing the value.
The obtained timestamp (as shown) can then be compared with the database values and requisite action be taken.
Figure 6: Parsed timestamp value
In this case, we check if the modified timestamp from front end is different from the one in the database then we fail the operation and notify the user that deletion failed as the data which he/she was trying to delete was stale (already modified by somebody else)
Please leave behind your experiences and feedback.