dav icon indicating copy to clipboard operation
dav copied to clipboard

Question: Incremental sync does not work

Open benbucksch opened this issue 4 years ago • 3 comments

This may be a missing feature, an outdated protocol implementation, a bug, or simply a misunderstanding on my side. I am going to assume the latter, and please forgive me posting this as issue here. However, I could not find anything in the docs, so this is at least a request to improve the docs.

I want to fetch the CalDAV calendar and address book from the server, store the values locally on disk to cache them, and save the syncToken. Then, later, either in an interval or at the latest during the next start of the app, run the sync again. During that second sync, I want to get only those (and exactly those) calendar entries and contacts that were changed (which includes added, removed, or modified).

I cannot figure out how to do that, from the calendar and address book sync API. The documentation does not describe that. There is a syncCalendar() function, which from the name should do what I need, but the params and the overall process are not described. In the code, there is a syncToken (and then there's also a ctag and etag). I am trying to use the syncToken, which should do what I need, but it doesn't.

After the sync, I get calendar.syncToken and store it, and before the next sync (which might be after a restart), I restore the syncToken. But I get a full sync every time. It seems that I am using the API wrong, but I don't know what is right. I will attach a dedicated test case and the output below.

benbucksch avatar May 24 '20 10:05 benbucksch

Dedicated test case:

  1. git clone https://github.com/benbucksch/test-dav-sync/
  2. yarn install
  3. yarn start

The code, for self-contained-ness:

import dav from 'dav';
import config from './config.js'; /* Pseudo JSON File with:
export default
{
  "serverURL": "https://...hostname.../remote.php/dav",
  "username": "...",
  "password": "..."
}
*/

var gJSONSavedFile = {}; // would normally be a file or database table

async function start() {
  let syncToken = gJSONSavedFile.syncToken; // get it from file
  console.info("sync token from file", syncToken);

  console.time("calendar-connect");
  let xhr = new dav.transport.Basic(
    new dav.Credentials({
      username: config.username,
      password: config.password,
    })
  );
  let account = await dav.createAccount({
    server: config.serverURL,
    //loadCollections: false,
    //loadObjects: false,
    xhr: xhr,
  });
  console.timeEnd("calendar-connect");
  let calendar = account.calendars[0]; // just the first, just for testing
  console.time("calendar sync");
  console.log("sync token after init", calendar.syncToken);
  if (syncToken) {
    console.log("but setting sync token to", syncToken);
    calendar.syncToken = syncToken; // TODO Already set. Doesn't work.
  }
  calendar = await dav.syncCalendar(calendar, {
    syncToken: syncToken, // TODO Doesn't work
    xhr: xhr,
  });
  console.timeEnd("calendar sync");
  gJSONSavedFile.syncToken = calendar.syncToken; // save it
  console.log("sync token after sync", gJSONSavedFile.syncToken);
  console.log("Got", calendar.objects.length, "calendar entries");
}


(async () => {
  try {
    console.log("Test first start");
    await start();
    console.log("\nTest second start");
    await start();
  } catch (ex) {
    console.error(ex);
  }
})();

The output, when running against NextCloud:

Test first start
sync token from file undefined
calendar-connect: 4.765s
sync token after init http://sabre.io/ns/sync/1157
calendar sync: 2.562s
sync token after sync http://sabre.io/ns/sync/1157
Got 1051 calendar entries

Test second start
sync token from file http://sabre.io/ns/sync/1157
calendar-connect: 3.351s
sync token after init http://sabre.io/ns/sync/1157
but setting sync token to http://sabre.io/ns/sync/1157
calendar sync: 2.510s
sync token after sync http://sabre.io/ns/sync/1157
Got 1051 calendar entries
Done in 13.29s.

Expected output would be:

  1. Second sync round returns 0 entries.
  2. calendar.syncToken is not set immediately after init and login

benbucksch avatar May 24 '20 10:05 benbucksch

The fact that the syncToken is already set after init suggests that I am misunderstanding the API here.

benbucksch avatar May 24 '20 10:05 benbucksch

Also posted on StackOverflow.

benbucksch avatar May 24 '20 11:05 benbucksch