1
0
mirror of https://github.com/etesync/android synced 2025-02-08 21:52:47 +00:00

SyncManager: Use the last journal id as the ctag, instead of storing it.

We were storing the ctag separately although the data was already
present in the journal. The last entry's ID is always the CTAG.
This could cause issues if sync is aborted exactly at the right time.
I managed to trigger this issue on rare cases.
This commit is contained in:
Tom Hacohen 2017-04-06 23:02:01 +01:00
parent 338dd5e075
commit 4e0cd7f554
6 changed files with 30 additions and 103 deletions

View File

@ -82,6 +82,15 @@ public class JournalModel {
} }
return journalEntity; return journalEntity;
} }
public String getLastUid(EntityDataStore<Persistable> data) {
EntryEntity last = data.select(EntryEntity.class).where(EntryEntity.JOURNAL.eq(this)).orderBy(EntryEntity.ID.desc()).limit(1).get().firstOrNull();
if (last != null) {
return last.getUid();
}
return null;
}
} }
@Entity @Entity

View File

@ -304,24 +304,6 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
} }
} }
@Override
public String getCTag() throws ContactsStorageException {
synchronized (syncState) {
readSyncState();
return syncState.getString(SYNC_STATE_CTAG);
}
}
@Override
public void setCTag(String cTag) throws ContactsStorageException {
synchronized (syncState) {
readSyncState();
syncState.putString(SYNC_STATE_CTAG, cTag);
writeSyncState();
}
}
// HELPERS // HELPERS
public static void onRenameAccount(@NonNull ContentResolver resolver, @NonNull String oldName, @NonNull String newName) throws RemoteException { public static void onRenameAccount(@NonNull ContentResolver resolver, @NonNull String oldName, @NonNull String newName) throws RemoteException {
@ -334,9 +316,9 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
} }
/** Fix all of the etags of all of the non-dirty contacts to be non-null. /** Fix all of the etags of all of the non-dirty contacts to be non-null.
* Currently set to the ctag. */ * Currently set to all ones. */
public void fixEtags() throws ContactsStorageException { public void fixEtags() throws ContactsStorageException {
String newEtag = getCTag(); String newEtag = "1111111111111111111111111111111111111111111111111111111111111111";
String where = ContactsContract.RawContacts.DIRTY + "=0 AND " + AndroidContact.COLUMN_ETAG + " IS NULL"; String where = ContactsContract.RawContacts.DIRTY + "=0 AND " + AndroidContact.COLUMN_ETAG + " IS NULL";
ContentValues values = new ContentValues(1); ContentValues values = new ContentValues(1);

View File

@ -160,31 +160,6 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
return dirty.toArray(new LocalResource[dirty.size()]); return dirty.toArray(new LocalResource[dirty.size()]);
} }
@Override
@SuppressWarnings("Recycle")
public String getCTag() throws CalendarStorageException {
try {
@Cleanup Cursor cursor = provider.query(calendarSyncURI(), new String[] { COLUMN_CTAG }, null, null, null);
if (cursor != null && cursor.moveToNext())
return cursor.getString(0);
} catch (RemoteException e) {
throw new CalendarStorageException("Couldn't read local (last known) CTag", e);
}
return null;
}
@Override
public void setCTag(String cTag) throws CalendarStorageException, ContactsStorageException {
try {
ContentValues values = new ContentValues(1);
values.put(COLUMN_CTAG, cTag);
provider.update(calendarSyncURI(), values, null, null);
} catch (RemoteException e) {
throw new CalendarStorageException("Couldn't write local (last known) CTag", e);
}
}
@SuppressWarnings("Recycle") @SuppressWarnings("Recycle")
public void processDirtyExceptions() throws CalendarStorageException { public void processDirtyExceptions() throws CalendarStorageException {
// process deleted exceptions // process deleted exceptions
@ -286,9 +261,9 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
} }
/** Fix all of the etags of all of the non-dirty events to be non-null. /** Fix all of the etags of all of the non-dirty events to be non-null.
* Currently set to the ctag. */ * Currently set to all ones.. */
public void fixEtags() throws CalendarStorageException { public void fixEtags() throws CalendarStorageException {
String newEtag = getCTag(); String newEtag = "1111111111111111111111111111111111111111111111111111111111111111";
String where = Events.CALENDAR_ID + "=? AND " + Events.DIRTY + "=0 AND " + LocalEvent.COLUMN_ETAG + " IS NULL"; String where = Events.CALENDAR_ID + "=? AND " + Events.DIRTY + "=0 AND " + LocalEvent.COLUMN_ETAG + " IS NULL";
String whereArgs[] = {String.valueOf(id)}; String whereArgs[] = {String.valueOf(id)};

View File

@ -22,8 +22,5 @@ public interface LocalCollection {
LocalResource getByUid(String uid) throws CalendarStorageException, ContactsStorageException; LocalResource getByUid(String uid) throws CalendarStorageException, ContactsStorageException;
String getCTag() throws CalendarStorageException, ContactsStorageException;
void setCTag(String cTag) throws CalendarStorageException, ContactsStorageException;
long count() throws CalendarStorageException, ContactsStorageException; long count() throws CalendarStorageException, ContactsStorageException;
} }

View File

@ -126,31 +126,6 @@ public class LocalTaskList extends AndroidTaskList implements LocalCollection {
} }
} }
@Override
@SuppressWarnings("Recycle")
public String getCTag() throws CalendarStorageException {
try {
@Cleanup Cursor cursor = provider.client.query(taskListSyncUri(), new String[] { COLUMN_CTAG }, null, null, null);
if (cursor != null && cursor.moveToNext())
return cursor.getString(0);
} catch (RemoteException e) {
throw new CalendarStorageException("Couldn't read local (last known) CTag", e);
}
return null;
}
@Override
public void setCTag(String cTag) throws CalendarStorageException {
try {
ContentValues values = new ContentValues(1);
values.put(COLUMN_CTAG, cTag);
provider.client.update(taskListSyncUri(), values, null, null);
} catch (RemoteException e) {
throw new CalendarStorageException("Couldn't write local (last known) CTag", e);
}
}
// helpers // helpers
public static boolean tasksProviderAvailable(@NonNull Context context) { public static boolean tasksProviderAvailable(@NonNull Context context) {

View File

@ -181,6 +181,8 @@ abstract public class SyncManager {
syncPhase = R.string.sync_phase_post_processing; syncPhase = R.string.sync_phase_post_processing;
App.log.info("Sync phase: " + context.getString(syncPhase)); App.log.info("Sync phase: " + context.getString(syncPhase));
postProcess(); postProcess();
App.log.info("Finished sync with CTag=" + remoteCTag);
} catch (IOException e) { } catch (IOException e) {
App.log.log(Level.WARNING, "I/O exception during sync, trying again later", e); App.log.log(Level.WARNING, "I/O exception during sync, trying again later", e);
syncResult.stats.numIoExceptions++; syncResult.stats.numIoExceptions++;
@ -295,24 +297,20 @@ abstract public class SyncManager {
String strTotal = String.valueOf(remoteEntries.size()); String strTotal = String.valueOf(remoteEntries.size());
int i = 0; int i = 0;
try { for (JournalEntryManager.Entry entry : remoteEntries) {
for (JournalEntryManager.Entry entry : remoteEntries) { if (Thread.interrupted()) {
if (Thread.interrupted()) { throw new InterruptedException();
throw new InterruptedException();
}
i++;
App.log.info("Processing (" + String.valueOf(i) + "/" + strTotal + ") " + entry.toString());
SyncEntry cEntry = SyncEntry.fromJournalEntry(crypto, entry);
App.log.info("Processing resource for journal entry");
processSyncEntry(cEntry);
persistSyncEntry(entry.getUid(), cEntry);
remoteCTag = entry.getUid();
} }
} finally { i++;
saveSyncTag(); App.log.info("Processing (" + String.valueOf(i) + "/" + strTotal + ") " + entry.toString());
SyncEntry cEntry = SyncEntry.fromJournalEntry(crypto, entry);
App.log.info("Processing resource for journal entry");
processSyncEntry(cEntry);
persistSyncEntry(entry.getUid(), cEntry);
remoteCTag = entry.getUid();
} }
} }
@ -352,8 +350,6 @@ abstract public class SyncManager {
localDirty = null; localDirty = null;
localDeleted = null; localDeleted = null;
saveSyncTag();
} }
} }
@ -390,12 +386,12 @@ abstract public class SyncManager {
/** /**
*/ */
protected void prepareLocal() throws CalendarStorageException, ContactsStorageException, FileNotFoundException { protected void prepareLocal() throws CalendarStorageException, ContactsStorageException, FileNotFoundException {
remoteCTag = getJournalEntity().getLastUid(data);
localDeleted = processLocallyDeleted(); localDeleted = processLocallyDeleted();
localDirty = localCollection.getDirty(); localDirty = localCollection.getDirty();
// This is done after fetching the local dirty so all the ones we are using will be prepared // This is done after fetching the local dirty so all the ones we are using will be prepared
prepareDirty(); prepareDirty();
remoteCTag = localCollection.getCTag();
} }
@ -438,11 +434,4 @@ abstract public class SyncManager {
*/ */
protected void postProcess() throws CalendarStorageException, ContactsStorageException { protected void postProcess() throws CalendarStorageException, ContactsStorageException {
} }
private void saveSyncTag() throws CalendarStorageException, ContactsStorageException {
App.log.info("Saving CTag=" + remoteCTag);
localCollection.setCTag(remoteCTag);
}
} }