Allow limited looping requests in sync

* Microsoft has documented cases in which the server can continue to
  send MoreAvailable=true even when no new data is received.  This
  can cause looping behavior, which we stop when we recognize it.
* This workaround, however, can prevent the situation from resolving
  itself, and lead to delayed sync (up to a few hours has been noticed)
* In this limited CL, we allow the sync to loop up to a maximum number
  of times before stopping it forcibly.

Bug: 2685984
Change-Id: I85981b85b71c4e7d53e69da2520543e8ef04c889
This commit is contained in:
Marc Blank 2010-05-19 09:50:09 -07:00
parent 2ac2c224d6
commit e034a5e366
4 changed files with 44 additions and 4 deletions

View File

@ -158,6 +158,10 @@ public class EasSyncService extends AbstractSyncService {
static private final int PING_HEARTBEAT_INCREMENT = 3*PING_MINUTES;
static private final int PING_FORCE_HEARTBEAT = 2*PING_MINUTES;
// Maximum number of times we'll allow a sync to "loop" with MoreAvailable true before
// forcing it to stop. This number has been determined empirically.
static private final int MAX_LOOPING_COUNT = 100;
static private final int PROTOCOL_PING_STATUS_COMPLETED = 1;
// The amount of time we allow for a thread to release its post lock after receiving an alert
@ -1926,6 +1930,7 @@ public class EasSyncService extends AbstractSyncService {
Mailbox mailbox = target.mMailbox;
boolean moreAvailable = true;
int loopingCount = 0;
while (!mStop && moreAvailable) {
// If we have no connectivity, just exit cleanly. SyncManager will start us up again
// when connectivity has returned
@ -2025,6 +2030,18 @@ public class EasSyncService extends AbstractSyncService {
InputStream is = resp.getEntity().getContent();
if (is != null) {
moreAvailable = target.parse(is);
if (target.isLooping()) {
loopingCount++;
userLog("** Looping: " + loopingCount);
// After the maximum number of loops, we'll set moreAvailable to false and
// allow the sync loop to terminate
if (moreAvailable && (loopingCount > MAX_LOOPING_COUNT)) {
userLog("** Looping force stopped");
moreAvailable = false;
}
} else {
loopingCount = 0;
}
target.cleanup();
} else {
userLog("Empty input stream in sync command response");

View File

@ -57,6 +57,10 @@ public abstract class AbstractSyncAdapter {
public abstract void cleanup();
public abstract boolean isSyncable();
public boolean isLooping() {
return false;
}
public AbstractSyncAdapter(Mailbox mailbox, EasSyncService service) {
mMailbox = mailbox;
mService = service;

View File

@ -45,6 +45,8 @@ public abstract class AbstractSyncParser extends Parser {
protected ContentResolver mContentResolver;
protected AbstractSyncAdapter mAdapter;
private boolean mLooping;
public AbstractSyncParser(InputStream in, AbstractSyncAdapter adapter) throws IOException {
super(in);
mAdapter = adapter;
@ -78,6 +80,10 @@ public abstract class AbstractSyncParser extends Parser {
*/
public abstract void wipe();
public boolean isLooping() {
return mLooping;
}
/**
* Loop through the top-level structure coming from the Exchange server
* Sync keys and the more available flag are handled here, whereas specific data parsing
@ -89,7 +95,7 @@ public abstract class AbstractSyncParser extends Parser {
boolean moreAvailable = false;
boolean newSyncKey = false;
int interval = mMailbox.mSyncInterval;
mLooping = false;
// If we're not at the top of the xml tree, throw an exception
if (nextTag(START_DOCUMENT) != Tags.SYNC_SYNC) {
throw new EasParserException();
@ -154,8 +160,7 @@ public abstract class AbstractSyncParser extends Parser {
// If we don't have a new sync key, ignore moreAvailable (or we'll loop)
if (moreAvailable && !newSyncKey) {
userLog("!! SyncKey hasn't changed, setting moreAvailable = false");
moreAvailable = false;
mLooping = true;
}
// Commit any changes

View File

@ -81,6 +81,9 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
// Holds the parser's value for isLooping()
boolean mIsLooping = false;
public EmailSyncAdapter(Mailbox mailbox, EasSyncService service) {
super(mailbox, service);
}
@ -88,7 +91,18 @@ public class EmailSyncAdapter extends AbstractSyncAdapter {
@Override
public boolean parse(InputStream is) throws IOException {
EasEmailSyncParser p = new EasEmailSyncParser(is, this);
return p.parse();
boolean res = p.parse();
// Hold on to the parser's value for isLooping() to pass back to the service
mIsLooping = p.isLooping();
return res;
}
/**
* Return the value of isLooping() as returned from the parser
*/
@Override
public boolean isLooping() {
return mIsLooping;
}
@Override