
Optimizing the use of data and service calls
Server calls are expensive. Optimizing the number of calls and the frequency of those calls often provides good performance returns. Not only does a service call require client resources, it also calls the server. Excessive refreshing of datasets can cripple the usability of an application. Let's explore how the application will use services and how we can optimize that usage.
Developers who haven't been watching the Network tab in Developer Tools can be astonished by the sheer volume of server calls their application is making. Requiring many service calls to initialize the application can significantly increase the duration it takes the application to be ready for the user. Optimizing the use of service calls is one of the other significant performance knobs developers have at their disposal.
As the application starts to come together, make a point to inventory the JSON network calls. Can you account for each and every one? Yes, I said every one. Do you understand why it was needed? How large is the dataset that is being returned? Was the call really needed at that time? Let's look at some common issues.
Beware of autoUpdate
The autoUpdate option is easy to use, but can often be the biggest performance drag on applications that use it. Let's see why.
Service variables can be invoked in the following different ways:
- Having its
update
function called from code. - Selecting the service call from an event dropdown. This is the code-free equivalent of calling
update()
. - Lazy loading of related data model entities by grids.
- Using the startUpdate option. This fires the service call when the page loads for the first time.
- Using the autoUpdate option. This fires the service call whenever any of its inputs change.
The autoUpdate option is too easy to use. You check the box and you get the data. If any bound input to the service call changes, such as an input to a query, the service call fires. Call setValue()
from the code on an input and it also fires. AutoUpdate really means update (fire, or trigger the call) whenever any inputs change. It's very simple and easy.
However, don't use it. Trigger every service variable from the events and code instead. Often the use of autoUpdate results in the service variable firing too often. This is a performance drag on client, server, and the network. Don't use autoUpdate in anything but trivial applications and avoid having to untangle a mess later.
A chain of service variables is a common pattern in AJAX applications. The onSuccess
event of one service call invokes another service call that is dependent upon data from the first. For example, selecting a value in one select menu can often filter the options presented in another select menu. Likewise, selecting a row in a grid often triggers other service calls filtered for the selected item. With autoUpdate, these chains can get long and messy. Pretty soon, you would have lost control of your network utilization. The application is making dozens of service calls whenever values change, and it is difficult to know what is what anymore. Sometimes, service variables fire multiple times for no known reason.
Instead, invoke each service variable directly. Use the onchange
events of components to invoke service calls instead of using the autoUpdate feature. Let binding update the input parameters to the service variable, but invoke the service variable yourself.

AutoUpdate can also result in the user seeing incorrect data. By default, when a service variable is triggered, such as by a bound value change event, and the service variable is still "in flight" from the last time it was triggered, the second call is ignored. This means that the data returned to the user was never updated to the most current inputs. In WaveMaker 6.5, service calls now have an inFlightBehavior property that will queue the last request for execution and another that queues all in-flight calls. The executeLast option is good for read operations. The executeAll option should be used judiciously. If the user is likely to be doing volume operations often, consider adding a bulk operation Java service instead. Consider the application with a common usage pattern of marking multiple items as resolved. Instead of selecting X items to be marked as resolved and calling an update X times, send an array of items to be resolved to a single server-side function. In that function, iterate over the items and mark them as resolved. We'll discuss creating such Java services in Chapter 9, Custom Java Services.
Controlling the result set size
Service variables have a property that specifies the maximum number of rows returned by the call. It is called maxResults
and defaults to 500
. The other maximum results property, DesignMaxResults
, applies only to design time, or when data is being loaded into the application while working in Studio.
The maxResults
property prevents thousands upon thousands of rows from being returned and overwhelming the browser. Consider a read operation on a table containing hundreds of thousands of entries. Even an accidental unfiltered read on such a table could leave the most modern browser struggling to manage such a dataset. Combined with the firstRow
property, large datasets can be paginated into the client. By advancing the firstrow
property to the next row to be returned and triggering the service call, data can be brought into the browser in chunks. The DataNavigator
component is a widget available on the palette to easily paginate data from live variables. Pagination with service variable data is possible, but requires exposing parameters to control pagination in the service methods.
While reducing the size of result sets is generally good for performance, sometimes a slightly larger result set is better overall. Let's say an application uses a table listing the 193 member nations of the United Nations. Using a service variable with a maxResults
value of anything less than 193 means that all results are not returned in the single read call. This may be desirable if the results are segmented somehow, but if the goal is to have a single list from which users can choose a member nation, populating the list in a single call is more efficient.
Client-side querying of variable results
Another tool in optimizing service usage is the client-side query mechanism in wm.Variable
. The Variable.query()
function can be performed on a wm.Variable
that contains a list of data and supports greater than, less than, not, and wildcard searches. The query returns a new wm.Variable
containing the results that match the specified query.
Client-side variable querying enables developers to avoid making calls to the server to filter moderate-sized datasets. This is particularly well suited for datasets that are not subject to change while the application is in use. Our United Nations member nations use case is a good example, as new members being added to the U.N. are extremely unlikely while the user has the dataset loaded.
More information about variable querying can be found at http://dev.wavemaker.com/wiki/bin/wmjsref_6.5/Variable_query.
Live views and related data
It is important to understand that live views return all columns of a related table, regardless of whether those columns are used or not. This can increase the size of the dataset returned significantly. We'll discuss live views further in Chapter 7, Working with Databases. For application planning, it is important to remember that all columns of the related table are always returned. A related table containing a blob, for example, is better retrieved using an HQL query in which we have specific control over the columns returned.
Lazy loading
Lazy loading is an alternative to live views and retrieve related data. When using database data with relations, WaveMaker will just-in-time fetch related data if needed. For example, if our employee live variable does not include the department information in its view, any request for department information will require a synchronous lazy load. Yuck! This enables the call to return a result as expected; however, the entire application stops until the call returns as a result.
In WaveMaker 6.5, dojoGrid
does not lazy load. Adding a custom column such as ${deparment.departmentname}
to the employee grid will provide only an empty column. The Lazy Loading tab of WaveyWeb loads PageLazy
. There is a button on PageLazy
that takes the selected item index and requests the department of the selected item from the live variable that populated the grid. This is a shortcut to give an example of lazy loading, and is not something we would normally do because we are working across two datasets. If the user sorts the grid, the selected item index will no longer match the index of that employee in the live variable. The following is the code for the Get Department button:
this.employeeLiveVariable1.getItem(this.employeeDojoGrid.getSelectedIndex()).getValue("department");
We have combined two calls. The first call, this.employeeDojoGrid.getSelectedIndex()
, returns the integer index of the grid selected item. The second call, this.employeeLiveVariable1.getItem(n).getValue("department")
, gets the nth item from the employee live variable and then fetches the value of its department. In our case, n is the selected item index. As the employee live variable does not include the department in its live view, each call to a different n results in a lazy load. Watch for yourself in the Network tab. Each click of Get Department for a new selected item results in a runtimeService
JSON call, filtered by a full employee. Lazy loading uses the full item as a filter, even when just a Primary Key would have been sufficient. This causes more data than needed to be sent in the request.
Lazy loading reduces the initial loading of the dataset at the cost of incurring synchronous reads when related data is required. WaveMaker does this for us, and most of the time, it is a good thing; however, for some models, fetching the related data as part of the initial read may be the more efficient pattern. More information on lazy loading can be found at http://dev.wavemaker.com/wiki/bin/wmdoc_6.5/LazyLoading.
Related editors
Another source of service calls is related editors. Related editors have an autoDataSet
property. By default, this is enabled, and means that each related editor has its own live variable and dataset. Clearing the autoDataSet
property shows the dataSet
property, enabling us to specify the related editor's dataset. The auto dataset feature is convenient, but when we have lookup editors on large datasets that can be filtered down, it is often more efficient to provide our own dataset. Providing our own dataset also lets us control when the dataset is refreshed. Again, in our United Nations example, we don't need to refresh the member nation list during the application's use.