Chapter 12. Optimization Techniques

There are numerous techniques the developer can use in order to ensure that Kodo operates in the fastest and most efficient manner. Following is a list of guidelines, in the approximate order of importance:

  1. Database indices: Indices created by Kodo's schematool may not always be the more appropriate for your application. Manually manipulating indices to include frequently-queried fields (as well as dropping indices on rarely-queried fields) can yield significant performance benefits.

  2. Use the best JDBC Driver: The JDBC driver provided by the database vendor is not always the fastest and most efficient. Some JDBC drivers do not support features like batched statements, the lack of which can significantly slow down Kodo's data access.

  3. JVM optimizations: Manipulating various parameters of the Java Virtual Machine (such as hotspot compilation modes and the maximum memory) can result in performance improvements. For more details about optimizing the JVM execution environment, please see http://java.sun.com/docs/hotspot/PerformanceFAQ.html.

  4. Use the data cache: Using the Kodo Data Cache feature (available as a separate product) can often result in a dramatic improvement in performance. See the DataStore Cache chapter for more details.

  5. Disable logging, performance tracking, SynchronizeSchema: Developer options such as logging, the PerformanceTracker, and using the SynchronizeSchema developer option will result in serious performance hits for your application. Before evaluating any Kodo performance, these options should all be disabled.

  6. Ensure that batch updates are available: When performing bulk inserts, updates, or delete, Kodo will use batched statements. If this feature is not available in your JDBC driver, then Kodo will need to issue multiple SQL statements instead of a single batch statement.

  7. Use single-table inheritance: Using a single-table inheritance model is faster for most operations than a multi-table inheritance model. If it is appropriate for your application, you should use the single-table inheritance model whenever possible.

  8. Use unordered Sets instead of Lists: There is extra overhead for Kodo to maintain ordered Collections (either as relations or privately-owned Collections). If your application does not require ordering for a relation or Collection, you should always use a HashSet as opposed to a LinkedList, ArrayList, or SortedSet.

  9. High increment in DBSequenceFactory: For applications that perform large bulk inserts, a bottleneck can be the retrieval of sequence numbers. Incrementing the value of the Increment parameter of the com.solarmetric.kodo.impl.jdbc.SequenceFactoryProperties property can result in this bottleneck being reduced. In some cases, implementing your own SequenceFactory can be used to optimize sequence number retrieval.

  10. Use optimistic transactions: Using datastore transactions translates into pessimistic database row locking, which can be a performance hit (depending on the database). If appropriate for your application, using optimistic transactions are typically faster than using datastore transactions.

  11. Perform nontransactional data reads outside a transaction.

  12. Always close PersistenceManagers and Query results: It is important to bear in mind that a PersistenceManager and the result from a Query are often backed by resources in the database. For example, if a Query result has not been completely instantiated, it will hold open a ResultSet object, which, in turn, will hold open a Statement object (preventing it from being re-used). Garbage collection will clean up these resources, so it is never necessary to explicitely close these resources, but it is always faster if it is done at the application level.

    Example 12.1. Explicitly closing resources

    public int getPersonCount (String jdoql)
    {
      PersistenceManagerFactory factory = ...; // obtain a PersistenceManagerFactory
      PersistenceManager pm = factory.getPersistenceManager ();
      try
      {
        Query query = pm.newQuery (Person.class, jdoql);
        try
        {
          return ((Collection)query.execute ()).size ();
        }
        finally
        {
          // close all results from this query
          query.closeAll (); 
        }
      }
      finally
      {
        // close the PersistenceManager and any associated resources
        pm.close (); 
      }
    }
    
    

  13. Optimize connection pool settings: Kodo's built-in connection pool's default settings may not be optimal for all applications. For applications that instantiate and close many PersistenceManagers (such as a web application), increasing the size of the connection pool will reduce the overhead of waiting on free connections or opening new connections.

  14. Utilize the PM cache: When possible and appropriate, re-using PersistenceManager objects will result in huge performance gains, since the PersistenceManager's built-in object cache will be used.

  15. Enable Multithreaded operation only when necessary: Kodo respects the javax.jdo.option.Multithreaded option in that it does not impose synchronization overhead for applications that set this value to false. If your application is guaranteed to be single-threaded, setting this option to false will result in the elimination synchronization overhead, and may result in a modest performance increase.

  16. Disable large data set handling: By default, Kodo JDO creates statements with the ResultSet.TYPE_SCROLL_INSENSITIVE flag. On some databases (SQLServer for example), result sets that support bidirectional scrolling are much slower than unidirectional result sets. So, if you do not have lots of data or your application always fully traverses large data sets, then you should disable large data set handling by setting the DefaultFetchThreshold property to -1. A future release of Kodo JDO will allow unidirectional lazily loaded result lists, which will permit lazy materialization of large result sets without incurring the penalty of using TYPE_SCROLL_INSENSITIVE result sets.

  17. Develop a custom SubclassProvider, use the com.solarmetric.kodo.impl.jdbc.ormapping.IntegerSubclassProvider, or turn off the subclass indicator column. Kodo JDO's default subclass provider is quite robust, in that it can handle any class and needs no configuration, but the downside of this robustness is that it puts a relatively lengthy string into each row of the database. With the IntegerSubclassProvider provider, a little application-specific configuration, you could easily reduce this to an integer. This can result in significant performance gains when dealing with many small objects, since the subclass indicator data can become a significant proportion of the data transferred between the JVM and the database.

    Alternately, if your application does not make use of inheritance, then you can disable the subclass provider column altogether.

  18. Set the com.solarmetric.kodo.CacheReferenceSize property to -1: Setting this property to -1 will cause the PersistenceManager to maintain hard references to all objects loaded through that PM, causing potential memory issues. However, it will no longer be necessary to maintain any ordering in this cache, so systems that load lots of objects may see some performance improvements.