[HER-1714] fixed-interval rescheduling

* AbstractFrontier.java, WorkQueueFrontier.java, BdbFrontier.java
    add 'futureUris' map of CrawlURIs to be reenqueued at specific future dates
    the usual findEligibleUri also checks for newly-eligible rescheduled URIs
    the frontier is no longer empty unless the future count is also 0
* CrawlURI.java
    add rescheduleTime property
* ReschedulingProcessor.java
    optional post-processor to set a CrawlURI's reschedulingTime
* CrawlStatSnapshot.java, CrawlJob.java
    add futureUriCount to stats/uriTotalsReport
This commit is contained in:
gojomo
2009-11-23 08:47:14 +00:00
parent 3c9df9a8c6
commit 18e165f21e
8 changed files with 151 additions and 12 deletions
@@ -710,7 +710,13 @@ public class CrawlJob implements Comparable<CrawlJob>, ApplicationListener {
.append(" queued = ")
.append(total)
.append(" total");
return sb.toString();
if(snapshot.futureUriCount>0) {
sb
.append(" (")
.append(snapshot.futureUriCount)
.append(" future)");
}
return sb.toString();
}
public String sizeTotalsReport() {
@@ -183,6 +183,12 @@ public interface Frontier extends Lifecycle, MultiReporter {
*/
public long queuedUriCount();
/**
* @return Number of URIs not currently queued/eligible but scheduled for future
*/
public long futureUriCount();
/**
* Ordinal position of the 'deepest' URI eligible
* for crawling. Essentially, the length of the longest
@@ -147,7 +147,7 @@ public abstract class AbstractFrontier
/** size of the 'outbound' mediation queue between manager thread
* and toethreads */
int outboundQueueCapacity = 50;
int outboundQueueCapacity = 200;
public int getOutboundQueueCapacity() {
return this.outboundQueueCapacity;
}
@@ -156,7 +156,7 @@ public abstract class AbstractFrontier
}
/** size of the inbound queue as multiple of the outbound queue */
int inboundQueueMultiple = 3;
int inboundQueueMultiple = 10;
public int getInboundQueueMultiple() {
return this.inboundQueueMultiple;
}
@@ -252,6 +252,8 @@ public abstract class AbstractFrontier
/** total URIs queued to be visited */
protected AtomicLong queuedUriCount = new AtomicLong(0);
protected AtomicLong futureUriCount = new AtomicLong(0);
protected AtomicLong succeededFetchCount = new AtomicLong(0);
protected AtomicLong failedFetchCount = new AtomicLong(0);
@@ -446,7 +448,7 @@ public abstract class AbstractFrontier
} else {
break;
}
}
}
}
/**
@@ -759,7 +761,14 @@ public abstract class AbstractFrontier
public long queuedUriCount() {
return queuedUriCount.get();
}
/* (non-Javadoc)
* @see org.archive.crawler.framework.Frontier#futureUriCount()
*/
public long futureUriCount() {
return futureUriCount.get();
}
/**
* (non-Javadoc)
*
@@ -216,6 +216,7 @@ implements Checkpointable, BeanNameAware {
JSONObject json = new JSONObject();
try {
json.put("queuedUriCount", queuedUriCount.get());
json.put("futureUriCount", futureUriCount.get());
json.put("succeededFetchCount", succeededFetchCount.get());
json.put("failedFetchCount", failedFetchCount.get());
json.put("disregardedUriCount", disregardedUriCount.get());
@@ -243,6 +244,7 @@ implements Checkpointable, BeanNameAware {
JSONObject json = recoveryCheckpoint.loadJson(beanName);
try {
queuedUriCount.set(json.getLong("queuedUriCount"));
futureUriCount.set(json.getLong("futureUriCount"));
succeededFetchCount.set(json.getLong("succeededFetchCount"));
failedFetchCount.set(json.getLong("failedFetchCount"));
disregardedUriCount.set(json.getLong("disregardedUriCount"));
@@ -293,6 +295,9 @@ implements Checkpointable, BeanNameAware {
// may exist
snoozedClassQueues = new DelayQueue<DelayedWorkQueue>();
this.futureUris = bdb.getStoredMap(
"futureUris", Long.class, CrawlURI.class, true, recoveryCheckpoint!=null);
// initialize master map in which other queues live
this.pendingUris = createMultipleWorkQueues();
}
@@ -68,6 +68,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.AbstractApplicationContext;
import com.sleepycat.collections.StoredSortedMap;
import com.sleepycat.je.DatabaseException;
/**
@@ -211,6 +212,8 @@ implements Closeable,
*/
transient protected DelayQueue<DelayedWorkQueue> snoozedClassQueues;
protected StoredSortedMap<Long, CrawlURI> futureUris;
transient protected WorkQueue longestActiveQueue = null;
protected int highestPrecedenceWaiting = Integer.MAX_VALUE;
@@ -584,6 +587,8 @@ implements Closeable,
assert Thread.currentThread() == managerThread;
// wake any snoozed queues
wakeQueues();
// consider rescheduled URIS
checkFutures();
// activate enough inactive queues to fill outbound
int activationsWanted =
outbound.remainingCapacity() - readyClassQueues.size();
@@ -671,7 +676,6 @@ implements Closeable,
continue findauri;
}
}
}
if(inProcessQueues.size()==0) {
@@ -689,7 +693,23 @@ implements Closeable,
return null;
}
/**
* Check for any future-scheduled URIs now eligible for reenqueuing
*/
protected void checkFutures() {
assert Thread.currentThread() == managerThread;
// TODO: consider only checking this every set interval
Iterator<CrawlURI> iter =
futureUris.headMap(System.currentTimeMillis())
.values().iterator();
while(iter.hasNext()) {
CrawlURI curi = iter.next();
curi.setRescheduleTime(-1); // unless again set elsewhere
iter.remove();
futureUriCount.decrementAndGet();
receive(curi);
}
}
/**
* Activate an inactive queue, if any are available.
@@ -981,12 +1001,18 @@ implements Closeable,
reenqueueQueue(wq);
}
curi.stripToMinimal();
curi.processingCleanup();
if(curi.getRescheduleTime()>0) {
// marked up for forced-revisit at a set time
curi.processingCleanup();
futureUris.put(curi.getRescheduleTime(),curi);
futureUriCount.incrementAndGet();
} else {
curi.stripToMinimal();
curi.processingCleanup();
}
}
private boolean includesRetireDirective(CrawlURI curi) {
protected boolean includesRetireDirective(CrawlURI curi) {
return curi.containsDataKey(A_FORCE_RETIRE)
&& (Boolean)curi.getData().get(A_FORCE_RETIRE);
}
@@ -1519,7 +1545,8 @@ implements Closeable,
public boolean isEmpty() {
return queuedUriCount.get() == 0
&& (uriUniqFilter == null || uriUniqFilter.pending() == 0)
&& (inbound == null || inbound.isEmpty());
&& (inbound == null || inbound.isEmpty())
&& futureUriCount.get() == 0;
}
/* (non-Javadoc)
@@ -0,0 +1,73 @@
/*
* This file is part of the Heritrix web crawler (crawler.archive.org).
*
* Licensed to the Internet Archive (IA) by one or more individual
* contributors.
*
* The IA licenses this file to You 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 org.archive.crawler.postprocessor;
import org.archive.modules.CrawlURI;
import org.archive.modules.Processor;
/**
* The most simple forced-rescheduling step possible: use a local
* setting (perhaps overlaid to vary based on the URI) to set an exact
* future reschedule time, as a delay from now. Unless the
* reschedulDelaySeconds value is changed from its default, URIs
* are not rescheduled.
*
* @author gojomo
* @version $Date: 2009-11-16 22:10:42 -0800 (Mon, 16 Nov 2009) $, $Revision: 6665 $
*/
public class ReschedulingProcessor extends Processor {
/**
* amount of time to wait before forcing a URI to be rescheduled
* default of -1 means "don't reschedule"
*/
{
setRescheduleDelaySeconds(-1L);
}
public long getRescheduleDelaySeconds() {
return (Long) kp.get("rescheduleDelaySeconds");
}
public void setRescheduleDelaySeconds(long rescheduleDelaySeconds) {
kp.put("rescheduleDelaySeconds",rescheduleDelaySeconds);
}
public ReschedulingProcessor() {
super();
}
@Override
protected boolean shouldProcess(CrawlURI curi) {
return true;
}
@Override
protected void innerProcess(CrawlURI curi) {
if(curi.isPrerequisite()) {
// never resched prereqs; they get rescheduled as needed
curi.setRescheduleTime(-1);
return;
}
long rds = getRescheduleDelaySeconds();
if(rds>0) {
curi.setRescheduleTime(System.currentTimeMillis()+(1000*rds));
} else {
curi.setRescheduleTime(-1);
}
}
}
@@ -37,6 +37,7 @@ public class CrawlStatSnapshot {
public long discoveredUriCount;
public long queuedUriCount;
public long futureUriCount;
public long finishedUriCount;
public long downloadedUriCount;
public long downloadFailures;
@@ -74,6 +75,7 @@ public class CrawlStatSnapshot {
discoveredUriCount = controller.getFrontier().discoveredUriCount();
finishedUriCount = controller.getFrontier().finishedUriCount();
queuedUriCount = controller.getFrontier().queuedUriCount();
futureUriCount = controller.getFrontier().futureUriCount();
downloadFailures = controller.getFrontier().failedFetchCount();
downloadDisregards = controller.getFrontier().disregardedUriCount();
@@ -1826,4 +1826,15 @@ implements MultiReporter, Serializable, OverlayContext {
public CrawlURI getFullVia() {
return fullVia;
}
/**
* A future time at which this CrawlURI should be reenqueued.
*/
protected long rescheduleTime = -1;
public void setRescheduleTime(long time) {
this.rescheduleTime = time;
}
public long getRescheduleTime() {
return this.rescheduleTime;
}
}