Filed under: Best Practices, Core Updates
Comments: None
Some background
First of all, what is a lazy load and why do I care? The NexJ Framework read event is at the same time very forgiving as well as demanding. What it demands of you is that you tell it exactly what you want. There is no equivalent wildcard like what is commonly found SQL queries, ie. ‘select * from NJEntity’. The framework expects you to explicitly state what attributes you need via the ‘attributes’ argument to the read event, or to be more accurate, anything in the attributes, where or order by arguments.
For example, the following snippet of code will return the firstName and lastName for any Person with a last name of “Smith”, as well as the city and country attributes for any addresses in the addrs collection.
1 |
(Person'read '(firstName lastName (addrs city country)) '(= lastName "Smith") () () () ()) |
It will not return the initial or title of the person. The reason for this is to avoid developers asking for all of the attributes in an RPC call when they only need one or two attributes. The system becomes unnecessarily bloated if too many of these ‘*’ calls are made by default.
Note: if you want to learn more about Object’read, refer to https://community.nexj.com/uncategorized/2015/09/14/all-about-object-reads-2/
Now comes the forgiving part. In many languages, if you then asked for your Person.initials attribute you might get a null pointer exception. Instead, (person’initials) will transparently ‘lazy’ load the value with a call to the server. If you ask for it again, it knows it already has the initials and won’t make a call the server. This comes in very handy when dynamically exploring the business model.
I say that lazy loads are made transparently, because the developer won’t know that a lazy load has happened unless they are watching RPC, service, or database activity in the logs. And even then it is just activity – it isn’t shouting out “LAZY LOAD!”.
But it does comes with a cost – 2 calls instead of one – or worse. Let’s say you are filling a list with 200 items per page and you ask for the firstName and lastName attributes. Then you loop over each item and output their full name including initials. This will result in 201 RPC calls to the server and the database. If you had just asked for firstName, lastName and initials in the original call, it would only be 1 server round-trip.
A new log category to the rescue.
A new logger category has been recently introduced that makes it much easier to identify lazy loading of attributes on classes in the model. In order to capture the output in the logs, set the level on the nexj.core.runtime.Instance.load class to DEBUG (or fine if using WebSphere).
This feature is available when using plugin 11.0.442.0+ (default). It has also been backported to select NexJ Contact 5.1 and 6.1 maintenance branches, although in some cases a new plugin has not yet been built and released.
When enabled, you will see output similar to the following format:
1 2 3 4 |
Loading attribute(s) (sendEnabled) of Instance<SysObjectQueue, OID:1:V32:9F02608CAB044145B9660C207ACC9334, clean> nexj.core.util.StackTrace at SysObjectQueue.send(msgOrMsgs)$main(class:SysObjectQueue.send(msgOrMsgs)$main:21) ... |
In the scenario above, load of the sendEnabled attribute of SysObjectQueue has led to a lazy load of the instance. Alternatively, in cases where a lazy load is not caused by an attribute you will see output that looks like:
1 |
Loading instance Instance<EntityActRollup, OID:3:V32:303031562900408C9287AAD826C4B156, clean> |
What to do about lazy loads?
If you see excessive lazy load entries in the logs, what should you do? First, identify what is causing it, which should be clear from inspecting the logs. If it is a piece of code, in an event for example, just add the missing attributes to the initial read statement. If it is because you have a list portlet, that then loads a detail portlet, you can add the attributes that will be loaded in the detail in the list’s query property, which is a list of attributes to be included in the read query beyond the attributes bound to controls on the portlet.
Note that enabling the Performance Banner is another good way of tracking lazy loads through the UI.
I hope this was helpful,
Bryan