Using stateful session for pessimistic objects locking
Hi to all,
as probably all of ABAPers that touch UI5 for even the simplest (transactional) applications, I found some bottlenecks, one of them is the pessimistic object locking, which in ABAP is the standard way to deny multi-user changes of the same object at one time. I found only a little information about the possibilities here, so I managed to write this information to share with you and expect any corrections / improvements you find.
As the UI5 application is stateless by default, ABAP enqueue statement is meaningful only during the execution of the backend ICF handler methods. After the program flow, the session is closed and thereafter all locks in the server enqueue table are lost for the corresponding session. Stateful session is the concept that can be helpful for (mostly) desktop applications, built in UI5.
To try it, create an ICF handler in SICF transaction, create a handler class by implementing the IF_HTTP_EXTENSION interface and use this class as the ICF node handler. Create some call to the SAP backend using e.g. loadDataNew statement (but any ajax will do). In the ABAP class and it’s HANDLE_REQUEST method, write:
server->set_session_stateful( stateful = if_http_server=>co_enabled ).
What this statement does is (surprisingly) keeping the session stateful. Ater this call was made, although the call is ended already, you can see that there is another connection in transaction SM04 as the HTTP session type. This means, that your handler is instantiated and it’s context is held in the memory for as long as the session is kept alive. Any ENQUEUE calls that are made till the session is closed are held in the ABAP enqueue table. Any parameters / attributes in the handler class you create / fill will be there unce you make another call from the UI5 application (for the particular session only).
So how to close the session:
- wait for the session timeout – this can be set (to a lower value only according to the server default session) in SICF transaction for a particular node in it’s Service data tab,
- close the session by another call to the SAP backend and using the statement server->set_session_stateful( stateful = if_http_server=>co_disabled ).
Of course, it is recommended not to rely on the internal mechanism of terminating the sessions, because you probably use too much resources of the server even those are not needed any more. A programmer in ABAP is often confrontated by a long-time duration of reports or processes, which would time-out once they are held in foreground (the same applies for example in a Webdynpro for ABAP application, which runs in portal – once the user does not touch the application, it time-outs). For this purpose – in webdynpro ABAP one would use the timer element. Here, in UI5, one of the solutions is using the jsWindow.setInterval( ) function, which could call a dummy method in SAP backend to prevent from time-outing.
Different approaches to stateful sessions
- you can make the whole application stateful (which means you would make some initialization call to the SAP, creating the stateful session and closing it once the user logs off, or navigates somewhere else, or closes the window etc.),
- make the application “hybrid” – meaning, it’s state is managed according to it’s context (once the user tries to lock an object, create the session, lock the object there using ENQUEUE and keep it locked until the situation changes – e.g. user switches to the processing of another object).
The design depends on the application itself and you should choose the most suitable for your scenario.
What in fact does the set_session_stateful( ) method?
It creates a response cookie in the handler class (and also sets some attributes for it’s own processing), which is sent to the browser. The name of the cookie is sap-contextid. The UI5 programmer does not really need to think about it, as the browser will make the next call using this cookie in the request header automatically. That is the way that SAP’s HTTP dispatcher is able to connect the (next) request to the specific user session, which provides him with the data that the programmer can buffer in his handler class. It also makes it possible to raise the use_count field of the locked objects in the same session (which you can check in the SM12 transaction).
Once the session is closed:
- if it is closed by the server (session time-outed), the browser does not know this yet. The browser still sends the cookie in request header, but SAP framework does not find any corresponding context (session), so it creates a new one. Of course, any object locks and “buffered” data in the handler class are lost in this case,
- if it is closed by the programmer (meaning, he / she called set_session_stateful( ) method to disable it, the cookie is deleted at client (in fact, it is sent to the client with the expiration in the past – browser evaluates this as the command to delete the cookie). Everything else applies here the same – system closes the session, locks for the session are lost (if there are still any). But the next time UI5 makes any calls to the backend, it does not send any cookie, so SAP is not confused with looking for any open sessions for that user.
And what about multi-connections from the same user?
It happens, that once an user runs the application from another instance of the browser, he / she would create a new instance at the server. As for the object blocking, this is the same as a new session in SAP GUI (and once the object is locked, the user would get a message, that the object is locked by himself). However, the user does not have to create a new instance, but he can create a new tab / window in the same browser instance. In this scenario, the application would send the same cookie request as the previous one (as the cookies are shared among the tabs). This means, different tabs would act as the same session.
This is where the “buffering” mechanism and the possibility to store values in the attributes of the handler class takes place. One of the solutions is to create an own “guid” once the application is initialized (either in SAP backend or in javascript of the app itself) – for every “tab”, or to say – for every instance of the application. Send this guid along all other form fields and let the backend decide. Once the application tries to lock the object, first compare, if the object is not locked already in the attribute of the handler class using a different guid. This ensures, that one user would not be able to change the same object in different tabs. If you would use only the ENQUEUE FM, you would raise the use_count of the object in the lock table sending no lock-errors, which could make trouble.
Please feel free to confront / add any information that would make the process event more transparent.