Characteristics of the Safe ObjectStore Implementation

Introduction

This document details the characteristics of the Safe ObjectStore Implementation, ObjectStoreSafeImpl. It describes how the implementation works, what characteristics this provides, how to use it for optimum performance, and when to use it.

How the implementation works

Simply put, the implementation is constructed with a reference to another valid ObjectStore. Whenever any of the methods of the implementation are called with a Query object, the Query is cloned and passed to the equivalent method in the other ObjectStore. Therefore, whenever a Query object is passed by the user to the implementation, a clone is made that can be treated independently of the user's Query (which may well be subsequently altered).

Obviously, it would be a very significant performance problem if every call to the database was made using a fresh clone of the Query. Apart from the overhead used to clone the Query objects, some ObjectStore implementations (such as ObjectStoreClient) cache Query objects, and give a significant performance difference between using a new Query on every operation and using the same Query every time. Therefore, it is recommended that the only method of the ObjectStoreSafeImpl that is called is ObjectStore.execute(Query), which returns a Results object. A Results object holds (among other things) a reference to an ObjectStore, and a reference to a Query object. The Results object returned by this implementation holds a reference to the underlying non-safe ObjectStore, with a reference to the clone of the original Query. Therefore, all operations performed on the Results object are handled without cloning the Query object. Methods have been added to the Results object that mirror most of the useful methods in the ObjectStore interface, but with the advantage that the Results object adds caching wherever appropriate.

Characteristics of the implementation

The general ObjectStore interface does not provide any guarantees of correct results if it is ever run with a Query object that is subsequently altered. For example, if a Query is constructed, and executed to produce Results object A, then modified and executed to produce Results object B, then A may exhibit characteristics of the query after modification, and B may exhibit characteristics of the query before modification.

The Safe ObjectStore Implementation guarantees that neither of these two problems can occur. It guarantees (assuming single-threaded operation) that the operation requested produces results consistent with the state of the Query object at the time the Query is passed to the ObjectStore. In the case of multi-threaded operation, the user must ensure that the Query object is not modified between the time the Query object is submitted to the ObjectStore, and the time that method of the ObjectStore returns.

The implementation produced a small computation overhead for each time a Query is submitted to it. However, if the user is careful to merely use the implementation to construct Results objects, and then access all data through those Results objects, then little overhead will be seen.

Recommended usage for best performance

  • Don't use ObjectStore.count(Query) or ObjectStore.estimate(Query) - instead, use ObjectStore.execute(Query) to get a Results object, and call size() and getInfo(). This will improve performance on all ObjectStore implementations because Results does some caching, but particularly ObjectStoreSafeImpl, because it will not have to clone the Query multiple times.
  • Try not to call ObjectStore.execute(Query) multiple times for a given unmodified Query object.

When to use the ObjectStoreSafeImpl

Generally, we recommend that you wrap any ObjectStore (except ObjectStoreSafeImpl) in an ObjectStoreSafeImpl. No functionality is lost, and a lot of confusion can be averted in one simple stroke. You should only not do this if you know for sure that the safety is not required.

If you are a remote user of a database service such as the InterMine database, then your underlying ObjectStore will be the ObjectStoreClient, which makes a connection to a server over a network connection. This implementation is particularly vulnerable to safety problems, so it is highly recommended that you wrap it in an ObjectStoreSafeImpl. However, it is then very vulnerable to performance problems if the end user calls ObjectStore.count() and ObjectStore.estimate(), or ObjectStore.execute() multiple times. Therefore, make sure you write your software to follow the guidelines above if it may use the ObjectStoreClient.

If you are writing software that generates Query objects from IQL (like our ObjectStoreServer - which is not an ObjectStore, but the server that ObjectStoreClient uses), then it is likely that it does not need the ObjectStoreSafeImpl.

If your software only runs a single Query, or if it is guaranteed to never modify a Query object once it has been handed to the ObjectStore, then it does not need the ObjectStoreSafeImpl.

Attachments