@Override public void run() { while (true) { RedoTask task; try { task = mQueue.take(); } catch (InterruptedException e) { break; } if (task.isShutdownTask()) break; if (hadError()) { // If there was an error, keep consuming from the queue without executing anything. // This thread must consume all tasks until shutdown task is received. If this // thread stopped consuming, the producer may not be able to enqueue the shutdown // task. continue; } RedoableOp op = task.getOp(); try { if (ZimbraLog.redolog.isDebugEnabled()) ZimbraLog.redolog.info("Executing: " + op.toString()); op.redo(); } catch (OutOfMemoryError oome) { Zimbra.halt("Out of memory while executing redo op", oome); } catch (Throwable e) { ZimbraLog.redolog.error("Unable to execute redo op: " + op.toString(), e); if (!ignoreReplayErrors()) raiseError(e); } } }
@Override protected void playOp(RedoableOp op) throws Exception { checkError(); int mboxId = op.getMailboxId(); if (mboxId == RedoableOp.MAILBOX_ID_ALL || mboxId == RedoableOp.UNKNOWN_ID) { // Multi-mailbox ops are executed by the main thread to prevent later ops // that depend on this op's result aren't run out of order. if (ZimbraLog.redolog.isDebugEnabled()) ZimbraLog.redolog.info("Executing: " + op.toString()); op.redo(); } else { // Ops for the same mailbox must be played back in order. To ensure that, // all ops for the same mailbox are sent to the same player thread. The // ops are added to the thread's internal queue and played back in order. // This assignment of ops to threads will result in uneven distribution. int index = Math.abs(mboxId % mPlayerThreads.length); PlayerThread player = mPlayerThreads[index]; RedoTask task = new RedoTask(op); if (ZimbraLog.redolog.isDebugEnabled()) ZimbraLog.redolog.info("Enqueuing: " + op.toString()); try { player.enqueue(task); } catch (InterruptedException e) { } } }