One of the component types that index managers coordinate are workers, which are responsible for the actual updates made to a Lucene index.
If you are using Lucene and Hibernate Search in a clustered environment, many of the configuration options are set at the worker level. We will explore those more fully in Chapter 7, Advanced Performance Strategies. However, three key configuration options are available in any environment.
By default, workers perform Lucene updates synchronously. That is, once an update begins, execution of the main thread is blocked until that update completes.
Workers may instead be configured to update asynchronously, a "fire and forget" mode that spawns a separate thread to perform the work. The advantages are that the main thread will be more responsive, and the workload handled more efficiently. The downside is that the database and the index may be out of sync for very brief periods.
Execution mode is declared in hibernate.cfg.xml
(or persistence.xml
for JPA). A global default may be established with the default
substring, and per-entity configurations may be set with the entity index name (for example, App
):
... <property name="hibernate.search.default.worker.execution"> sync </property> <property name="hibernate.search.App.worker.execution"> async </property> ...
By default workers perform updates in only one thread, either the main thread in the synchronous mode, or a single spawned thread in the asynchronous mode. However, you have the option of creating a larger pool of threads to handle the work. The pool may apply at the global default level, or be specific to a particular index:
... <property name="hibernate.search.default.worker.thread_pool.size"> 2 </property> <property name="hibernate.search.App.worker.thread_pool.size"> 5 </property> ...
Pending work gets backed up in a queue, waiting for a thread to free up and deal with it. By default, the size of this buffer is infinite, at least in theory. In reality, it is bound by the amount of system memory available, and an OutOfMemoryExeception
may be thrown if the buffer grows too large.
Therefore, it is a good idea to place some limit, globally or on a per-index basis, for the size to which these buffer can grow.
... <property name="hibernate.search.default.worker.buffer_queue.max"> 50 </property> <property name="hibernate.search.App.worker.buffer_queue.max"> 250 </property> ...
When a buffer reaches the maximum allowable size for its index, additional operations will be performed by the thread which creates them. This blocks execution and slows down performance, but ensures that the application will not run out of memory. Experiment to find a balanced threshold for an application.