Monday, May 2, 2016

Never use long living database instances

Every user who works with OrientDB knows that to manipulate records he or she has to create ODatabaseDocumetTX an instance first. But there is a wide misunderstanding about the meaning of this object. Many users think that this object may be treated as an abstraction of data which are stored on disk and placed on a server. As a result, they create a single instance of this object per thread, assign it to the class field and use the same object during whole application life. That is “not performance” wise decision. There are several reasons why you should consider using short living database instance acquired from the pool for example from OPartitionedDatabasePool instead.

The most important reason is that during loading of records we put records into the local cache which is based on WeakHashMap. We need this cache for 2 reasons:
  1. To avoid OConcurrentModificationException in the case of loading of the same record in a call stack.
  2. To speed up graph traversal.
Let's look how WeakHashMap works (in OpenJDK at least):
  1. Every put/get/remove method of this HashMap calls getTable() method.
  2. Which in turn calls the expungeStaleEntries() .
The responsibility of the last method is to remove weak references which are already not accessible. expungeStaleEntries() use ReferenceQueue which was passed during the creation of WeakReference to detect unreachable references.

The main problem there is mechanics which is used to fill in and pull items from reference queue. When weak reference is becoming a subject of garbage collection special thread which has high priority java.lang.ref.Reference.ReferenceHandler put such reference into reference queue. But reference queue itself is guarded by object wide lock !

Let’s put all of this together:
  1. You have long living ODatabaseDocumetTX instance.
  2. You use it to load many short living record objects.
  3. You have WeakHashMap polluted by many WeakReferences.
  4. After next GC run ReferenceHandler starts to pull and lock ReferenceQueue and as result lock WeakHashMap objects.
  5. Your threads become frozen for a long time. We know situations when threads were frozen for several seconds !
So the main rule of thumb - never use long living ODatabaseDocumetTX instances, use database pool instead. Despite solving the problem described above pool also provides support for nested transactions.

P.S. In 3.0 version we are going to implement WeakHashMap which will be based on Hopscotch hashing algorithm and will not suffer from synchronization problem described above.