Skip to content

Commit

Permalink
HSEARCH-615 expose ways to limit results based on time
Browse files Browse the repository at this point in the history
For now, setTimeout and fetching limit are mutually exclusive
  • Loading branch information
emmanuelbernard committed Nov 5, 2010
1 parent 6ad2ba2 commit 6454e7d
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 19 deletions.
Expand Up @@ -153,4 +153,29 @@ public interface FullTextQuery extends Query, ProjectionConstants {
*
*/
FullTextQuery setTimeout(long timeout, TimeUnit timeUnit);

/**
* *Experimental* API, subject to change or removal
*
* Limit the time used by Hibernate Search to execute the query. When the limit is reached, results already
* fetched are returned. This time limit is a best effort. The query will likely run for longer than the
* provided time.
*
* The time limit only applies to the interactions between Hibernate Search and Lucene. In other words,
* a query to the database will not be limited.
*
* If the limit is reached and all results are not yet fetched, {@link #hasPartialResults()} returns true.
*
* @param timeout time out period
* @param timeUnit time out unit
*/
FullTextQuery limitFetchingTime(long timeout, TimeUnit timeUnit);

/**
* *Experimental* API, subject to change or removal
*
* When using {@link #limitFetchingTime(long, java.util.concurrent.TimeUnit)} }, returns true if partial results are returned (ie if the time limit has been reached
* and the result fetching process has been terminated.
*/
boolean hasPartialResults();
}
Expand Up @@ -103,7 +103,7 @@ public static void initializeObjects(EntityInfo[] entityInfos,
}
criteria.add( disjunction );
//not best effort so fail fast
if ( ! timeoutManager.isBestEffort() ) {
if ( timeoutManager.getType() != TimeoutManager.Type.LIMIT ) {
final Long timeLeftInSecond = timeoutManager.getTimeoutLeftInSeconds();
if ( timeLeftInSecond != null ) {
criteria.setTimeout( timeLeftInSecond.intValue() );
Expand Down
Expand Up @@ -216,13 +216,7 @@ public Iterator iterate() throws HibernateException {
}

private void reactOnQueryTimeoutExceptionWhileExtracting(QueryTimeoutException e) {
if ( timeoutManager.isBestEffort() ) {
//we stop where we are return what we have
//TODO expose timeout exceeded
}
else {
throw e;
}
timeoutManager.reactOnQueryTimeoutExceptionWhileExtracting(e);
}

/**
Expand Down Expand Up @@ -982,9 +976,20 @@ public FullTextQuery setTimeout(int timeout) {
public FullTextQuery setTimeout(long timeout, TimeUnit timeUnit) {
super.setTimeout( (int)timeUnit.toSeconds( timeout ) );
timeoutManager.setTimeout( timeout, timeUnit );
timeoutManager.raiseExceptionOnTimeout();
return this;
}

public FullTextQuery limitFetchingTime(long timeout, TimeUnit timeUnit) {
timeoutManager.setTimeout( timeout, timeUnit );
timeoutManager.limitFetchingOnTimeout();
return this;
}

public boolean hasPartialResults() {
return timeoutManager.hasPartialResults();
}

private SearchFactoryImplementor getSearchFactoryImplementor() {
if ( searchFactoryImplementor == null ) {
searchFactoryImplementor = ContextHelper.getSearchFactoryBySFI( session );
Expand Down
Expand Up @@ -149,7 +149,7 @@ private boolean updateTopDocs(int n) throws IOException {
);
}
maybeTimeLimitingCollector = topCollector;
if ( timeoutManager.isBestEffort() ) {
if ( timeoutManager.getType() == TimeoutManager.Type.LIMIT ) {
final Long timeoutLeft = timeoutManager.getTimeoutLeftInMilliseconds();
if ( timeoutLeft != null ) {
maybeTimeLimitingCollector = new TimeLimitingCollector(topCollector, timeoutLeft );
Expand Down
Expand Up @@ -6,17 +6,20 @@
import org.apache.lucene.search.Query;

import org.hibernate.QueryTimeoutException;
import org.hibernate.search.SearchException;

/**
* @author Emmanuel Bernard
*/
public class TimeoutManager {
// timeout in nanoseconds
private Long timeout;
private boolean bestEffort;
private long start;
boolean timedOut = false;
private Query luceneQuery;
private Type type;
private boolean partialResults;


public TimeoutManager() {
}
Expand All @@ -26,6 +29,7 @@ public void start(Query luceneQuery) {
if ( timeout == null ) return;
this.luceneQuery = luceneQuery;
this.start = System.nanoTime();
this.partialResults = false;
}

public Long getTimeoutLeftInMilliseconds() {
Expand Down Expand Up @@ -64,14 +68,6 @@ private Long getTimeoutLeft(long factor) {
}
}

public boolean isBestEffort() {
return bestEffort;
}

public void setBestEffort(boolean bestEffort) {
this.bestEffort = bestEffort;
}

public boolean isTimedOut() {
if ( timeout == null ) return false;
if ( timedOut ) {
Expand All @@ -88,7 +84,7 @@ private boolean isTimedOut(long currentTime) {
else {
final long elapsedTime = currentTime - start;
timedOut = elapsedTime > timeout;
if ( ! bestEffort ) {
if ( this.type != Type.LIMIT ) {
throw new QueryTimeoutException(
"Full-text query took longer than expected (in microsecond): " + TimeUnit.NANOSECONDS.toMicros( elapsedTime ),
( SQLException) null,
Expand All @@ -101,13 +97,57 @@ private boolean isTimedOut(long currentTime) {

public void stop() {
this.timeout = null;
this.type = Type.NONE;
this.partialResults = false;
}

public void setTimeout(long timeout, TimeUnit timeUnit) {
this.timeout = timeUnit.toNanos( timeout );
//timeout of 0 means no more timeout
if ( timeout == 0 ) {
stop();
}
}

public void forceTimedOut() {
this.timedOut = Boolean.TRUE;
}

public void raiseExceptionOnTimeout() {
if ( this.type == Type.LIMIT ) {
throw new SearchException("Cannot define both setTimeout and limitFetchingTime on a full-text query. Please report your need to the Hibernate team");
}
this.type = Type.EXCEPTION;
}

public void limitFetchingOnTimeout() {
if ( this.type == Type.EXCEPTION ) {
throw new SearchException("Cannot define both setTimeout and limitFetchingTime on a full-text query. Please report your need to the Hibernate team");
}
this.type = Type.LIMIT;
}

public void reactOnQueryTimeoutExceptionWhileExtracting(QueryTimeoutException e) {
if ( type == Type.LIMIT) {
//we stop where we are return what we have
this.partialResults = true;
}
else {
throw e;
}
}

public boolean hasPartialResults() {
return partialResults;
}

public Type getType() {
return type;
}

public static enum Type {
NONE,
EXCEPTION,
LIMIT
}
}

0 comments on commit 6454e7d

Please sign in to comment.