Author: kwright
Date: Thu Feb 6 18:59:13 2014
New Revision: 1565396
URL: http://svn.apache.org/r1565396
Log:
Hook up notification retry logic
Modified:
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/JobNotifyRecord.java
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
Modified:
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
URL:
http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java?rev=1565396&r1=1565395&r2=1565396&view=diff
==============================================================================
---
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
(original)
+++
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/IJobManager.java
Thu Feb 6 18:59:13 2014
@@ -512,6 +512,14 @@ public interface IJobManager
public void resetCleaningDocumentMultiple(DocumentDescription[]
documentDescriptions, long checkTime)
throws ManifoldCFException;
+ /** Retry notification.
+ *@param jobNotifyRecord is the current job notification record.
+ *@param failTime is the new fail time (-1L if none).
+ *@param failRetryCount is the new fail retry count (-1 if none).
+ */
+ public void retryNotification(JobNotifyRecord jnr, long failTime, int
failRetryCount)
+ throws ManifoldCFException;
+
/** Add an initial set of documents to the queue.
* This method is called during job startup, when the queue is being loaded.
* A set of document references is passed to this method, which updates the
status of the document
@@ -961,6 +969,14 @@ public interface IJobManager
public void deleteIngestedDocumentIdentifiers(DocumentDescription[]
identifiers)
throws ManifoldCFException;
+ /** Abort notification.
+ *@param jobID is the job to abort.
+ *@param errorText is the error text.
+ *@return true if this is the first time the job is aborted.
+ */
+ public boolean notifyAbort(Long jobID, String errorText)
+ throws ManifoldCFException;
+
/** Abort a running job due to a fatal error condition.
*@param jobID is the job to abort.
*@param errorText is the error text.
Modified:
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/JobNotifyRecord.java
URL:
http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/JobNotifyRecord.java?rev=1565396&r1=1565395&r2=1565396&view=diff
==============================================================================
---
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/JobNotifyRecord.java
(original)
+++
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/interfaces/JobNotifyRecord.java
Thu Feb 6 18:59:13 2014
@@ -25,11 +25,34 @@ public class JobNotifyRecord extends Job
{
public static final String _rcsid = "@(#)$Id$";
+ /** Fail time; -1L if none currently set */
+ protected final long failTime;
+ /** Fail retry count; -1 if none currently set */
+ protected final int failRetryCount;
+
/** Constructor.
*/
- public JobNotifyRecord(Long jobID)
+ public JobNotifyRecord(Long jobID, long failTime, int failRetryCount)
{
super(jobID);
+ this.failTime = failTime;
+ this.failRetryCount = failRetryCount;
+ }
+
+ /** Get the hard fail time.
+ *@return the fail time in ms since epoch, or -1L if none.
+ */
+ public long getFailTime()
+ {
+ return failTime;
+ }
+
+ /** Get the hard fail retry count.
+ *@return the fail retry count, or -1 if none.
+ */
+ public int getFailRetryCount()
+ {
+ return failRetryCount;
}
}
Modified:
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
URL:
http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java?rev=1565396&r1=1565395&r2=1565396&view=diff
==============================================================================
---
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
(original)
+++
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/JobManager.java
Thu Feb 6 18:59:13 2014
@@ -3572,6 +3572,68 @@ public class JobManager implements IJobM
}
}
+ /** Retry notification.
+ *@param jobNotifyRecord is the current job notification record.
+ *@param failTime is the new fail time (-1L if none).
+ *@param failCount is the new fail retry count (-1 if none).
+ */
+ @Override
+ public void retryNotification(JobNotifyRecord jnr, long failTime, int
failCount)
+ throws ManifoldCFException
+ {
+ Long jobID = jnr.getJobID();
+ long oldFailTime = jnr.getFailTime();
+ if (oldFailTime == -1L)
+ oldFailTime = failTime;
+ failTime = oldFailTime;
+ int oldFailCount = jnr.getFailRetryCount();
+ if (oldFailCount == -1)
+ oldFailCount = failCount;
+ else
+ {
+ oldFailCount--;
+ if (failCount != -1 && oldFailCount > failCount)
+ oldFailCount = failCount;
+ }
+ failCount = oldFailCount;
+
+ while (true)
+ {
+ long sleepAmt = 0L;
+ database.beginTransaction();
+ try
+ {
+ jobs.retryNotification(jobID,failTime,failCount);
+ database.performCommit();
+ break;
+ }
+ catch (Error e)
+ {
+ database.signalRollback();
+ throw e;
+ }
+ catch (ManifoldCFException e)
+ {
+ database.signalRollback();
+ if (e.getErrorCode() == e.DATABASE_TRANSACTION_ABORT)
+ {
+ if (Logging.perf.isDebugEnabled())
+ Logging.perf.debug("Aborted transaction resetting job
notification: "+e.getMessage());
+ sleepAmt = getRandomAmount();
+ continue;
+ }
+ throw e;
+ }
+ finally
+ {
+ database.endTransaction();
+ sleepFor(sleepAmt);
+ }
+ }
+
+ }
+
+
/** Reset a set of cleaning documents for further processing in the future.
* This method is called after some unknown number of the documents were
cleaned, but then an ingestion service interruption occurred.
* Note well: The logic here basically presumes that we cannot know whether
the documents were indeed cleaned or not.
@@ -6107,17 +6169,73 @@ public class JobManager implements IJobM
* until the job finishes on its own.
*@param jobID is the job to abort.
*/
+ @Override
public void manualAbortRestart(Long jobID)
throws ManifoldCFException
{
manualAbortRestart(jobID,false);
}
+ /** Abort notification.
+ *@param jobID is the job to abort.
+ *@param errorText is the error text.
+ *@return true if this is the first time the job is aborted.
+ */
+ @Override
+ public boolean notifyAbort(Long jobID, String errorText)
+ throws ManifoldCFException
+ {
+ if (Logging.jobs.isDebugEnabled())
+ {
+ Logging.jobs.debug("Aborting notification for "+jobID+" due to error
'"+errorText+"'");
+ }
+ boolean rval;
+ while (true)
+ {
+ long sleepAmt = 0L;
+ database.beginTransaction();
+ try
+ {
+ rval = jobs.notifyAbort(jobID,errorText);
+ database.performCommit();
+ break;
+ }
+ catch (ManifoldCFException e)
+ {
+ database.signalRollback();
+ if (e.getErrorCode() == e.DATABASE_TRANSACTION_ABORT)
+ {
+ if (Logging.perf.isDebugEnabled())
+ Logging.perf.debug("Aborted transaction aborting job notification:
"+e.getMessage());
+ sleepAmt = getRandomAmount();
+ continue;
+ }
+ throw e;
+ }
+ catch (Error e)
+ {
+ database.signalRollback();
+ throw e;
+ }
+ finally
+ {
+ database.endTransaction();
+ sleepFor(sleepAmt);
+ }
+ }
+ if (rval && Logging.jobs.isDebugEnabled())
+ {
+ Logging.jobs.debug("Job notification job "+jobID+" abort signal
successfully sent");
+ }
+ return rval;
+ }
+
/** Abort a running job due to a fatal error condition.
*@param jobID is the job to abort.
*@param errorText is the error text.
*@return true if this is the first logged abort request for this job.
*/
+ @Override
public boolean errorAbort(Long jobID, String errorText)
throws ManifoldCFException
{
@@ -7184,7 +7302,8 @@ public class JobManager implements IJobM
StringBuilder sb = new StringBuilder("SELECT ");
ArrayList list = new ArrayList();
- sb.append(jobs.idField).append(" FROM
").append(jobs.getTableName()).append(" WHERE ")
+
sb.append(jobs.idField).append(",").append(jobs.failTimeField).append(jobs.failCountField)
+ .append(" FROM ").append(jobs.getTableName()).append(" WHERE ")
.append(database.buildConjunctionClause(list,new ClauseDescription[]{
new
UnitaryClause(jobs.statusField,jobs.statusToString(jobs.STATUS_READYFORNOTIFY))}))
.append(" FOR UPDATE");
@@ -7197,13 +7316,26 @@ public class JobManager implements IJobM
{
IResultRow row = set.getRow(i);
Long jobID = (Long)row.getValue(jobs.idField);
+ Long failTimeLong = (Long)row.getValue(jobs.failTimeField);
+ Long failRetryCountLong = (Long)row.getValue(jobs.failCountField);
+ long failTime;
+ if (failTimeLong == null)
+ failTime = -1L;
+ else
+ failTime = failTimeLong.longValue();
+ int failRetryCount;
+ if (failRetryCountLong == null)
+ failRetryCount = -1;
+ else
+ failRetryCount = (int)failRetryCountLong.longValue();
+
// Mark status of job as "starting delete"
jobs.writeTransientStatus(jobID,jobs.STATUS_NOTIFYINGOFCOMPLETION,processID);
if (Logging.jobs.isDebugEnabled())
{
Logging.jobs.debug("Found job "+jobID+" in need of notification");
}
- rval[i++] = new JobNotifyRecord(jobID);
+ rval[i++] = new JobNotifyRecord(jobID,failTime,failRetryCount);
}
database.performCommit();
return rval;
Modified:
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
URL:
http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java?rev=1565396&r1=1565395&r2=1565396&view=diff
==============================================================================
---
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
(original)
+++
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/jobs/Jobs.java
Thu Feb 6 18:59:13 2014
@@ -1689,6 +1689,30 @@ public class Jobs extends org.apache.man
}
+ /** Retry notification.
+ *@param jobID is the job identifier.
+ *@param failTime is the fail time, -1 == none
+ *@param failCount is the fail count to use, -1 == none.
+ */
+ public void retryNotification(Long jobID, long failTime, int failCount)
+ throws ManifoldCFException
+ {
+ ArrayList list = new ArrayList();
+ String query = buildConjunctionClause(list,new ClauseDescription[]{
+ new UnitaryClause(idField,jobID)});
+ HashMap map = new HashMap();
+ map.put(statusField,statusToString(STATUS_READYFORNOTIFY));
+ if (failTime == -1L)
+ map.put(failTimeField,null);
+ else
+ map.put(failTimeField,new Long(failTime));
+ if (failCount == -1)
+ map.put(failCountField,null);
+ else
+ map.put(failCountField,failCount);
+ performUpdate(map,"WHERE "+query,list,new StringSet(getJobStatusKey()));
+ }
+
/** Write job status and window end, and clear the endtime field. (The
start time will be written
* when the job enters the "active" state.)
*@param jobID is the job identifier.
@@ -2033,6 +2057,44 @@ public class Jobs extends org.apache.man
performUpdate(map,"WHERE "+query,list,new StringSet(getJobStatusKey()));
}
+ /** Abort a job notification.
+ *@param jobID is the job id.
+ *@param errorText is the error, or null if none.
+ */
+ public boolean notifyAbort(Long jobID, String errorText)
+ throws ManifoldCFException
+ {
+ // Get the current job status
+ ArrayList list = new ArrayList();
+ String query = buildConjunctionClause(list,new ClauseDescription[]{
+ new UnitaryClause(idField,jobID)});
+ IResultSet set = performQuery("SELECT "+statusField+" FROM
"+getTableName()+
+ " WHERE "+query+" FOR UPDATE",list,null,null);
+ if (set.getRowCount() == 0)
+ throw new ManifoldCFException("Job does not exist: "+jobID);
+ IResultRow row = set.getRow(0);
+ int status = stringToStatus(row.getValue(statusField).toString());
+ if (status == STATUS_INACTIVE)
+ return false;
+ int newStatus;
+ switch (status)
+ {
+ case STATUS_NOTIFYINGOFCOMPLETION:
+ newStatus = STATUS_INACTIVE;
+ break;
+ default:
+ throw new ManifoldCFException("Job "+jobID+" is not notifying");
+ }
+ // Pause the job
+ HashMap map = new HashMap();
+ map.put(statusField,statusToString(newStatus));
+ map.put(errorField,errorText);
+ map.put(failTimeField,null);
+ map.put(failCountField,null);
+ performUpdate(map,"WHERE "+query,list,new StringSet(getJobStatusKey()));
+ return true;
+ }
+
/** Abort a job.
*@param jobID is the job id.
*@param errorText is the error, or null if none.
@@ -2555,6 +2617,8 @@ public class Jobs extends org.apache.man
HashMap map = new HashMap();
map.put(statusField,statusToString(STATUS_INACTIVE));
map.put(processIDField,null);
+ map.put(failTimeField,null);
+ map.put(failCountField,null);
// Leave everything else around from the abort/finish.
performUpdate(map,"WHERE "+query,list,new StringSet(getJobStatusKey()));
}
Modified:
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
URL:
http://svn.apache.org/viewvc/manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java?rev=1565396&r1=1565395&r2=1565396&view=diff
==============================================================================
---
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
(original)
+++
manifoldcf/branches/CONNECTORS-833/framework/pull-agent/src/main/java/org/apache/manifoldcf/crawler/system/JobNotificationThread.java
Thu Feb 6 18:59:13 2014
@@ -75,7 +75,7 @@ public class JobNotificationThread exten
JobNotifyRecord[] jobsNeedingNotification =
jobManager.getJobsReadyForInactivity(processID);
try
{
- HashMap connectionNames = new HashMap();
+ Set<OutputAndRepositoryConnection> connectionNames = new
HashSet<OutputAndRepositoryConnection>();
int k = 0;
while (k < jobsNeedingNotification.length)
@@ -89,18 +89,15 @@ public class JobNotificationThread exten
String repositoryConnectionName = job.getConnectionName();
String outputConnectionName = job.getOutputConnectionName();
OutputAndRepositoryConnection c = new
OutputAndRepositoryConnection(outputConnectionName, repositoryConnectionName);
- connectionNames.put(c,c);
+ connectionNames.add(c);
}
}
// Attempt to notify the specified connections
- HashMap notifiedConnections = new HashMap();
+ Map<OutputAndRepositoryConnection,Disposition> notifiedConnections
= new HashMap<OutputAndRepositoryConnection,Disposition>();
- Iterator iter = connectionNames.keySet().iterator();
- while (iter.hasNext())
+ for (OutputAndRepositoryConnection connections : connectionNames)
{
- OutputAndRepositoryConnection connections =
(OutputAndRepositoryConnection)iter.next();
-
String outputConnectionName =
connections.getOutputConnectionName();
String repositoryConnectionName =
connections.getRepositoryConnectionName();
@@ -119,11 +116,11 @@ public class JobNotificationThread exten
try
{
connector.noteJobComplete(activity);
+ notifiedConnections.put(connections,new Disposition());
}
catch (ServiceInterruption e)
{
- Logging.threads.warn("Service interruption notifying
connection - retrying: "+e.getMessage(),e);
- continue;
+ notifiedConnections.put(connections,new Disposition(e));
}
catch (ManifoldCFException e)
{
@@ -136,7 +133,6 @@ public class JobNotificationThread exten
// Nothing special; report the error and keep going.
Logging.threads.error(e.getMessage(),e);
}
- notifiedConnections.put(connections,connections);
}
finally
{
@@ -160,11 +156,61 @@ public class JobNotificationThread exten
String repositoryConnectionName = job.getConnectionName();
OutputAndRepositoryConnection c = new
OutputAndRepositoryConnection(outputConnectionName, repositoryConnectionName);
- if (notifiedConnections.get(c) != null)
+ Disposition d = notifiedConnections.get(c);
+ if (d != null)
{
- // When done, put the job into the Inactive state.
Otherwise, the notification will be retried until it succeeds.
- jobManager.inactivateJob(jobID);
- jsr.noteStarted();
+ ServiceInterruption e = d.getServiceInterruption();
+ if (e == null)
+ {
+ jobManager.inactivateJob(jobID);
+ jsr.noteStarted();
+ }
+ else
+ {
+ if (!e.jobInactiveAbort())
+ {
+ Logging.jobs.warn("Notification service interruption
reported for job "+
+ jobID+" output connection '"+outputConnectionName+"':
"+
+ e.getMessage());
+ }
+
+ ManifoldCFException abortOnFail;
+ if (!e.jobInactiveAbort() && e.isAbortOnFail())
+ abortOnFail = new ManifoldCFException("Repeated service
interruptions - failure performing notification"+((e.getCause()!=null)?":
"+e.getCause().getMessage():""),e.getCause());
+ else
+ abortOnFail = null;
+
+ // If either we are going to be requeuing beyond the fail
time, OR
+ // the number of retries available has hit 0, THEN we
treat this
+ // as either an "ignore" or a hard error.
+ if (!e.jobInactiveAbort() && (jsr.getFailTime() != -1L &&
jsr.getFailTime() < e.getRetryTime() ||
+ jsr.getFailRetryCount() == 0))
+ {
+ // Treat this as a hard failure.
+ if (e.isAbortOnFail())
+ {
+ // Note the error in the job, and transition to
inactive state
+ if (abortOnFail != null)
+
Logging.jobs.error(abortOnFail.getMessage(),abortOnFail);
+
jobManager.notifyAbort(jobID,(abortOnFail==null)?"":abortOnFail.getMessage());
+ jsr.noteStarted();
+ }
+ else
+ {
+ // Not sure this can happen -- but just transition
silently to inactive state
+ jobManager.inactivateJob(jobID);
+ jsr.noteStarted();
+ }
+ }
+ else
+ {
+ // Reset the job to the READYFORNOTIFY state, updating
the failtime and failcount fields
+ if (abortOnFail != null)
+
Logging.jobs.warn(abortOnFail.getMessage(),abortOnFail);
+
jobManager.retryNotification(jsr,e.getFailTime(),e.getFailRetryCount());
+ jsr.noteStarted();
+ }
+ }
}
}
}
@@ -255,11 +301,32 @@ public class JobNotificationThread exten
}
}
+ /** Disposition of an output/repository connection combination */
+ protected static class Disposition
+ {
+ protected final ServiceInterruption serviceInterruption;
+
+ public Disposition(ServiceInterruption serviceInterruption)
+ {
+ this.serviceInterruption = serviceInterruption;
+ }
+
+ public Disposition()
+ {
+ this.serviceInterruption = null;
+ }
+
+ public ServiceInterruption getServiceInterruption()
+ {
+ return serviceInterruption;
+ }
+ }
+
/** Output connection/repository connection pair object */
protected static class OutputAndRepositoryConnection
{
- protected String outputConnectionName;
- protected String repositoryConnectionName;
+ protected final String outputConnectionName;
+ protected final String repositoryConnectionName;
public OutputAndRepositoryConnection(String outputConnectionName, String
repositoryConnectionName)
{