use of com.zimbra.cs.mailbox.util.PagedDelete in project zm-mailbox by Zimbra.
the class PagedDeleteTest method testRemoveBeforeAfterCutoff.
@Test
public void testRemoveBeforeAfterCutoff() {
TypedIdList tombstone = new TypedIdList();
tombstone.add(Type.MESSAGE, 3, "", 100);
tombstone.add(Type.MESSAGE, 4, "", 101);
tombstone.add(Type.APPOINTMENT, 1, "", 101);
tombstone.add(Type.APPOINTMENT, 5, "", 100);
tombstone.add(Type.APPOINTMENT, 9, "", 103);
tombstone.add(Type.MESSAGE, 2, "", 99);
tombstone.add(Type.MESSAGE, 22, "", 105);
tombstone.add(Type.MESSAGE, 24, "", 103);
tombstone.add(Type.MESSAGE, 28, "", 106);
PagedDelete pgDelete = new PagedDelete(tombstone, true);
pgDelete.removeBeforeCutoff(4, 101);
Collection<Integer> ids = pgDelete.getAllIds();
Assert.assertEquals(5, ids.size());
pgDelete.trimDeletesTillPageLimit(5);
ids = pgDelete.getAllIds();
Assert.assertEquals(5, ids.size());
pgDelete.removeAfterCutoff(105);
ids = pgDelete.getAllIds();
Assert.assertEquals(4, ids.size());
Assert.assertTrue(ids.contains(4));
Assert.assertTrue(ids.contains(9));
Assert.assertTrue(ids.contains(24));
Assert.assertTrue(ids.contains(22));
Assert.assertTrue(pgDelete.isDeleteOverFlow());
Assert.assertEquals(pgDelete.getCutOffModsequnce(), 106);
Assert.assertEquals(pgDelete.getLastItemId(), 28);
Multimap<Type, Integer> ids2Type = pgDelete.getTypedItemIds();
Assert.assertEquals(4, ids2Type.size());
Assert.assertTrue(ids2Type.containsEntry(Type.MESSAGE, 4));
Assert.assertTrue(ids2Type.containsEntry(Type.APPOINTMENT, 9));
Assert.assertTrue(ids2Type.containsEntry(Type.MESSAGE, 24));
Assert.assertTrue(ids2Type.containsEntry(Type.MESSAGE, 22));
}
use of com.zimbra.cs.mailbox.util.PagedDelete in project zm-mailbox by Zimbra.
the class PagedDeleteTest method testRemoveBeforeCutoff.
@Test
public void testRemoveBeforeCutoff() {
TypedIdList tombstone = new TypedIdList();
tombstone.add(Type.MESSAGE, 3, "", 100);
tombstone.add(Type.MESSAGE, 4, "", 101);
tombstone.add(Type.APPOINTMENT, 1, "", 101);
tombstone.add(Type.APPOINTMENT, 5, "", 100);
tombstone.add(Type.APPOINTMENT, 9, "", 103);
tombstone.add(Type.MESSAGE, 2, "", 99);
tombstone.add(Type.MESSAGE, 22, "", 105);
tombstone.add(Type.MESSAGE, 24, "", 103);
PagedDelete pgDelete = new PagedDelete(tombstone, true);
pgDelete.removeBeforeCutoff(4, 101);
Collection<Integer> ids = pgDelete.getAllIds();
Assert.assertEquals(4, ids.size());
pgDelete.trimDeletesTillPageLimit(3);
ids = pgDelete.getAllIds();
Assert.assertEquals(3, ids.size());
Assert.assertTrue(ids.contains(4));
Assert.assertTrue(ids.contains(9));
Assert.assertTrue(ids.contains(24));
Assert.assertTrue(pgDelete.isDeleteOverFlow());
Assert.assertEquals(pgDelete.getCutOffModsequnce(), 105);
Assert.assertEquals(pgDelete.getLastItemId(), 22);
Multimap<Type, Integer> ids2Type = pgDelete.getTypedItemIds();
Assert.assertEquals(3, ids2Type.size());
Assert.assertTrue(ids2Type.containsEntry(Type.MESSAGE, 4));
Assert.assertTrue(ids2Type.containsEntry(Type.APPOINTMENT, 9));
Assert.assertTrue(ids2Type.containsEntry(Type.MESSAGE, 24));
}
use of com.zimbra.cs.mailbox.util.PagedDelete in project zm-mailbox by Zimbra.
the class PagedDeleteTest method testTrimDeletesTillPageLimit.
@Test
public void testTrimDeletesTillPageLimit() {
TypedIdList tombstone = new TypedIdList();
tombstone.add(Type.MESSAGE, 3, "", 100);
tombstone.add(Type.MESSAGE, 4, "", 101);
tombstone.add(Type.MESSAGE, 1, "", 101);
tombstone.add(Type.MESSAGE, 5, "", 100);
tombstone.add(Type.MESSAGE, 9, "", 103);
PagedDelete pgDelete = new PagedDelete(tombstone, false);
pgDelete.trimDeletesTillPageLimit(3);
Collection<Integer> ids = pgDelete.getAllIds();
Assert.assertEquals(3, ids.size());
Assert.assertTrue(ids.contains(3));
Assert.assertTrue(ids.contains(5));
Assert.assertTrue(ids.contains(1));
Assert.assertTrue(pgDelete.isDeleteOverFlow());
Assert.assertTrue(pgDelete.getCutOffModsequnce() == 101);
Assert.assertTrue(pgDelete.getLastItemId() == 4);
}
use of com.zimbra.cs.mailbox.util.PagedDelete in project zm-mailbox by Zimbra.
the class PagedDeleteTest method testTypedDeletesTillPageLimit.
@Test
public void testTypedDeletesTillPageLimit() {
TypedIdList tombstone = new TypedIdList();
tombstone.add(Type.MESSAGE, 3, "", 100);
tombstone.add(Type.MESSAGE, 4, "", 101);
tombstone.add(Type.APPOINTMENT, 1, "", 101);
tombstone.add(Type.APPOINTMENT, 5, "", 100);
tombstone.add(Type.MESSAGE, 9, "", 103);
PagedDelete pgDelete = new PagedDelete(tombstone, true);
pgDelete.trimDeletesTillPageLimit(3);
Collection<Integer> ids = pgDelete.getAllIds();
Assert.assertEquals(3, ids.size());
Assert.assertTrue(ids.contains(3));
Assert.assertTrue(ids.contains(5));
Assert.assertTrue(ids.contains(1));
Assert.assertTrue(pgDelete.isDeleteOverFlow());
Assert.assertTrue(pgDelete.getCutOffModsequnce() == 101);
Assert.assertTrue(pgDelete.getLastItemId() == 4);
Multimap<Type, Integer> ids2Type = pgDelete.getTypedItemIds();
Assert.assertEquals(3, ids2Type.size());
Assert.assertTrue(ids2Type.containsEntry(Type.MESSAGE, 3));
Assert.assertTrue(ids2Type.containsEntry(Type.APPOINTMENT, 5));
Assert.assertTrue(ids2Type.containsEntry(Type.APPOINTMENT, 1));
}
use of com.zimbra.cs.mailbox.util.PagedDelete in project zm-mailbox by Zimbra.
the class Sync method deltaSync.
private static String deltaSync(Element response, OperationContext octxt, ItemIdFormatter ifmt, Mailbox mbox, SyncToken syncToken, int deleteLimit, int changeLimit, boolean typedDeletes, Folder root, Set<Folder> visible, int messageSyncStart) throws ServiceException {
int begin = syncToken.getChangeId();
int deleteModSeqCutoff = syncToken.getDeleteModSeq();
deleteModSeqCutoff = deleteModSeqCutoff <= 0 ? begin : deleteModSeqCutoff;
int mboxLastChangeId = mbox.getLastChangeID();
SyncToken newSyncToken = new SyncToken(mboxLastChangeId);
if (begin >= mboxLastChangeId && deleteModSeqCutoff >= mboxLastChangeId) {
return newSyncToken.toString();
}
int changeItemIdCutoff = syncToken.getOffsetInNext();
int deleteItemIdCutoff = syncToken.getDeleteOffsetInNext();
// first, fetch deleted items
TypedIdList tombstones = mbox.getTombstones(deleteModSeqCutoff);
Element eDeleted = response.addElement(MailConstants.E_DELETED);
// then, put together the requested folder hierarchy in 2 different flavors
List<Folder> hierarchy = (root == null || root.getId() == Mailbox.ID_FOLDER_USER_ROOT ? null : root.getSubfolderHierarchy());
Set<Integer> targetIds = (root != null && root.getId() == Mailbox.ID_FOLDER_USER_ROOT ? null : new HashSet<Integer>(hierarchy == null ? 0 : hierarchy.size()));
if (hierarchy != null) {
for (Folder folder : hierarchy) {
targetIds.add(folder.getId());
}
}
// then, handle created/modified folders
if (octxt.isDelegatedRequest(mbox)) {
// first, make sure that something changed...
if (!mbox.getModifiedFolders(begin).isEmpty() || !Collections.disjoint(tombstones.types(), FOLDER_TYPES)) {
// special-case the folder hierarchy for delegated delta sync
boolean anyFolders = folderSync(response, octxt, ifmt, mbox, root, visible, -1, messageSyncStart, SyncPhase.DELTA);
// if no folders are visible, add an empty "<folder/>" as a hint
if (!anyFolders) {
response.addElement(MailConstants.E_FOLDER);
}
}
} else {
for (Folder folder : mbox.getModifiedFolders(begin)) {
// no case of "synthetic tombstone" (item falling out of the tree being synced)
if (targetIds == null || targetIds.contains(folder.getId())) {
ToXML.encodeFolder(response, ifmt, octxt, folder, Change.ALL_FIELDS);
} else {
tombstones.add(folder.getType(), folder.getId(), folder.getUuid(), folder.getModifiedSequence());
}
}
}
// next, handle created/modified tags
for (Tag tag : mbox.getModifiedTags(octxt, begin)) {
ToXML.encodeTag(response, ifmt, octxt, tag, Change.ALL_FIELDS);
}
// finally, handle created/modified "other items"
int itemCount = 0;
Pair<List<Integer>, TypedIdList> changed = mbox.getModifiedItems(octxt, Math.min(begin, deleteModSeqCutoff), messageSyncStart, MailItem.Type.UNKNOWN, targetIds, deleteModSeqCutoff);
List<Integer> modified = changed.getFirst();
// items that have been altered in non-visible folders will be returned as "deleted" in order to handle moves
if (changed.getSecond() != null) {
tombstones.addAll(changed.getSecond());
}
delta: while (!modified.isEmpty()) {
List<Integer> batch = modified.subList(0, Math.min(modified.size(), FETCH_BATCH_SIZE));
for (MailItem item : mbox.getItemById(octxt, batch, MailItem.Type.UNKNOWN)) {
// detect interrupted sync and resume from the appropriate place
if ((item.getModifiedSequence() == begin + 1 && item.getId() < changeItemIdCutoff) || item.getModifiedSequence() <= begin) {
//if interrupted delete and un-interrupted modifications.
continue;
}
// if we've overflowed this sync response, set things up so that a subsequent sync starts from where we're cutting off
if (itemCount >= changeLimit) {
response.addAttribute(MailConstants.A_QUERY_MORE, true);
newSyncToken.setChangeModSeq((item.getModifiedSequence() - 1));
newSyncToken.setChangeItemId(item.getId());
newSyncToken.setDeleteModSeq(mboxLastChangeId);
break delta;
}
// For items in the system, if the content has changed since the user last sync'ed
// (because it was edited or created), just send back the folder ID and saved date --
// the client will request the whole object out of band -- potentially using the
// content servlet's "include metadata in headers" hack.
// If it's just the metadata that changed, send back the set of mutable attributes.
boolean created = item.getSavedSequence() > begin;
ToXML.encodeItem(response, ifmt, octxt, item, created ? Change.FOLDER | Change.CONFLICT | Change.DATE | Change.PARENT : MUTABLE_FIELDS);
itemCount++;
}
batch.clear();
}
// cleanup: only return a <deleted> element if we're sending back deleted item ids
if ((deleteLimit > 0 && tombstones.size() > deleteLimit) || deleteItemIdCutoff > 0) {
PagedDelete pgDel = new PagedDelete(tombstones, typedDeletes);
pgDel.removeBeforeCutoff(deleteItemIdCutoff, deleteModSeqCutoff);
if (deleteLimit > 0) {
pgDel.trimDeletesTillPageLimit(deleteLimit);
}
encodePagedDelete(eDeleted, pgDel, newSyncToken, tombstones, typedDeletes);
if (pgDel.isDeleteOverFlow()) {
response.addAttribute(MailConstants.A_QUERY_MORE, true);
response.addAttribute(MailConstants.A_QUERY_MORE, true);
}
} else {
encodeUnpagedDelete(eDeleted, tombstones, typedDeletes);
}
return newSyncToken.toString();
}
Aggregations