Beginning with the R8.52 release of Notes/Domino there is a clear performance winner in the race to enumerate data from a View using the Backend View related classes. Significant performance work has been done on the
ViewNavigator class to allow it perform well enough to serve as the underpinnings for XPage screen display. You can gain the benefits of these enhancements for your application whether it is written in Java, LotusScript, or JavaScript.
The Backend
ViewNavigator cache reduces the number of server transactions and associated network overhead when navigating and reading ColumnValues information from the Documents and Entries in a View. Performance gains are most profound when accessing a View residing on a server from a client, however retrieval from local Views will also be greatly improved.
The cache does not change any behaviors, i.e.
ViewNavigator and
ViewEntry methods will behave the same with or without an enabled cache. If what you need is not in the cache, it will be fetched in another transaction. Care should be used in configuring the cache so as not to incur additional costs of reading entries twice.
There are 4 prerequisites to use the ViewNavigator cache for rapid data access:
1. Notes/Domino Release 8.52 or greater
2.
View.setAutoUpdate must be False
3.
ViewNavigator cache must be enabled
4.
ViewNavigator.getNext() (or
getPrev) must be used
The
AutoUpdate property of
View is an instruction to the Backend classes on how to manage updates to a View collection. The
AutoUpdate property of View is true by default. This means that the Backend will pay attention to "View collection has changed" signals from the core during method execution. In this case, the method must be restarted, which can be time consuming on a busy View and will negatively impact performance. In these cases, setting
AutoUpdate = true is a very worthwhile optimization in its own right.
There are some conditions which must be met before you can set
AutoUpdate = false. This is, in effect, saying to the backend "it is safe to ignore updates to my View because the collection order won't change". For example, since ViewNavigator is a sorted View, if it is sorted on date ascending, all new Documents will occur at the end of the collection. Therefore, the collection order is "safe" from those updates. Also, deletions during normal running hours can be marked and deferred to off hours, preserving collection order during high access periods. There is a much more detailed discussion of this here:
http://www-10.lotus.com/ldd/ddwiki.nsf/dx/View.AutoUpdate
To utilize the cache, AutoUpdate must be set to false.
To enable the cache, call
ViewNavigator.setBufferMaxEntries( int numentries); (Java shown)
where numentries is an integer between 2 and 400. This is the number of entries tthat will be retrieved whenever the cache is populated. If you intend to read out all ViewEntries in the View, use the maximum of 400. However, if you are just interested in a screenload, as XPages might be, use something like 25. There is no point in buffering more entries than you need. The cache automatically refills itself as needed - tuning the cache for maximum performance is as much art as science, and will depend on the runtime characteristics of your application.
Other optimizations
Depending on exactly what your application needs to do, other optimizations can be made. These are available on the ViewNavigator under the EntryOptions property. This property currently accepts two constants:
VN_ENTRYOPT_NOCOUNTDATA - do not count children (this can be expensive on some Views)
VN_ENTRYOPT_NOCOLUMNVALUES - do not gather ColumnValues data
Example:
ViewNavigator.setEntryOptions(lotus.domino.ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);
This would tell core services not to bother counting children, which may be faster on Views populated with lots of child Documents. Note that even though the child count is not buffered in the cache, if the program later calls
ViewEntry.getCount(), the children will be counted in another transaction, which may be counterproductive.
The same would be true for
VN_ENTRYOPT_NOCOLUMNVALUES. Setting this option would cause the ColumnValues data to not be stored in the cache, so any calls to
ViewEntry.getColumnValues() would cause another transaction to fetch ColumnValues data. Again, this may or may not be a counterproductive situation. One case where it could be beneficial might be if you are only interested in the ColumnValues of one or two entries in a collection. In that case, it may pay to ignore them for all cached entries.
Examples:
Here is a LotusScript example which uses the cache to pull ColumnValues out of all entries in a View in the fastest possible way: Running against a local Database on R8.52, this script runs many times faster with the cache set to 400 vs the cache set to 1 (cache disabled). Additional gains will be seen running on a remote Database on a busy server.
Dim view As NotesView
Dim db As NotesDatabase
Dim s As New NotesSession
Dim nav As NotesViewNavigator
Dim ve As NotesViewEntry
Set db = s.CurrentDatabase
Set view = db.getView("$All")
Set nav = view.createViewNav()
' do not do AutoUpdates
view.AutoUpdate = False
' create a ViewNavigator
Set nav = view.createViewNav()
' enable cache for max buffering
nav.BufferMaxEntries = 400
Msgbox "View: " + view.Name + " 10000 entries, Cache: " + Cstr(nav.BufferMaxEntries)
' if we are not interested in the number of children, we can go a little faster
nav.EntryOptions = Vn_entryopt_nocountdata
' System.out.println("Start: " + dt);
tstr = "Start: " + Cstr(Now)
Dim i As Integer
i = 0
Set ve = nav.GetFirst
Dim vstr As String
While ( i< 10000 )
vstr = ve.ColumnValues(0)
Set ve=nav.getNext(ve)
i = i + 1
Wend
Msgbox tstr + " Finish: " + Cstr(Now)
Below is the same test, written in Java, yielding the same before/after results:
import java.util.*;
import lotus.domino.*;
public class JavaAgent extends AgentBase {
public void NotesMain() {
try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
// (Your code goes here)
Database db = agentContext.getCurrentDatabase();
View view = db.getView("$All");
// ignore updates
view.setAutoUpdate(false);
// create a ViewNavigator
ViewNavigator nav = view.createViewNav();
// enable cache for max buffering
nav.setBufferMaxEntries(400);
System.out.println("view: " + view.getName() + " 10000 entries, Cache: " + nav.getBufferMaxEntries());
// if we are not interested in the number of children, we can go a little faster
nav.setEntryOptions(ViewNavigator.VN_ENTRYOPT_NOCOUNTDATA);
Date sdate = new Date();
String vstr;
ViewEntry tmpve;
ViewEntry ve = nav.getFirst();
for (int i = 0; i < 10000; i++) {
vstr = ve.getColumnValues().elementAt(0).toString();
// System.out.println(i + ": " + vstr);
tmpve = nav.getNext(ve);
ve.recycle();
ve = tmpve;
}
Date edate = new Date();
System.out.println( "Start: " + sdate + " Finish: " + edate);
} catch(Exception e) {
e.printStackTrace();
}
}
}
In R8.52 the ViewNavigator cache can make a significant contribution to improving performance of your applications.