Search code examples
webdavcarddav

How to determine if a DAV folder had parallel updates while I was modifying it


I'm syncing local client with a DAV folder (CardDAV in this particular case).

For this folder, I have ETag (CTag in SabreDAV dialect, to distinguish folder etags from item etags). If CTag has changed, I need to re-sync again. But if this change was caused by myself (e.g. I just uploaded a contact into this CardDAV folder), isn't there any way to avoid resync?

Ideally, I wanted that the DAV server would return this info on each request which changes anything on the server:

  • CTag1, CTag of the current folder as it was before my action was applied
  • CTag2, CTag of the current folder after my action was applied
  • ETag assigned to the item in question (although it's not relevant to this particular question).

This would let me understand if CTag change was only caused by my own actions (and no resync needed) or something else occurred in between (and thus resync is needed).

Currently, I can only query the folder for its CTag at any time but I have no clue what to do if CTag changed (in pseudo-code):

cTag0 = ReadStoredValue() ' The value left from the previous sync.
cTag1 = GetCTag()
If cTag0 <> cTag1 Then
  Resync()
End If
UploadItem() ' Can get race condition if another client changes anything right now
cTag2 = GetCTag()

cTag2 will obviously be not the same as cTag1 but this provide zero information on whether something else occurred in the middle (another client changed something in the same folder). So, cTag0 <> cTag1 compare won't save me from race conditions, I could think that I'm in sync while some other update sneaked unnoticed.

Would be great to have:

cTag0 = ReadStoredValue() ' The value left from the previous sync.
(cTag1, cTag2) = UploadItem()
If cTag0 == cTag1
  ' No resync needed, just remember new CTag for the next sync cycle.
  cTag0 = cTag2
Else
  Resync()
  cTag0 = cTag2
End If

I'm aware of DAV-Sync protocol extension but this would be a different story. In this task, I'm referring to the standard DAV, no extensions allowed.

EDIT: One thought which just crossed my mind. I noticed that CTag is sequential. It's a number which gets incremented by 1 on each operation with the folder. So if it's increased by more than 1 between obtaining CTag, making my action and then obtaining CTag again, this will indicate something else has just occurred. But this does not seem to be reliable, I'm afraid it's too implementation-specific to rely on this behavior. Looking for a more robust solution.


Solution

  • How to determine if a DAV folder had parallel updates while I was modifying it

    This is very similar to How to avoid time conflict or overlap for CalDAV?

    Technically in pure DAV you are not guaranteed to be able to do this. Though in the real world, most servers will return you the ETag in the response to the PUT which was used to create/update the resource. This allows you to reconcile concurrent changes to the same resource.

    There is also the Calendar Server Bulk Change Requests for *DAV Protocols which is supported by some servers and which provides a more specific way to do this. Since it isn't an RFC, I wouldn't suggest to rely on that though.

    So what you would probably do is a PUT. If that returns you the ETag, you are good and can reconcile by syncing the collection (by whatever mechanism, PROPFIND:1, CTag or sync-report). If not, you either have the option to reconciling by other means (e.g. comparing/hashing the content), or to just treat the change as a concurrent edit, which I think most implementations do.

    If you are very lucky, the server may also return the CTag/sync-token in the PUT. But AFAIK there is no standard for that, servers are not required to do it.

    For this folder, I have ETag (CTag in SabreDAV dialect)

    This is a misconception of yours. A CTag is absolutely not the same like an ETag, it is its own thing documented over here: CalDAV CTag.

    I'm aware of DAV-Sync protocol extension but this would be a different story. In this task, I'm referring to the standard DAV, no extensions allowed.

    CTag is not a DAV standard at all, it is a private Apple extension (there is no RFC for that).

    Standard HTTP/1.1 specs the ETag. It corresponds to the resource representation and doesn't apply to WebDAV collection contents, which are distinct to that. WebDAV collections often also have contents (that can be retrieved by GET etc), the ETag corresponds to that.

    The official standard which replaces the proprietary CTag extension is in fact DAV-Sync aka RFC 6578. And the sync-token property and header is what replaces CTag header.

    So if "no extensions allowed" is your use case, you need to resource comparison on the client side. Pure WebDAV doesn't provide this capability.

    I noticed that CTag is sequential

    CTags are not sequential, they are opaque tokens. Specific servers may use a sequence, but that is completely arbitrary. (the same is true for all DAV tokens, they are always opaque)