Successful applications provide a smooth user experience. An integral aspect of user experience is the performance and the responsiveness of an application. When some aspects of an application cannot be made faster by more performance tuning, there are still ways to further improve the user experience. These ways make use of the “
perceived performance”. This article describes two distinct ways to improve the
loading experience of data sources.
Delayed Loading with Background Processing and Loading Indicator
Let’s take an application that has spread its information across multiple tabs of a TabStrip component. When the user clicks on the second tab, there are three data sources displayed, one of them is used in a Chart component and the two others in a Crosstab component. Adding up the loading times of all three data sources would cause a significant waiting time when the user switches to “Tab 2”. Therefore, the application developer decides to
pick the “most relevant” data source and shows it right after the tab switch, whereas the
other two data sources are loaded with background processing. During background processing, the user can already take a look at the first data source and does not perceive the loading time as waiting time.
The starting point is to set the “Load in Script” property of the data sources to “true” that shall not be loaded right after the tab switch:
When the user switches tabs, the “On Select” event of the TabStrip is triggered. This event handler is used for two things. First, a
loading indicator is displayed in the components displaying the data sources for which the loading shall be delayed. Second, a
background processing step is triggered, which performs the loading of data.
TABLE_LOWER_LEFT.showLoadingState("Loading...");
TABLE_LOWER_RIGHT.showLoadingState("Loading...");
APPLICATION.doBackgroundProcessing();
This will show the contents of “Tab 2” in the following sequence: The chart in the upper part shows up first, the two tables in the lower part display a “Loading…” message.
During that time, the “
On Background Processing” handler
loads the data sources for the two tables and
removes the loading indicators:
TABLE_LOWER_LEFT.getDataSource().loadDataSource();
TABLE_LOWER_RIGHT.getDataSource().loadDataSource();
TABLE_LOWER_LEFT.hideLoadingState();
TABLE_LOWER_RIGHT.hideLoadingState();
After the background processing has finished the data in the tables is shown:
This sample has illustrated a basic use case for improving the perceived user experience with background processing. More use cases and more extensive insights into the possibilities of background processing can be found in Lumira’s
“Application Designer Guide” in chapter “Working with Background Processing”:
https://help.sap.com/doc/851a17484c184c798e645e7e81d37e2f/2.3.2.0/en-US/lum_23SP02_ds_user_en.pdf
Pre-Loading of Data Sources
With pre-loading of data sources, the above example improves the perceived performance in a different way. This “different way” seems to be the “better way” at first glance. However, it has significant drawbacks, which should be considered before choosing this option. These drawbacks will be explained at the end of this chapter.
The Idea Behind Pre-Loading
With “delayed loading”, the application
uses the time when the user looks at the chart in the upper part of “Tab 2”. During that time, the data for the two tables is loaded and the user won’t perceive this as waiting. Why not
use the time when the user is looking at “Tab 1” and pre-load all data sources of “Tab 2”? Then, there would be no waiting time at all when the user switches to “Tab 2”. With the current solution we’d have two waiting times: First waiting for the chart to load its data right after clicking on “Tab 2”, and then the waiting time for the loading of the two tables.
The Timing of Pre-Loading
Loading all data at startup of an application might not be a good idea, because startup performance is one of the most critically perceived times. If data in an application is structured, for example, into five tabs and it is assumed that users go through these tabs sequentially, it might be a good choice to preload the data of “Tab 5”, when the user is at “Tab 4”. In our example the data of “Tab 2” shall be pre-loaded and “Tab 1” is visible initially. This means we’re back to application startup – which is okay if other data sources in the application are still delayed to later points in time.
Pre-loading must make use of background processing, because otherwise, in the example above, the pre-loading of “Tab 5” when “Tab 4” is activated, would slow down the visibility of “Tab 4” with the same amount of time that shall be gained for “Tab 5”.
The API for Pre-Loading
Looking at the available APIs in Lumira, it seems trivial which API to call for pre-loading data:
DS_1.loadDataSource(); // <-- Nice try. Does not pre-load the data!
However,
“loading a data source” is not “loading the result set of a data source execution”. Loading a data source involves obtaining a connection/session in the target analytic system (for example, HANA, BW, …). It loads the “meta data” of a data source, such as available measures, dimensions, variables, and prompts. If there are mandatory variables or prompts that have no valid value, then the “load” process will trigger a prompt screen. However, the
“load” process will not execute the “query” for the data source, and in turn the “result set” of the “query” will not be calculated by HANA/BW and therefore cannot be passed to Lumira.
Lumira
delays the “query execution” to get the “result set” to the latest possible moment. That is because getting the result set is by far the slowest operation in analytics. Therefore, Lumira does this only when it’s really needed. This means, even if a data source is loaded but the
component that shows the values of a data source
is not visible, then the result set will not be fetched. This fetching is done only when the component becomes visible. In case of a tab strip, this is when the tab is made visible.
At this point, it seems that idea of pre-loading data sources is a dead end. Pre-loading shall load data for a component that is not yet visible, but Lumira delays loading data until a component is visible. Are we doomed? ?
But wait, there’s a way out! ?
Lumira doesn’t prevent all loading of data for invisible components. If data is consumed via APIs, such as “getData” or “getDataSelections”, then Lumira must get the query result even for invisible components.
Using “getData” in a generic way is tricky. It requires passing a selection that refers to a specific data cell. This selection differs for each data source and it depends even on the state of data sources (for example, what’s on each axis). Fortunately, “getDataSelections” can be called in a way to “misuse” it for all data-sources in any state:
DS_1.getDataSelections({});
Under normal circumstances, the
return value of “getDataSelections” is used to iterate over values in the result set. However, in our use case this is not needed. We
only want to profit from the side effect of loading the result set.
Pre-Loading in Action
In our example, we want to pre-load all three data sources that are in “Tab 2”. The first tab is visible already at startup of the application, so we start a background processing job in “On Startup”:
APPLICATION.doBackgroundProcessing();
Note that unlike with “delayed loading”, starting
background processing is not combined with showing loading indicators, because
these components are not visible at that point in time.
In the background processing job, we load the data sources that have not been loaded yet. In addition, we pre-load the result set with the “getDataSelections” API:
CHART_UPPER.getDataSource().loadDataSource();
TABLE_LOWER_LEFT.getDataSource().loadDataSource();
TABLE_LOWER_RIGHT.getDataSource().loadDataSource();
CHART_UPPER.getDataSource().getDataSelections({});
TABLE_LOWER_LEFT.getDataSource().getDataSelections({});
TABLE_LOWER_RIGHT.getDataSource().getDataSelections({});
Note that for consistency and performance reasons the data source of the upper chart has also been marked as “Load In Script” (unlike in the “Delayed Loading” section, where it has been loaded right away).
With that implementation, switching to “Tab 2” will provide a very fast user experience, because no loading of data from BW or HANA is involved. Just a rendering of the data which has already been pre-loaded will take place.
The Downsides, Pitfalls, and Disadvantages of Pre-Loading
As we have seen previously, there is no “official” API for pre-loading data. This is for a good reason. In most cases best performance is achieved when Lumira can decide when to perform such lengthy operations as fetching result sets. Lumira can make internal optimizations such as combining result set requests along with other data that needs to be fetched from BW or HANA.
In addition, there is the danger with complex applications that script code which pre-loads data is accidentally called in flows where pre-loading wasn’t intended. That is especially relevant in applications that are highly modular and that structure their code using “Global Script Objects”.
But finally, the
most important aspects are these two interrelated ones:
- Delayed loading uses loading indicators, but pre-loading cannot give any feedback to the user that something is “currently working” in the application, because the affected components are not visible.
- Like with all background processing jobs, the user cannot interact with the application while pre-loading is running.
Because there is no “loading indicator”, which makes the user aware of the activity, users will assume that they can interact with the application, and when they do so, they will experience the application is not responding. They may get annoyed and think the application is buggy and unresponsive. This is exactly the opposite of what pre-loading aims for.
Simply put,
pre-loading can only be considered, when it is certain that the user will only “look at” the application while pre-loading takes place. If pre-loading takes some time, and the
user tries to interact with the application during that time, the user experience will be disappointing.
Summary / Conclusion
- Consider using “delayed loading” of data sources to improve user experience for applications that have large and long-loading data sources.
- Read the chapter “Working with Background Processing” of Lumira’s “Application Designer Guide” to familiarize yourself with more things that background processing can do to improve application performance:
https://help.sap.com/doc/851a17484c184c798e645e7e81d37e2f/2.3.2.0/en-US/lum_23SP02_ds_user_en.pdf
- Check out the official SAP note about “Lumira Performance” for in-depth resources about performance tuning in Lumira applications:
http://service.sap.com/sap/support/notes/1931691
- If you understood the pitfalls and impacts of pre-loading data sources, you might consider using this concept if it is appropriate for your use case.