Search in sources :

Example 1 with RedoableOp

use of com.zimbra.cs.redolog.op.RedoableOp in project zm-mailbox by Zimbra.

the class DesktopMailboxTest method init.

@BeforeClass
public static void init() throws Exception {
    //TODO: allow paths to be specified so we can run tests outside of ZimbraServer
    MailboxTestUtil.initServer();
    MailboxManager.setInstance(new MailboxManager() {

        @Override
        protected Mailbox instantiateMailbox(MailboxData data) {
            //mock the behaviors we need to test in DesktopMailbox
            return new Mailbox(data) {

                @Override
                protected boolean needRedo(OperationContext octxt, RedoableOp recorder) {
                    return false;
                }
            };
        }
    });
    Provisioning prov = Provisioning.getInstance();
    prov.createAccount("test@zimbra.com", "secret", new HashMap<String, Object>());
}
Also used : RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) MailboxData(com.zimbra.cs.mailbox.Mailbox.MailboxData) Provisioning(com.zimbra.cs.account.Provisioning) BeforeClass(org.junit.BeforeClass)

Example 2 with RedoableOp

use of com.zimbra.cs.redolog.op.RedoableOp in project zm-mailbox by Zimbra.

the class RedoLogVerify method scanLog.

public boolean scanLog(File logfile) throws IOException {
    boolean good = false;
    FileLogReader logReader = new FileLogReader(logfile, false);
    logReader.open();
    if (!mParams.quiet) {
        FileHeader header = logReader.getHeader();
        mOut.println("HEADER");
        mOut.println("------");
        mOut.print(header);
        mOut.println("------");
    }
    boolean hasMailboxIdsFilter = !mParams.mboxIds.isEmpty();
    RedoableOp op = null;
    long lastPosition = 0;
    long lastOpStartOffset = 0;
    try {
        while ((op = logReader.getNextOp()) != null) {
            lastOpStartOffset = logReader.getLastOpStartOffset();
            lastPosition = logReader.position();
            if (hasMailboxIdsFilter) {
                int mboxId = op.getMailboxId();
                if (op instanceof StoreIncomingBlob) {
                    List<Integer> list = ((StoreIncomingBlob) op).getMailboxIdList();
                    if (list != null) {
                        boolean match = false;
                        for (Integer mid : list) {
                            if (mParams.mboxIds.contains(mid)) {
                                match = true;
                                break;
                            }
                        }
                        if (!match)
                            continue;
                    }
                // If list==null, it's a store incoming blob op targeted at unknown set of mailboxes.
                // It applies to our filtered mailboxes.
                } else if (!mParams.mboxIds.contains(mboxId)) {
                    continue;
                }
            }
            if (!mParams.quiet) {
                printOp(mOut, op, mParams.hideOffset, lastOpStartOffset, lastPosition - lastOpStartOffset);
                if (mParams.showBlob) {
                    InputStream dataStream = op.getAdditionalDataStream();
                    if (dataStream != null) {
                        mOut.println("<START OF BLOB>");
                        ByteUtil.copy(dataStream, true, mOut, false);
                        mOut.println();
                        mOut.println("<END OF BLOB>");
                    }
                }
            }
        }
        good = true;
    } catch (IOException e) {
        // The IOException could be a real I/O problem or it could mean
        // there was a server crash previously and there were half-written
        // log entries.
        mOut.println();
        mOut.printf("Error while parsing data starting at offset 0x%08x", lastPosition);
        mOut.println();
        long size = logReader.getSize();
        long diff = size - lastPosition;
        mOut.printf("%d bytes remaining in the file", diff);
        mOut.println();
        mOut.println();
        if (op != null) {
            mOut.println("Last suceessfully parsed redo op:");
            printOp(mOut, op, false, lastOpStartOffset, lastPosition - lastOpStartOffset);
            mOut.println();
        }
        // hexdump data around the bad bytes
        int bytesPerLine = 16;
        int linesBefore = 10;
        int linesAfter = 10;
        long startPos = Math.max(lastPosition - (lastPosition % bytesPerLine) - linesBefore * bytesPerLine, 0);
        int count = (int) Math.min((linesBefore + linesAfter + 1) * bytesPerLine, lastPosition - startPos + diff);
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(logfile, "r");
            raf.seek(startPos);
            byte[] buf = new byte[count];
            raf.read(buf, 0, count);
            mOut.printf("Data near error offset %08x:", lastPosition);
            mOut.println();
            hexdump(mOut, buf, 0, count, startPos, lastPosition);
            mOut.println();
        } catch (IOException eh) {
            mOut.println("Error opening log file " + logfile.getAbsolutePath() + " for hexdump");
            eh.printStackTrace(mOut);
        } finally {
            if (raf != null)
                raf.close();
        }
        throw e;
    } finally {
        logReader.close();
    }
    return good;
}
Also used : RandomAccessFile(java.io.RandomAccessFile) RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) StoreIncomingBlob(com.zimbra.cs.redolog.op.StoreIncomingBlob) InputStream(java.io.InputStream) IOException(java.io.IOException) FileHeader(com.zimbra.cs.redolog.logger.FileHeader) FileLogReader(com.zimbra.cs.redolog.logger.FileLogReader)

Example 3 with RedoableOp

use of com.zimbra.cs.redolog.op.RedoableOp in project zm-mailbox by Zimbra.

the class RedoLogManager method start.

public synchronized void start() {
    mEnabled = true;
    try {
        File logdir = mLogFile.getParentFile();
        if (!logdir.exists()) {
            if (!logdir.mkdirs())
                throw new IOException("Unable to create directory " + logdir.getAbsolutePath());
        }
        if (!mArchiveDir.exists()) {
            if (!mArchiveDir.mkdirs())
                throw new IOException("Unable to create directory " + mArchiveDir.getAbsolutePath());
        }
    } catch (IOException e) {
        signalFatalError(e);
    }
    setInCrashRecovery(true);
    // mSupportsCrashRecovery is false.
    try {
        mRolloverMgr.crashRecovery();
    } catch (IOException e) {
        ZimbraLog.redolog.fatal("Exception during crash recovery");
        signalFatalError(e);
    }
    long fsyncInterval = RedoConfig.redoLogFsyncIntervalMS();
    mLogWriter = createLogWriter(this, mLogFile, fsyncInterval);
    ArrayList<RedoableOp> postStartupRecoveryOps = new ArrayList<RedoableOp>(100);
    int numRecoveredOps = 0;
    if (mSupportsCrashRecovery) {
        mRecoveryMode = true;
        ZimbraLog.redolog.info("Starting pre-startup crash recovery");
        // Run crash recovery.
        try {
            mLogWriter.open();
            mRolloverMgr.initSequence(mLogWriter.getSequence());
            RedoPlayer redoPlayer = new RedoPlayer(true);
            try {
                numRecoveredOps = redoPlayer.runCrashRecovery(this, postStartupRecoveryOps);
            } finally {
                redoPlayer.shutdown();
            }
            mLogWriter.close();
        } catch (Exception e) {
            ZimbraLog.redolog.fatal("Exception during crash recovery");
            signalFatalError(e);
        }
        ZimbraLog.redolog.info("Finished pre-startup crash recovery");
        mRecoveryMode = false;
    }
    setInCrashRecovery(false);
    // Reopen log after crash recovery.
    try {
        mLogWriter.open();
        mRolloverMgr.initSequence(mLogWriter.getSequence());
        mInitialLogSize = mLogWriter.getSize();
    } catch (IOException e) {
        ZimbraLog.redolog.fatal("Unable to open redo log");
        signalFatalError(e);
    }
    if (numRecoveredOps > 0) {
        // file after rollover will still list these uncommitted ops.
        if (postStartupRecoveryOps.size() > 0) {
            synchronized (mActiveOps) {
                for (Iterator iter = postStartupRecoveryOps.iterator(); iter.hasNext(); ) {
                    RedoableOp op = (RedoableOp) iter.next();
                    assert (op.isStartMarker());
                    mActiveOps.put(op.getTransactionId(), op);
                }
            }
        }
        // Force rollover to clear the current log file.
        forceRollover();
        // requests.
        if (postStartupRecoveryOps.size() > 0) {
            synchronized (mShuttingDownGuard) {
                mInPostStartupCrashRecovery = true;
            }
            Thread psrThread = new PostStartupCrashRecoveryThread(postStartupRecoveryOps);
            psrThread.start();
        }
    }
}
Also used : RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) ArrayList(java.util.ArrayList) Iterator(java.util.Iterator) IOException(java.io.IOException) File(java.io.File) Checkpoint(com.zimbra.cs.redolog.op.Checkpoint) IOException(java.io.IOException) MailServiceException(com.zimbra.cs.mailbox.MailServiceException)

Example 4 with RedoableOp

use of com.zimbra.cs.redolog.op.RedoableOp in project zm-mailbox by Zimbra.

the class RedoLogManager method getChangedMailboxesSince.

/**
     * Returns the set of mailboxes that had any committed changes since a
     * particular CommitId in the past, by scanning redologs.  Also returns
     * the last CommitId seen during the scanning process.
     * @param cid
     * @return can be null if server is shutting down
     * @throws IOException
     * @throws MailServiceException
     */
public Pair<Set<Integer>, CommitId> getChangedMailboxesSince(CommitId cid) throws IOException, MailServiceException {
    Set<Integer> mailboxes = new HashSet<Integer>();
    // Grab a read lock to prevent rollover.
    ReadLock readLock = mRWLock.readLock();
    try {
        readLock.lockInterruptibly();
    } catch (InterruptedException e) {
        synchronized (mShuttingDownGuard) {
            if (!mShuttingDown)
                ZimbraLog.redolog.error("InterruptedException during redo log scan for CommitId", e);
            else
                ZimbraLog.redolog.debug("Redo log scan for CommitId interrupted for shutdown");
        }
        return null;
    }
    File linkDir = null;
    File[] logs;
    try {
        try {
            long seq = cid.getRedoSeq();
            File[] archived = getArchivedLogsFromSequence(seq);
            if (archived != null) {
                logs = new File[archived.length + 1];
                System.arraycopy(archived, 0, logs, 0, archived.length);
                logs[archived.length] = mLogFile;
            } else {
                logs = new File[] { mLogFile };
            }
            // Make sure the first log has the sequence in cid.
            FileLogReader firstLog = new FileLogReader(logs[0]);
            if (firstLog.getHeader().getSequence() != seq) {
                // Most likely, the CommitId is too old.
                throw MailServiceException.INVALID_COMMIT_ID(cid.toString());
            }
            // Create a temp directory and make hard links to all redologs.
            // This prevents the logs from disappearing while being scanned.
            String dirName = "tmp-scan-" + System.currentTimeMillis();
            linkDir = new File(mLogFile.getParentFile(), dirName);
            if (linkDir.exists()) {
                int suffix = 1;
                while (linkDir.exists()) {
                    linkDir = new File(mLogFile.getParentFile(), dirName + "-" + suffix);
                }
            }
            if (!linkDir.mkdir())
                throw new IOException("Unable to create temp dir " + linkDir.getAbsolutePath());
            for (int i = 0; i < logs.length; i++) {
                File src = logs[i];
                File dest = new File(linkDir, logs[i].getName());
                IO.link(src.getAbsolutePath(), dest.getAbsolutePath());
                logs[i] = dest;
            }
        } finally {
            // We can let rollover happen now.
            readLock.unlock();
        }
        // Scan redologs to get list with IDs of mailboxes that have
        // committed changes since the given commit id.
        long lastSeq = -1;
        CommitTxn lastCommitTxn = null;
        boolean foundMarker = false;
        for (File logfile : logs) {
            FileLogReader logReader = new FileLogReader(logfile);
            logReader.open();
            lastSeq = logReader.getHeader().getSequence();
            try {
                RedoableOp op = null;
                while ((op = logReader.getNextOp()) != null) {
                    if (ZimbraLog.redolog.isDebugEnabled())
                        ZimbraLog.redolog.debug("Read: " + op);
                    if (!(op instanceof CommitTxn))
                        continue;
                    lastCommitTxn = (CommitTxn) op;
                    if (foundMarker) {
                        int mboxId = op.getMailboxId();
                        if (mboxId > 0)
                            mailboxes.add(mboxId);
                    } else {
                        if (cid.matches(lastCommitTxn))
                            foundMarker = true;
                    }
                }
            } catch (IOException e) {
                ZimbraLog.redolog.warn("IOException while reading redolog file", e);
            } finally {
                logReader.close();
            }
        }
        if (!foundMarker) {
            // Most likely, the CommitId is too old.
            throw MailServiceException.INVALID_COMMIT_ID(cid.toString());
        }
        CommitId lastCommitId = new CommitId(lastSeq, lastCommitTxn);
        return new Pair<Set<Integer>, CommitId>(mailboxes, lastCommitId);
    } finally {
        if (linkDir != null) {
            // Clean up the temp dir with links.
            try {
                if (linkDir.exists())
                    FileUtil.deleteDir(linkDir);
            } catch (IOException e) {
                ZimbraLog.redolog.warn("Unable to delete temporary directory " + linkDir.getAbsolutePath(), e);
            }
        }
    }
}
Also used : ReadLock(java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock) CommitTxn(com.zimbra.cs.redolog.op.CommitTxn) IOException(java.io.IOException) FileLogReader(com.zimbra.cs.redolog.logger.FileLogReader) Checkpoint(com.zimbra.cs.redolog.op.Checkpoint) RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) File(java.io.File) HashSet(java.util.HashSet) LinkedHashSet(java.util.LinkedHashSet) Pair(com.zimbra.common.util.Pair)

Example 5 with RedoableOp

use of com.zimbra.cs.redolog.op.RedoableOp in project zm-mailbox by Zimbra.

the class RedoLogManager method checkpoint.

/**
     * Should be called with write lock on mRWLock held.
     */
private void checkpoint() {
    LinkedHashSet<TransactionId> txns = null;
    synchronized (mActiveOps) {
        if (mActiveOps.size() == 0)
            return;
        // Create an empty LinkedHashSet and insert keys from mActiveOps
        // by iterating the keyset.
        txns = new LinkedHashSet<TransactionId>();
        for (Iterator<Map.Entry<TransactionId, RedoableOp>> it = mActiveOps.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<TransactionId, RedoableOp> entry = it.next();
            txns.add(entry.getKey());
        }
    }
    Checkpoint ckpt = new Checkpoint(txns);
    logOnly(ckpt, true);
}
Also used : Checkpoint(com.zimbra.cs.redolog.op.Checkpoint) RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) LinkedHashMap(java.util.LinkedHashMap) Map(java.util.Map)

Aggregations

RedoableOp (com.zimbra.cs.redolog.op.RedoableOp)14 IOException (java.io.IOException)9 Checkpoint (com.zimbra.cs.redolog.op.Checkpoint)5 File (java.io.File)4 HashSet (java.util.HashSet)4 Iterator (java.util.Iterator)4 LinkedHashMap (java.util.LinkedHashMap)4 Map (java.util.Map)4 ServiceException (com.zimbra.common.service.ServiceException)3 FileLogReader (com.zimbra.cs.redolog.logger.FileLogReader)3 CommitTxn (com.zimbra.cs.redolog.op.CommitTxn)3 StoreIncomingBlob (com.zimbra.cs.redolog.op.StoreIncomingBlob)3 ArrayList (java.util.ArrayList)3 Set (java.util.Set)3 NoSuchItemException (com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException)2 RedoLogProvider (com.zimbra.cs.redolog.RedoLogProvider)2 HashMap (java.util.HashMap)2 Entry (java.util.Map.Entry)2 ConcurrentHashMap (java.util.concurrent.ConcurrentHashMap)2 ConcurrentMap (java.util.concurrent.ConcurrentMap)2