use of com.zimbra.cs.mailbox.MailItem.PendingDelete 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);
}
}
}
}
}
use of com.zimbra.cs.mailbox.MailItem.PendingDelete in project zm-mailbox by Zimbra.
the class Mailbox method purgeImapDeleted.
public void purgeImapDeleted(OperationContext octxt) throws ServiceException {
PurgeImapDeleted redoRecorder = new PurgeImapDeleted(mId);
boolean success = false;
try {
beginTransaction("purgeImapDeleted", octxt, redoRecorder);
Set<Folder> purgeable = getAccessibleFolders((short) (ACL.RIGHT_READ | ACL.RIGHT_DELETE));
// short-circuit the DB call if we're tracking \Deleted counts and they're all 0
boolean skipDB = false;
if (getVersion().atLeast(1, 9)) {
int deleted = 0;
for (Folder folder : purgeable != null ? purgeable : listAllFolders()) {
deleted += folder.getDeletedCount();
}
skipDB = deleted == 0;
}
if (!skipDB) {
PendingDelete info = DbTag.getImapDeleted(this, purgeable);
MailItem.delete(this, info, null, true, false);
}
success = true;
} finally {
endTransaction(success);
}
}
use of com.zimbra.cs.mailbox.MailItem.PendingDelete in project zm-mailbox by Zimbra.
the class DbMailItem method getLeafNodes.
public static PendingDelete getLeafNodes(Mailbox mbox, List<Folder> folders, int before, boolean globalMessages, Boolean unread, boolean useChangeDate, Integer maxItems) throws ServiceException {
DbConnection conn = mbox.getOperationConnection();
PreparedStatement stmt = null;
ResultSet rs = null;
if (folders == null) {
folders = Collections.emptyList();
}
try {
StringBuilder constraint = new StringBuilder();
String dateColumn = (useChangeDate ? "change_date" : "date");
if (globalMessages) {
constraint.append(dateColumn).append(" < ? AND ").append(typeIn(MailItem.Type.MESSAGE));
} else {
constraint.append(dateColumn).append(" < ? AND type NOT IN ").append(NON_SEARCHABLE_TYPES);
if (!folders.isEmpty()) {
constraint.append(" AND ").append(DbUtil.whereIn("folder_id", folders.size()));
}
}
if (unread != null) {
constraint.append(" AND unread = ?");
}
String orderByLimit = "";
if (maxItems != null && Db.supports(Db.Capability.LIMIT_CLAUSE)) {
orderByLimit = " ORDER BY " + dateColumn + " " + Db.getInstance().limit(maxItems);
}
stmt = conn.prepareStatement("SELECT " + LEAF_NODE_FIELDS + " FROM " + getMailItemTableName(mbox) + " WHERE " + IN_THIS_MAILBOX_AND + constraint + orderByLimit);
if (globalMessages || getTotalFolderSize(folders) > RESULTS_STREAMING_MIN_ROWS) {
Db.getInstance().enableStreaming(stmt);
}
int pos = 1;
pos = setMailboxId(stmt, mbox, pos);
stmt.setInt(pos++, before);
if (!globalMessages) {
for (Folder folder : folders) {
stmt.setInt(pos++, folder.getId());
}
}
if (unread != null) {
stmt.setBoolean(pos++, unread);
}
PendingDelete info = accumulateDeletionInfo(mbox, stmt);
stmt = null;
return info;
} catch (SQLException e) {
throw ServiceException.FAILURE("fetching list of items for purge", e);
} finally {
DbPool.closeResults(rs);
DbPool.closeStatement(stmt);
}
}
use of com.zimbra.cs.mailbox.MailItem.PendingDelete in project zm-mailbox by Zimbra.
the class DbMailItem method accumulateDeletionInfo.
/** Note: In all cases, this method closes the passed-in {@code Statement}.
* The caller must take care not to close it a second time. */
static PendingDelete accumulateDeletionInfo(Mailbox mbox, PreparedStatement stmt) throws ServiceException {
ResultSet rs = null;
try {
rs = stmt.executeQuery();
PendingDelete info = new PendingDelete();
info.size = 0;
List<Integer> versionedIds = accumulateLeafNodes(info, mbox, rs);
rs.close();
rs = null;
stmt.close();
stmt = null;
accumulateLeafRevisions(info, mbox, versionedIds);
accumulateComments(info, mbox);
return info;
} catch (SQLException e) {
throw ServiceException.FAILURE("accumulating deletion info", e);
} finally {
DbPool.closeResults(rs);
DbPool.closeStatement(stmt);
}
}
use of com.zimbra.cs.mailbox.MailItem.PendingDelete in project zm-mailbox by Zimbra.
the class DbTag method getImapDeleted.
public static PendingDelete getImapDeleted(Mailbox mbox, Set<Folder> folders) throws ServiceException {
if (folders != null && folders.isEmpty()) {
return new PendingDelete();
}
DbConnection conn = mbox.getOperationConnection();
PreparedStatement stmt = null;
ResultSet rs = null;
try {
String mailboxesMatchAnd = DebugConfig.disableMailboxGroups ? "" : "mi.mailbox_id = ti.mailbox_id AND ";
String folderconstraint = folders == null ? "" : " AND " + DbUtil.whereIn("folder_id", folders.size());
stmt = conn.prepareStatement("SELECT " + DbMailItem.LEAF_NODE_FIELDS + " FROM " + DbMailItem.getMailItemTableName(mbox, "mi") + " INNER JOIN " + getTaggedItemTableName(mbox, "ti") + " ON " + mailboxesMatchAnd + "ti.item_id = mi.id" + " WHERE " + inThisMailboxAnd("ti") + "ti.tag_id = " + Flag.ID_DELETED + " AND type IN " + DbMailItem.IMAP_TYPES + folderconstraint);
if (DbMailItem.getTotalFolderSize(folders) > DbMailItem.RESULTS_STREAMING_MIN_ROWS) {
Db.getInstance().enableStreaming(stmt);
}
int pos = 1;
pos = DbMailItem.setMailboxId(stmt, mbox, pos);
if (folders != null) {
for (Folder folder : folders) {
stmt.setInt(pos++, folder.getId());
}
}
PendingDelete info = DbMailItem.accumulateDeletionInfo(mbox, stmt);
stmt = null;
return info;
} catch (SQLException e) {
throw ServiceException.FAILURE("fetching list of \\Deleted items for purge", e);
} finally {
DbPool.closeResults(rs);
DbPool.closeStatement(stmt);
}
}
Aggregations