/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.elasticsearch.processor.impl;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.search.backend.IndexingMonitor;
import org.hibernate.search.elasticsearch.client.impl.ElasticsearchClient;
import org.hibernate.search.elasticsearch.client.impl.URLEncodedString;
import org.hibernate.search.elasticsearch.gson.impl.GsonProvider;
import org.hibernate.search.elasticsearch.logging.impl.Log;
import org.hibernate.search.elasticsearch.work.impl.ElasticsearchWork;
import org.hibernate.search.elasticsearch.work.impl.ElasticsearchWorkExecutionContext;
import org.hibernate.search.elasticsearch.work.impl.builder.RefreshWorkBuilder;
import org.hibernate.search.elasticsearch.work.impl.factory.ElasticsearchWorkFactory;
import org.hibernate.search.exception.ErrorHandler;
import org.hibernate.search.util.logging.impl.LoggerFactory;
/**
* The execution context used in {@link ElasticsearchWorkProcessor}
* when multiple works are executed one after another.
* <p>
* This context is mutable and is not thread-safe.
*
* @author Yoann Rodiere
*/
class SequentialWorkExecutionContext implements ElasticsearchWorkExecutionContext {
private static final Log log = LoggerFactory.make( Log.class );
private final ElasticsearchClient client;
private final GsonProvider gsonProvider;
private final ElasticsearchWorkFactory workFactory;
private final ElasticsearchWorkProcessor workProcessor;
private final ErrorHandler errorHandler;
/*
* We use buffers to avoid too many calls to the actual index monitor, which is potentially synchronized and hence
* may be a contention point.
*/
private final Map<IndexingMonitor, BufferedIndexingMonitor> bufferedIndexMonitors = new HashMap<>();
private final Set<URLEncodedString> dirtyIndexes = new HashSet<>();
public SequentialWorkExecutionContext(ElasticsearchClient client,
GsonProvider gsonProvider, ElasticsearchWorkFactory workFactory,
ElasticsearchWorkProcessor workProcessor,
ErrorHandler errorHandler) {
super();
this.client = client;
this.gsonProvider = gsonProvider;
this.workFactory = workFactory;
this.workProcessor = workProcessor;
this.errorHandler = errorHandler;
}
@Override
public ElasticsearchClient getClient() {
return client;
}
@Override
public GsonProvider getGsonProvider() {
return gsonProvider;
}
@Override
public void setIndexDirty(URLEncodedString indexName) {
dirtyIndexes.add( indexName );
}
@Override
public IndexingMonitor getBufferedIndexingMonitor(IndexingMonitor originalMonitor) {
return bufferedIndexMonitors.computeIfAbsent( originalMonitor, BufferedIndexingMonitor::new );
}
public void flush() {
// Refresh dirty indexes
if ( !dirtyIndexes.isEmpty() ) {
refreshDirtyIndexes();
dirtyIndexes.clear();
}
// Flush the indexing monitors
for ( BufferedIndexingMonitor buffer : bufferedIndexMonitors.values() ) {
try {
buffer.flush();
}
catch (RuntimeException e) {
errorHandler.handleException( "Flushing an indexing monitor failed", e );
}
}
bufferedIndexMonitors.clear();
}
private void refreshDirtyIndexes() {
if ( log.isTraceEnabled() ) {
log.tracef( "Refreshing index(es) %s", dirtyIndexes );
}
RefreshWorkBuilder builder = workFactory.refresh();
for ( URLEncodedString index : dirtyIndexes ) {
builder.index( index );
}
ElasticsearchWork<?> work = builder.build();
try {
workProcessor.executeSyncUnsafe( work );
}
catch (RuntimeException e) {
errorHandler.handleException( "Refresh failed", e );
}
}
private static final class BufferedIndexingMonitor implements IndexingMonitor {
private final IndexingMonitor delegate;
private long documentsAdded = 0L;
public BufferedIndexingMonitor(IndexingMonitor delegate) {
super();
this.delegate = delegate;
}
@Override
public void documentsAdded(long increment) {
documentsAdded += increment;
}
private void flush() {
delegate.documentsAdded( documentsAdded );
documentsAdded = 0L;
}
}
}