Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Handle 410 errors by early-exiting sync
This commit ensures that if the sync token or timeMin value for a
notebook is invalid, we immediately abort sync, and then clear the
sync token stored in the notebook's custom property as well as
update the syncDate to an appropriate value.
  • Loading branch information
Chris Adams committed Jan 29, 2020
1 parent 039257d commit fb592dd
Showing 1 changed file with 35 additions and 27 deletions.
62 changes: 35 additions & 27 deletions src/google/google-calendars/googlecalendarsyncadaptor.cpp
Expand Up @@ -1003,12 +1003,39 @@ void GoogleCalendarSyncAdaptor::finalCleanup()
// there is only one account per sync run, even though we haven't fully
// cleaned up the multi-account-isms from the member variables / API.
int accountId = m_syncSucceeded.keys().first();
if (m_syncSucceeded[accountId]) {
if (!m_syncSucceeded[accountId]) {
// sync failed. check to see if we need to apply any changes to the database.
QSet<QString> calendarsRequiringChange;
for (const QString &calendarId : m_timeMinFailure) {
calendarsRequiringChange.insert(calendarId);
}
for (const QString &calendarId : m_syncTokenFailure) {
calendarsRequiringChange.insert(calendarId);
}
const KDateTime yesterdayDate = KDateTime::currentDateTime(KDateTime::Spec::UTC()).addDays(-1);
for (const QString &calendarId : calendarsRequiringChange) {
// this codepath is hit if the server replied with HTTP 410 for the sync token or timeMin value.
if (mKCal::Notebook::Ptr notebook = notebookForCalendarId(accountId, calendarId)) {
if (m_syncTokenFailure.contains(calendarId)) {
// this sync cycle failed due to the sync token being invalidated server-side.
// trigger clean sync with wide time span on next sync.
notebook->setSyncDate(KDateTime());
} else if (m_timeMinFailure.contains(calendarId)) {
// this sync cycle failed due to the timeMin value being too far in the past.
// trigger clean sync with short time span on next sync.
notebook->setSyncDate(yesterdayDate);
}
notebook->setCustomProperty(NOTEBOOK_SERVER_SYNC_TOKEN_PROPERTY, QString());
m_storage->updateNotebook(notebook);
m_storageNeedsSave = true;
}
}
} else {
// sync succeeded. apply the changes to the database.
applyRemoteChangesLocally(accountId);
if (!m_syncSucceeded[accountId]) {
SOCIALD_LOG_INFO("Error occurred while applying remote changes locally");
} else {
// also update the remote sync timestamp in each notebook.
Q_FOREACH (const QString &updatedCalendarId, m_calendarsFinishedRequested.keys()) {
// Update the sync date for the notebook, to the timestamp reported by Google
// in the calendar request for the remote calendar associated with the notebook,
Expand Down Expand Up @@ -1039,21 +1066,11 @@ void GoogleCalendarSyncAdaptor::finalCleanup()
notebook->setSyncDate(syncDate);
}
} else {
// this codepath is hit if the server replied with HTTP 410 for the sync token or timeMin value.
if (m_syncTokenFailure.contains(updatedCalendarId)) {
// this sync cycle failed due to the sync token being invalidated server-side.
// trigger clean sync with wide time span on next sync.
notebook->setSyncDate(KDateTime());
} else if (m_timeMinFailure.contains(updatedCalendarId)) {
// this sync cycle failed due to the timeMin value being too far in the past.
// trigger clean sync with short time span on next sync.
notebook->setSyncDate(yesterdayDate);
} else {
SOCIALD_LOG_ERROR("Error: no update timestamp, but no error detected!");
notebook->setSyncDate(yesterdayDate);
}
SOCIALD_LOG_ERROR("Error: no update timestamp, but no error detected!");
notebook->setSyncDate(yesterdayDate);
}

// also update the remote sync token in each notebook.
notebook->setCustomProperty(NOTEBOOK_SERVER_SYNC_TOKEN_PROPERTY,
m_calendarsNextSyncTokens.value(updatedCalendarId));
m_storage->updateNotebook(notebook);
Expand Down Expand Up @@ -1536,8 +1553,8 @@ void GoogleCalendarSyncAdaptor::eventsFinishedHandler()
} else {
SOCIALD_LOG_ERROR("unable to parse event data from request with account" << accountId << "; got:");
errorDumpStr(QString::fromUtf8(replyData.constData()));
m_syncSucceeded[accountId] = false;
}
m_syncSucceeded[accountId] = false;
}

if (!fetchingNextPage) {
Expand Down Expand Up @@ -1580,21 +1597,12 @@ void GoogleCalendarSyncAdaptor::finishedRequestingRemoteEvents(int accountId, co
return; // still waiting for more requests to finish.
}

if (syncAborted()) {
return; // sync was aborted before we received all remote data, and before we could upsync local changes.
if (syncAborted() || !m_syncSucceeded[accountId]) {
return; // sync was aborted or failed before we received all remote data, and before we could upsync local changes.
}

// determine local changes to upsync.
Q_FOREACH (const QString &finishedCalendarId, m_calendarsFinishedRequested.keys()) {
// only determine changes and attempt to finish sync cycle if the server didn't respond with a 410 sync token error.
if (m_syncTokenFailure.contains(finishedCalendarId)) {
SOCIALD_LOG_DEBUG("skipping calculating sync delta due to 410 sync token error during sync");
continue;
} else if (m_timeMinFailure.contains(finishedCalendarId)) {
SOCIALD_LOG_DEBUG("skipping calculating sync delta due to 410 updated min error during sync");
continue;
}

// now upsync the local changes to the remote server
QList<UpsyncChange> changesToUpsync = determineSyncDelta(accountId, accessToken, finishedCalendarId, m_calendarsSyncDate.value(finishedCalendarId));
if (changesToUpsync.size()) {
Expand Down

0 comments on commit fb592dd

Please sign in to comment.