Retry failed operations

pull/174/head
M66B 6 years ago
parent 2274d4870e
commit 904b5a3920

@ -126,33 +126,33 @@ class Core {
private static final int DOWNLOAD_BATCH_SIZE = 20;
private static final long YIELD_DURATION = 200L; // milliseconds
private static final long FUTURE_RECEIVED = 30 * 24 * 3600 * 1000L; // milliseconds
private static final int OPERATION_RETRY_MAX = 10;
private static final long OPERATION_RETRY_DELAY = 15 * 1000L; // milliseconds
static void processOperations(
Context context,
EntityAccount account, EntityFolder folder, List<TupleOperationEx> ops,
Store istore, Folder ifolder,
State state)
throws MessagingException, JSONException, IOException {
throws JSONException {
try {
Log.i(folder.name + " start process");
DB db = DB.getInstance(context);
List<Long> processed = new ArrayList<>();
int retry = 0;
boolean group = true;
Log.i(folder.name + " executing operations=" + ops.size());
for (int i = 0; i < ops.size() && state.isRunning() && state.isRecoverable(); i++) {
EntityOperation op = ops.get(i);
if (processed.contains(op.id)) {
Log.i(folder.name + " already processed op=" + op.id + "/" + op.name);
continue;
}
while (retry < OPERATION_RETRY_MAX && ops.size() > 0 && state.isRunning() && state.isRecoverable()) {
TupleOperationEx op = ops.get(0);
try {
Log.i(folder.name +
" start op=" + op.id + "/" + op.name +
" folder=" + op.folder +
" msg=" + op.message +
" args=" + op.args);
" args=" + op.args +
" retry=" + retry);
// Fetch most recent copy of message
EntityMessage message = null;
@ -160,7 +160,7 @@ class Core {
message = db.message().getMessage(op.message);
JSONArray jargs = new JSONArray(op.args);
Map<EntityOperation, EntityMessage> similar = new HashMap<>();
Map<TupleOperationEx, EntityMessage> similar = new HashMap<>();
try {
// Operations should use database transaction when needed
@ -173,8 +173,8 @@ class Core {
// Process similar operations
boolean skip = false;
for (int j = i + 1; j < ops.size(); j++) {
EntityOperation next = ops.get(j);
for (int j = 1; j < ops.size(); j++) {
TupleOperationEx next = ops.get(j);
switch (op.name) {
case EntityOperation.ADD:
@ -195,16 +195,15 @@ class Core {
break;
case EntityOperation.MOVE:
if (message.uid != null &&
if (group &&
message.uid != null &&
EntityOperation.MOVE.equals(next.name)) {
JSONArray jnext = new JSONArray(next.args);
// Same target
if (jargs.getLong(0) == jnext.getLong(0)) {
EntityMessage m = db.message().getMessage(next.message);
if (m != null && m.uid != null) {
processed.add(next.id);
if (m != null && m.uid != null)
similar.put(next, m);
}
}
}
break;
@ -216,11 +215,12 @@ class Core {
" skipping op=" + op.id + "/" + op.name +
" msg=" + op.message + " args=" + op.args);
db.operation().deleteOperation(op.id);
ops.remove(op);
continue;
}
List<Long> sids = new ArrayList<>();
for (EntityOperation s : similar.keySet())
for (TupleOperationEx s : similar.keySet())
sids.add(s.id);
if (similar.size() > 0)
@ -242,7 +242,7 @@ class Core {
db.beginTransaction();
db.operation().setOperationError(op.id, null);
for (EntityOperation s : similar.keySet())
for (TupleOperationEx s : similar.keySet())
db.operation().setOperationError(s.id, null);
if (message != null) {
@ -253,7 +253,7 @@ class Core {
if (!EntityOperation.SYNC.equals(op.name)) {
db.operation().setOperationState(op.id, "executing");
for (EntityOperation s : similar.keySet())
for (TupleOperationEx s : similar.keySet())
db.operation().setOperationState(s.id, "executing");
}
@ -374,13 +374,17 @@ class Core {
db.beginTransaction();
db.operation().deleteOperation(op.id);
for (EntityOperation s : similar.keySet())
for (TupleOperationEx s : similar.keySet())
db.operation().deleteOperation(s.id);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
ops.remove(op);
for (TupleOperationEx s : similar.keySet())
ops.remove(s);
} catch (Throwable ex) {
Log.e(folder.name, ex);
EntityLog.log(context, folder.name + " " + Log.formatThrowable(ex, false));
@ -389,7 +393,7 @@ class Core {
db.beginTransaction();
db.operation().setOperationError(op.id, Log.formatThrowable(ex));
for (EntityOperation s : similar.keySet())
for (TupleOperationEx s : similar.keySet())
db.operation().setOperationError(s.id, Log.formatThrowable(ex));
if (message != null && !(ex instanceof IllegalArgumentException)) {
@ -406,6 +410,12 @@ class Core {
db.endTransaction();
}
if (similar.size() > 0) {
// Retry individually
group = false;
continue;
}
if (ex instanceof OutOfMemoryError ||
ex instanceof MessageRemovedException ||
ex instanceof MessageRemovedIOException ||
@ -433,8 +443,6 @@ class Core {
// There is no use in repeating
db.operation().deleteOperation(op.id);
for (EntityOperation s : similar.keySet())
db.operation().deleteOperation(s.id);
// Cleanup folder
if (EntityOperation.SYNC.equals(op.name))
@ -445,34 +453,32 @@ class Core {
(ex instanceof MessageRemovedException ||
ex instanceof MessageRemovedIOException ||
ex.getCause() instanceof MessageRemovedException ||
ex.getCause() instanceof MessageRemovedIOException)) {
// Failsafe: retry batch
if (similar.size() > 0)
throw ex;
ex.getCause() instanceof MessageRemovedIOException))
db.message().deleteMessage(message.id);
}
// Cleanup operations
op.cleanup(context);
for (EntityOperation s : similar.keySet())
s.cleanup(context);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
continue;
ops.remove(op);
} else {
retry++;
try {
Thread.sleep(OPERATION_RETRY_DELAY);
} catch (InterruptedException ex1) {
Log.w(ex1);
}
}
throw ex;
} finally {
try {
db.beginTransaction();
db.operation().setOperationState(op.id, null);
for (EntityOperation s : similar.keySet())
for (TupleOperationEx s : similar.keySet())
db.operation().setOperationState(s.id, null);
db.setTransactionSuccessful();
@ -484,6 +490,10 @@ class Core {
Log.i(folder.name + " end op=" + op.id + "/" + op.name);
}
}
if (ops.size() > 0)
Log.w("Operations failed=" + ops.size());
} finally {
Log.i(folder.name + " end process state=" + state);
}

Loading…
Cancel
Save