Modification during sync may never get uploaded



  • In SyncManager.kt uploadDirty() you call prepareUpload() and then remote.put() on a dirty event. If it succeeds, you then call clearDirty() so that you won’t try to upload it again. The problem with this is that another process in the Android device can modify the event after you have called prepareUpload(). This will set the dirty flag (not documented, but I looked in CalendarProvider source code and it does), but you clear it in clearDirty(), so you will never synchronise the new modification until someone changes that event again.

    It is fairly improbable that the user modifies a calendar event just as you are synchronising, but if some app makes a lot of modifications to calendar events, the first modification will start a sync, so subsequent modifications may well happen while you are syncing.

    I think that the correct logic is to call clearDirty() before prepareUpload(). Of course if the upload fails you then have to set the dirty bit again, so you have to enclose all the code from clearDirty() to remote.put() in a try block which catches all exceptions. This can still fail if Android crashes or if your process gets summarily terminated by the OOM killer, but these are very unlikely events and it is theoretically impossible to design a protocol that is proof against these failures anyway.

    I would offer a patch if it was Java code, but I’m not familiar enough with Kotlin to write code in it.


  • developer

    @rparkins said in Modification during sync may never get uploaded:

    I think that the correct logic is to call clearDirty() before prepareUpload(). Of course if the upload fails you then have to set the dirty bit again, so you have to enclose all the code from clearDirty() to remote.put() in a try block which catches all exceptions. This can still fail if Android crashes or if your process gets summarily terminated by the OOM killer, but these are very unlikely events and it is theoretically impossible to design a protocol that is proof against these failures anyway.

    Android and DAVx⁵ will crash (for instance, another thread) and DAVx⁵ is often interrupted and terminated by the system, so I wouldn’t consider this an alternative. We would just replace one problem by other problems.

    We would need (row) locking on calendar provider level, but this is not provided by Android 😕



  • If DAVx⁵ is interrupted, you catch that. Terminating a program without warning is a very rare event.

    Row locking would indeed be a good solution if Android provided it.

    An alternative and possibly better solution is to store a hash of the event data (not including the dirty flag, of course). If after you have done the remote.put() and called clearDirty() the hash is not the same, someone else has changed the event. Of course there is a cost to compute the hash, but that is probably small compared with the delay while you communicate with the server. This solution does avoid the problem of a change which never gets synced.


  • developer

    @rparkins True, this solution adds a lot of complexity (and thus open a Pandora’s box full with new bugs) for a very rare problem. But i will consider it. Thanks for your thoughts.


Log in to reply