-
Notifications
You must be signed in to change notification settings - Fork 23
Archived Synchronization Model
This is outdated information that has been left here in case future work might find it interesting or useful.
Contact synchronization between Ensemble and external contact providers is a key goal of the project.
It's also pretty tricky. There are lots of edge cases to consider, and lots of places where things can go wrong.
Up until now, I've had a vague notion of how all of this is supposed to work. It's high time I wrote some of that stuff down, so people can poke holes in it, and help me make it better.
So, where should I begin?
A Contact Provider is some outside entity that can allow Thunderbird to access information about some contacts.
Each contact provider gets a unique "tag". A contact is tagged with that unique tag if and only if that contact has a corresponding record for that contact provider.
Examples: The System Address Book, Google Contacts, Facebook, a Zimbra instance, or other CardDAV server.
We're going to be tossing the word "contact" around a lot, and I want to distinguish the contacts that Thunderbird stores and knows about locally, and the contacts that are stored within Contact Providers. When I say "contact record", or "record", I'm talking about data stored in the Contact Provider.
Example: There might be a record for "Mike Conley" in the System Address Book.
A mechanism for Thunderbird to communicate with a Contact Provider. These can be built-in, or registered via add-ons.
Example: CardDAV connector, LDAP connector, System Address Book connector, Facebook connector, etc.
This means doing our best effort to make the records for a contact provider resemble the corresponding local contacts that have a tag for that contact provider, and vice-versa.
Examples:
- A new contact is created locally, and tagged with "System Address Book". That contact is then written to the System Address Book.
- A contact tagged with "System Address Book" is modified. Some new fields are added. Those changes are written to the System Address Book's record for that contact.
- A new record is created in the System Address Book by another application. This record appears as a new contact within Ensemble.
- A contact has the "System Address Book" tag removed from it. The corresponding record is removed from the System Address Book.
I'm making the following assertions:
- I think it's safe to say that if somebody is interested in syncing, that means they have contacts spread out over one or more contact providers.
- Those different places might represent contacts, and collections of contacts differently from one another.
- Ensemble will do its best to make sure that when a user saves a contact within Ensemble, the fields in that contact will be written to the corresponding records for each contact provider.
- It is the responsibility of the connector to make sure the fields that Ensemble sends "make sense" for the contact provider. Dropping a field that a contact provider cannot contain is preferable over trying to shoe-horn it in with hacks.
- Ensemble is not going to transparently try to equalize all contact records across each contact provider. Ensemble will support having records exist in one contact provider, without needing corresponding records to exist in all of the other contact providers.
- When a record is updated in one contact provider X, we should only attempt to push that change to the other providers if the user has given their explicit permission that X can push changes outwards.
- If the user has not given their permission to do this, the change is brought into the local contact record from X, but the other contact providers are not updated until the next time the contact is saved.
- Some contact providers may be read-only. Ensemble will, of course, not attempt to write to these.
A sync operation can get kicked off in a few different ways:
- A new contact provider is registered
- Changes are reported from a contact provider
- Changes are made locally to one or more contacts
- A contact provider is unregistered
- A tag is saved to a contact provider
- A tag is removed from a contact provider.
For (1), the sync operation is simply to pull in the records that the contact provider offers. This is probably followed by an optional merge operation, which could then be followed by a local change, which might trigger yet another sync operation.
For (4), this does not actually write anything to the now unregistered contact provider. It just means that the unique tag for that contact provider is removed from any of the contacts that were tagged with it. That change of tags could then trigger a sync operation if those contacts are tagged to other contact providers.
Tags are pretty to all of this. They're so important that they might deserve their own wiki page.
We will keep a local cache of each contact provider's list of records to do diffing against. Changes to be written to a contact provider will be queued until a connection is re-established.
- Connection is established with contact provider
- All records and "tag equivalents" are requested from the contact provider
- Ensemble writes all of those records to the local cache
- New local tags are created to represent the contact provider's "tag equivalents"
- A tag is created for the new contact provider.
- The contact provider tag is written to all contacts.
- If the connection to the contact provider allows writing, the altered records are written to the contact provider.
- The user makes a change to a contact, and chooses to save it to the associated record's contact provider.
- The local cached record X is diffed against the recently updated contact Y. This creates a diff D which, when applied to X, will make it equate Y.
- D is applied to X, and sent to the contact provider.
- If the contact provider reports that our local cache was out of date, we update our local cache, update the contact with the new changes, and apply D on top. We will report this conflict to the user after the save completes, and let them undo the write / see the updated contact information.
- Report to the user that the write was successful, displaying the newly updated contact, if necessary.
- The user makes a change to a contact, and chooses to save it to all of the associated record's contact providers.
- The local cached records (X_1, X_2, ..., X_N) are each diffed against the recently updated contact Y. This creates a set of diffs (D_1, D_2, ..., D_N) which, when applied to the corresponding X's, will make them equate Y.
- The D's are applied to the X's, and sent to each contact provider. If any contact providers (C_0, C_1, ..., C_N) report that their locally cached records was out of date, record enough information to undo this write, and then report to the user that the save was successful, but that some new things were overwritten, with the option to undo.
- If all write operations are successful, report this to the user, displaying the newly updated contact, if necessary.