Increase timeouts for Exchange sync; prevent early upload sync

* It's reported that 50% of third party users have issues syncing
  Calendar in Exchange
* In testing, it was determined that the server takes > 30 seconds
  to respond to a sync request initially, which is beyond our timeout
  limit
* Also, I found that the system SyncManager was trying to trigger an
  upload sync at the same time (i.e. before the sync session was
  established with the server)
* There are four changes here:
  1) Prevent upload syncs while the sync key is null or "0" ("0"
     is the initial state)
  2) Increase timeout for connection; at worst, this will
     cause a short extra delay in syncs with a bad connection, but this
     will be unnoticable to users
  3) Increase the read timeout for initial sync to twice that of
     regular syncs (the initial sync always seems to take longer)
  3) Reduce the lookback for calendar to two weeks (from one month);
     this is a better default anyway, and it probably reduces the server
     and client load a great deal
* Empirically, this solves the bug for a known completely repeatable
  case.

Bug: 2569162
Change-Id: I36b1c3e1e0b65f50d42e05f1830fed912191651f
This commit is contained in:
Marc Blank 2010-04-12 11:39:05 -07:00
parent c3aa318200
commit 2e22263777
2 changed files with 32 additions and 8 deletions

View File

@ -45,6 +45,10 @@ public class CalendarSyncAdapterService extends Service {
MailboxColumns.ACCOUNT_KEY + "=? AND " + MailboxColumns.TYPE + '=' + Mailbox.TYPE_CALENDAR;
private static final String DIRTY_IN_ACCOUNT =
Events._SYNC_DIRTY + "=1 AND " + Events._SYNC_ACCOUNT + "=?";
private static final String[] ID_SYNC_KEY_PROJECTION =
new String[] {MailboxColumns.ID, MailboxColumns.SYNC_KEY};
private static final int ID_SYNC_KEY_MAILBOX_ID = 0;
private static final int ID_SYNC_KEY_SYNC_KEY = 1;
public CalendarSyncAdapterService() {
super();
@ -119,15 +123,22 @@ public class CalendarSyncAdapterService extends Service {
if (accountCursor.moveToFirst()) {
long accountId = accountCursor.getLong(0);
// Now, find the calendar mailbox associated with the account
Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, EmailContent.ID_PROJECTION,
Cursor mailboxCursor = cr.query(Mailbox.CONTENT_URI, ID_SYNC_KEY_PROJECTION,
ACCOUNT_AND_TYPE_CALENDAR, new String[] {Long.toString(accountId)}, null);
try {
if (mailboxCursor.moveToFirst()) {
if (logging) {
Log.d(TAG, "Upload sync requested for " + account.name);
}
String syncKey = mailboxCursor.getString(ID_SYNC_KEY_SYNC_KEY);
if ((syncKey == null) || (syncKey.equals("0"))) {
if (logging) {
Log.d(TAG, "Can't sync; mailbox in initial state");
}
return;
}
// Ask for a sync from our sync manager
SyncManager.serviceRequest(mailboxCursor.getLong(0),
SyncManager.serviceRequest(mailboxCursor.getLong(ID_SYNC_KEY_MAILBOX_ID),
SyncManager.SYNC_UPSYNC);
}
} finally {

View File

@ -120,7 +120,14 @@ public class EasSyncService extends AbstractSyncService {
static private final int CHUNK_SIZE = 16*1024;
static private final String PING_COMMAND = "Ping";
// Command timeout is the the time allowed for reading data from an open connection before an
// IOException is thrown. After a small added allowance, our watchdog alarm goes off (allowing
// us to detect a silently dropped connection). The allowance is defined below.
static private final int COMMAND_TIMEOUT = 20*SECONDS;
// Connection timeout is the time given to connect to the server before reporting an IOException
static private final int CONNECTION_TIMEOUT = 30*SECONDS;
// The extra time allowed beyond the COMMAND_TIMEOUT before which our watchdog alarm triggers
static private final int WATCHDOG_TIMEOUT_ALLOWANCE = 10*SECONDS;
static private final String AUTO_DISCOVER_SCHEMA_PREFIX =
"http://schemas.microsoft.com/exchange/autodiscover/mobilesync/";
@ -1034,7 +1041,7 @@ public class EasSyncService extends AbstractSyncService {
private HttpClient getHttpClient(int timeout) {
HttpParams params = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(params, 15*SECONDS);
HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, timeout);
HttpConnectionParams.setSocketBufferSize(params, 8192);
HttpClient client = new DefaultHttpClient(getClientConnectionManager(), params);
@ -1078,7 +1085,7 @@ public class EasSyncService extends AbstractSyncService {
boolean isPingCommand) throws IOException {
synchronized(getSynchronizer()) {
mPendingPost = method;
long alarmTime = timeout+(10*SECONDS);
long alarmTime = timeout + WATCHDOG_TIMEOUT_ALLOWANCE;
if (isPingCommand) {
SyncManager.runAsleep(mMailboxId, alarmTime);
} else {
@ -1878,9 +1885,14 @@ public class EasSyncService extends AbstractSyncService {
.data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId)
.tag(Tags.SYNC_DELETES_AS_MOVES);
// EAS doesn't like GetChanges if the syncKey is "0"; not documented
// Start with the default timeout
int timeout = COMMAND_TIMEOUT;
if (!syncKey.equals("0")) {
// EAS doesn't like GetChanges if the syncKey is "0"; not documented
s.tag(Tags.SYNC_GET_CHANGES);
} else {
// Use 2x timeout for initial sync, which empirically can take a while longer
timeout <<= 1;
}
s.data(Tags.SYNC_WINDOW_SIZE,
className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
@ -1891,8 +1903,8 @@ public class EasSyncService extends AbstractSyncService {
if (className.equals("Email")) {
s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter());
} else if (className.equals("Calendar")) {
// TODO Force one month for calendar until we can set this!
s.data(Tags.SYNC_FILTER_TYPE, Eas.FILTER_1_MONTH);
// TODO Force two weeks for calendar until we can set this!
s.data(Tags.SYNC_FILTER_TYPE, Eas.FILTER_2_WEEKS);
}
// Set the truncation amount for all classes
if (mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
@ -1911,7 +1923,8 @@ public class EasSyncService extends AbstractSyncService {
target.sendLocalChanges(s);
s.end().end().end().done();
HttpResponse resp = sendHttpClientPost("Sync", s.toByteArray());
HttpResponse resp = sendHttpClientPost("Sync", new ByteArrayEntity(s.toByteArray()),
timeout);
int code = resp.getStatusLine().getStatusCode();
if (code == HttpStatus.SC_OK) {
InputStream is = resp.getEntity().getContent();