Burst Load Modes
Nando Dessena, 19/09/2010


What is burst load
------------------

Burst Load Modes are alternative ways of retrieving objects in InstantObjects' SQL brokers. These new modes can be selected on a case by case basis by setting the new RequestedLoadMode property of an InstantSelector or InstantQuery to the value lmPartialBurst or lmFullBurst. The value lmKeysFirst means the standard, historical load mode.


What does it do
---------------

When IO retrieves a set of objects through a SQL broker, the order of operations is as follows:
1) Execute a select statement (we'll call it the primary query) that retrieves the primary keys of all objects selected by the IQL command.
2) Fetch all records (up to MaxCount).
3) For each record accessed, materialize the object. This implies executing one or more select statements to get all object data (main object query), containers and references (accessory queries). External storage implies more queries. The main query joins all the tables for the given class and ancestors.

Full Burst mode causes these changes:
1) The primary query retrieves all object data, and not just the primary keys.
2) lmFullBurst: All objects are materialized (up to MaxCount) from the obtained data set; this causes the execution of all accessory and external queries that in the standard case are executed at point 3, minus the main query, which is not needed anymore. lmPartialBurst: objects are materialized on demand, like in the standard mode, but using data retrieved by the primary query, like in the full burst mode.

Partial Burst mode works this way:
1) Like Full Burst mode 1).
2) Like standard mode 2).
3) Like standard mode 3), but using data retrieved by the primary query and kept around.

This means that Full Burst mode is much quicker when retrieving a whole dataset, whereas standard mode is best at getting the first records. Partial Burst mode gives the best of both worlds, as it is almost as quick as Full Burst when fetching an entire dataset, and slightly quicker than standard mode at getting the first records.


When to use burst load
---------------------- 

Full Burst mode is well suited for all cases in which objects are selected (typically through an IQL command with a WHERE clause) to be processed. If the entire dataset is going to be fetched and all records visited anyway, then it's much quicker to do it in Full Burst mode.

The standard mode is more of the "lazy load" kind, and as such it is more suited for when a list of objects is displayed for browsing, typically in a DBGrid, and not all objects are needed but just one or a few are selected to work with. In this case displaying the first records in the grid is much quicker in standard mode.

Partial Burst Mode should cover both cases above, and as such is the preferred mode. The only glitch about Partial Burst mode is that it keeps the database connection open for longer than Full Burst mode. If that is a concern, then Full Burst mode should be used instead.


Performance notes
-----------------

Here are some quick benchmarks done with one of the example queries of PrimerExternal and a dataset with a couple thousand records.
The database is a local Firebird server and the compiler is Delphi XE. All caches were flushed at each iteration. Three iterations for each test (average value reported). Times in seconds.

select * from TCompany

No fetch, just open an InstantSelector and fetch some 20 objects for the DBGrid.

KF     PB     FB
0,9    0,8    36,1

Fetch all records and materialize all objects (while not Eof do Next).

KF     PB     FB
65,5   45,4   45,4

Same as above, but with statement cache enabled.

KF     PB     FB
38,6   28,9  29,5

This confirms that using Full Burst mode when not appropriate can hurt performance, which suggests to keep it disabled by default and only enable it on request. On the other hand, Partial Burst mode appears suitable for most cases. 

Caveats and future improvements
-------------------------------

- Currently Burst Load modes are not supported for IQL commands using the ANY keyword. Doing so will require some significant refactorings and was postponed. The system will silently fall back to a supported mode, if the requested mode is not supported by the actual query. You can tell by inspecting the value of the ActualLoadMode property.

- External atPart and all atReference attributes still need separate queries. It is difficult but not impossible to get them in the first query as well, thus reducing fetch time even more.  

- A mixture of standard and Burst modes looks interesting, and the work done on Burst modes makes it easier to implement it. This mixture would be a standard mode in which one or more specified attributes are fetched as well as the primary keys in the main query. This would allow to display a list of objects in a DBGrid, or other multi-record control, without materializing them, as quickly as when doing direct SQL queries. Thought should be given to design details such as when where and how to specify the attributes that should be loaded together with the primary key, and an attribute-level mechanism to signal incomplete vs complete data would probably be needed.
    