Search in sources :

Example 1 with AllAccountsRedoCommitCallback

use of com.zimbra.cs.session.AllAccountsRedoCommitCallback in project zm-mailbox by Zimbra.

the class Mailbox method endTransaction.

/**
     * Be very careful when changing code in this method.  The order of almost
     * every line of code is important to ensure correct redo logging and crash
     * recovery.
     *
     * @param success true to commit the transaction, false to rollback
     * @throws ServiceException error
     */
protected void endTransaction(boolean success) throws ServiceException {
    assert !Thread.holdsLock(this) : "Use MailboxLock";
    if (lock.isUnlocked()) {
        ZimbraLog.mailbox.warn("transaction canceled because of lock failure");
        assert (!success);
        return;
    }
    // blob and index to delete
    PendingDelete deletes = null;
    // blob to delete for failure cases
    List<Object> rollbackDeletes = null;
    try {
        if (!currentChange().isActive()) {
            // would like to throw here, but it might cover another
            // exception...
            ZimbraLog.mailbox.warn("cannot end a transaction when not inside a transaction", new Exception());
            return;
        }
        if (!currentChange().endChange()) {
            return;
        }
        ServiceException exception = null;
        if (success) {
            List<IndexItemEntry> indexItems = currentChange().indexItems;
            if (!indexItems.isEmpty()) {
                assert (currentChange().writeChange);
                //TODO: See bug 15072 - we need to clear mCurrentChange.indexItems (it is stored in a temporary) here,
                // just in case item.reindex() recurses into a new transaction...
                currentChange().indexItems = new ArrayList<IndexItemEntry>();
                index.add(indexItems);
            }
            // update mailbox size, folder unread/message counts
            try {
                snapshotCounts();
            } catch (ServiceException e) {
                exception = e;
                success = false;
            }
        }
        DbConnection conn = currentChange().conn;
        // transaction, so no redo cleanup is necessary.
        if (!success) {
            DbPool.quietRollback(conn);
            rollbackDeletes = rollbackCache(currentChange());
            if (exception != null) {
                throw exception;
            }
            return;
        }
        RedoableOp redoRecorder = currentChange().recorder;
        boolean needRedo = needRedo(currentChange().octxt, redoRecorder);
        // Log the change redo record for main transaction.
        if (redoRecorder != null && needRedo) {
            redoRecorder.log(true);
        }
        boolean dbCommitSuccess = false;
        try {
            // Commit the main transaction in database.
            if (conn != null) {
                try {
                    conn.commit();
                } catch (Throwable t) {
                    // Any exception during database commit is a disaster
                    // because we don't know if the change is committed or
                    // not.  Force the server to abort.  Next restart will
                    // redo the operation to ensure the change is made and
                    // committed.  (bug 2121)
                    Zimbra.halt("Unable to commit database transaction.  Forcing server to abort.", t);
                }
            }
            dbCommitSuccess = true;
        } finally {
            if (!dbCommitSuccess) {
                // recovery will try to redo the operation.
                if (needRedo) {
                    if (redoRecorder != null) {
                        redoRecorder.abort();
                    }
                }
                DbPool.quietRollback(conn);
                rollbackDeletes = rollbackCache(currentChange());
                return;
            }
        }
        if (needRedo) {
            // case would result in a redo error, and the second case would index the wrong value.
            if (redoRecorder != null) {
                if (currentChange().dirty != null && !currentChange().dirty.changedTypes.isEmpty()) {
                    // if an "all accounts" waitset is active, and this change has an appropriate type,
                    // then we'll need to set a commit-callback
                    AllAccountsRedoCommitCallback cb = AllAccountsRedoCommitCallback.getRedoCallbackIfNecessary(getAccountId(), currentChange().dirty.changedTypes);
                    if (cb != null) {
                        redoRecorder.setCommitCallback(cb);
                    }
                }
                redoRecorder.commit();
            }
        }
        boolean changeMade = currentChange().changeId != MailboxChange.NO_CHANGE;
        // keep a reference for cleanup
        deletes = currentChange().deletes;
        // deletes outside the lock
        // We are finally done with database and redo commits. Cache update
        // comes last.
        commitCache(currentChange());
        // down in its call stack.
        if (changeMade) {
            index.maybeIndexDeferredItems();
        }
    } finally {
        lock.release();
        // entail a blocking network operation
        if (deletes != null) {
            if (!deletes.indexIds.isEmpty()) {
                // delete any index entries associated with items deleted from db
                index.delete(deletes.indexIds);
            }
            if (deletes.blobs != null) {
                // delete any blobs associated with items deleted from db/index
                StoreManager sm = StoreManager.getInstance();
                for (MailboxBlob blob : deletes.blobs) {
                    sm.quietDelete(blob);
                }
            }
        }
        if (rollbackDeletes != null) {
            StoreManager sm = StoreManager.getInstance();
            for (Object obj : rollbackDeletes) {
                if (obj instanceof MailboxBlob) {
                    sm.quietDelete((MailboxBlob) obj);
                } else if (obj instanceof Blob) {
                    sm.quietDelete((Blob) obj);
                }
            }
        }
    }
}
Also used : StoreIncomingBlob(com.zimbra.cs.redolog.op.StoreIncomingBlob) StagedBlob(com.zimbra.cs.store.StagedBlob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) Blob(com.zimbra.cs.store.Blob) MailboxBlob(com.zimbra.cs.store.MailboxBlob) AllAccountsRedoCommitCallback(com.zimbra.cs.session.AllAccountsRedoCommitCallback) AccountServiceException(com.zimbra.cs.account.AccountServiceException) IOException(java.io.IOException) NoSuchItemException(com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException) MessageChannelException(com.zimbra.cs.iochannel.MessageChannelException) ServiceException(com.zimbra.common.service.ServiceException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) DbConnection(com.zimbra.cs.db.DbPool.DbConnection) StoreManager(com.zimbra.cs.store.StoreManager) AccountServiceException(com.zimbra.cs.account.AccountServiceException) ServiceException(com.zimbra.common.service.ServiceException) RedoableOp(com.zimbra.cs.redolog.op.RedoableOp) PendingDelete(com.zimbra.cs.mailbox.MailItem.PendingDelete)

Aggregations

ServiceException (com.zimbra.common.service.ServiceException)1 AccountServiceException (com.zimbra.cs.account.AccountServiceException)1 DbConnection (com.zimbra.cs.db.DbPool.DbConnection)1 MessageChannelException (com.zimbra.cs.iochannel.MessageChannelException)1 PendingDelete (com.zimbra.cs.mailbox.MailItem.PendingDelete)1 NoSuchItemException (com.zimbra.cs.mailbox.MailServiceException.NoSuchItemException)1 RedoableOp (com.zimbra.cs.redolog.op.RedoableOp)1 StoreIncomingBlob (com.zimbra.cs.redolog.op.StoreIncomingBlob)1 AllAccountsRedoCommitCallback (com.zimbra.cs.session.AllAccountsRedoCommitCallback)1 Blob (com.zimbra.cs.store.Blob)1 MailboxBlob (com.zimbra.cs.store.MailboxBlob)1 StagedBlob (com.zimbra.cs.store.StagedBlob)1 StoreManager (com.zimbra.cs.store.StoreManager)1 IOException (java.io.IOException)1 UnsupportedEncodingException (java.io.UnsupportedEncodingException)1