/**
* Copyright Plugtree LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.plugtree.solrmeter.model.executor;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.response.UpdateResponse;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.plugtree.solrmeter.model.InputDocumentExtractor;
import com.plugtree.solrmeter.model.SolrMeterConfiguration;
import com.plugtree.solrmeter.model.SolrServerRegistry;
import com.plugtree.solrmeter.model.UpdateExecutor;
import com.plugtree.solrmeter.model.UpdateStatistic;
import com.plugtree.solrmeter.model.exception.CommitException;
import com.plugtree.solrmeter.model.exception.UpdateException;
import com.plugtree.solrmeter.model.operation.CommitOperation;
import com.plugtree.solrmeter.model.operation.ConstantOperationExecutorThread;
import com.plugtree.solrmeter.model.operation.UpdateOperation;
import com.plugtree.stressTestScope.StressTestScope;
/**
* Executor that executes updates in a constant period of time, determined
* by the specified number of updates per minute.
* @see com.plugtree.solrmeter.model.operation.ConstantOperationExecutorThread
* @author tflobbe
*
*/
@StressTestScope
public class UpdateExecutorConstantImpl implements UpdateExecutor {
private final static Logger logger = Logger.getLogger(UpdateExecutorConstantImpl.class);
//TODO DI
private SolrServer server;
private Integer numberOfDocumentsBeforeCommit;
private Integer maxTimeBeforeCommit;
private List<UpdateStatistic> statistics;
protected boolean autocommit;
private int notCommitedDocuments;
private ConstantOperationExecutorThread commiterThread;
private InputDocumentExtractor documentExtractor;
private int operationsPerMinute;
private boolean running;
private ConstantOperationExecutorThread updateExecutorThread;
@Inject
public UpdateExecutorConstantImpl(@Named("updateExtractor") InputDocumentExtractor documentExtractor) {
super();
this.documentExtractor = documentExtractor;
statistics = new LinkedList<UpdateStatistic>();
operationsPerMinute = Integer.valueOf(SolrMeterConfiguration.getProperty(SolrMeterConfiguration.UPDATES_PER_SECOND)).intValue();
autocommit = Boolean.valueOf(SolrMeterConfiguration.getProperty("solr.update.solrAutocommit", "false"));;
maxTimeBeforeCommit = Integer.valueOf(SolrMeterConfiguration.getProperty("solr.update.timeToCommit", "10000"));
numberOfDocumentsBeforeCommit = Integer.valueOf(SolrMeterConfiguration.getProperty("solr.update.documentsToCommit", "100"));
}
public synchronized SolrServer getSolrServer() {
if(server == null) {
server = SolrServerRegistry.getSolrServer(SolrMeterConfiguration.getProperty(SolrMeterConfiguration.SOLR_ADD_URL));
}
return server;
}
private void prepareCommitter() {
if(commiterThread != null) {
commiterThread.destroy();
}
commiterThread = new ConstantOperationExecutorThread(new CommitOperation(this));
commiterThread.setTimeToWait(maxTimeBeforeCommit);
}
public void start() {
if(this.isRunning()) {
return;
}
updateExecutorThread = new ConstantOperationExecutorThread(new UpdateOperation(this, documentExtractor));
onOperationsPerMinuteChange();
updateExecutorThread.start();
if(!isAutocommit()) {
prepareCommitter();
commiterThread.start();
}
logger.info("Update Executor started");
}
public void stop() {
if(!this.isRunning()) {
return;
}
if(!isAutocommit()) {
commiterThread.destroy();
}
updateExecutorThread.destroy();
stopStatistics();
}
protected void stopStatistics() {
for(UpdateStatistic statistic:statistics) {
statistic.onFinishedTest();
}
}
public void notifyAddedDocument(UpdateResponse response) {
for(UpdateStatistic statistic:statistics) {
statistic.onAddedDocument(response);
}
if(!isAutocommit()) {
notCommitedDocuments++;//synchronize this?
if(notCommitedDocuments >= numberOfDocumentsBeforeCommit) {
logger.debug("Not Commited Docs is " + notCommitedDocuments + ". Waking commiter thread.");
commiterThread.wake();
}
}
}
public void notifyCommitSuccessfull(UpdateResponse response) {
notCommitedDocuments = 0;
for(UpdateStatistic statistic:statistics) {
statistic.onCommit(response);
}
}
public void notifyCommitError(CommitException exception) {
for(UpdateStatistic statistic:statistics) {
statistic.onCommitError(exception);
}
}
public void notifyUpdateError(UpdateException updateException) {
for(UpdateStatistic statistic:statistics) {
statistic.onAddError(updateException);
}
}
public void addStatistic(UpdateStatistic statistic) {
this.statistics.add(statistic);
}
public int getNotCommitedDocuments() {
return notCommitedDocuments;
}
public Integer getNumberOfDocumentsBeforeCommit() {
return numberOfDocumentsBeforeCommit;
}
public void setMaxTimeBeforeCommit(Integer value) {
if(value <= 0) {
throw new RuntimeException("Time before commit can't be 0");
}
maxTimeBeforeCommit = value;
SolrMeterConfiguration.setProperty("solr.update.timeToCommit", String.valueOf(maxTimeBeforeCommit));
if(commiterThread != null) {
commiterThread.setTimeToWait(value);
}
}
public Integer getMaxTimeBeforeCommit() {
return maxTimeBeforeCommit;
}
public boolean isAutocommit() {
return autocommit;
}
public Integer getUpdatesPerMinute() {
return this.operationsPerMinute;
}
public boolean isRunning() {
return running;
}
private void onOperationsPerMinuteChange() {
SolrMeterConfiguration.setProperty("solr.load.updatesperminute", String.valueOf(operationsPerMinute));
if(this.updateExecutorThread != null) {
this.updateExecutorThread.setTimeToWait(60000/operationsPerMinute);
}
}
@Override
public void setOperationsPerSecond(int value) {
if (operationsPerMinute < 1) {
throw new IllegalArgumentException("Invalid number of operations per second: " + value);
}
this.operationsPerMinute = value;
onOperationsPerMinuteChange();
}
@Override
public void setNumberOfDocumentsBeforeCommit(int value) {
if (value == Integer.MAX_VALUE) {
throw new IllegalArgumentException("Number of documents before commit can't be more than " + Integer.MAX_VALUE);
}
if (value < 0) {
throw new IllegalArgumentException("Number of documents before commit can't be less than 0");
}
numberOfDocumentsBeforeCommit= value;
SolrMeterConfiguration.setProperty("solr.update.documentsToCommit", String.valueOf(numberOfDocumentsBeforeCommit));
}
}