SyncSourceRevisions: added updateAll[Sub]Items

There may be backends (like CalDAV) where updating existing
information about items is more efficient than reading them
from scratch. This commit adds updateAll[Sub]Items in SyncSourceRevisions/TrackingSyncSource resp. the SubSyncSource API for that purpose.

If it is called when some information exits (without it, the attempt
would be useless) and when change tracking may resuse existing
information. So an explicit slow sync still reads all items and wipes
out all old information, just in case that it is somehow
broken. Should not matter in most cases, though, because wrong luids
and mismatching revisions will be detected and corrected. The only
situation where incorrect information would matter is correct
luid+revision with wrong meta information attached in MapSyncSource
(subids, UID, ...).
This commit is contained in:
Patrick Ohly 2011-06-25 12:19:13 +02:00
parent c1116941e5
commit 7e542c596d
5 changed files with 127 additions and 21 deletions

View file

@ -236,24 +236,9 @@ MapSyncSource::MapSyncSource(const SyncSourceParams &params,
}
}
void MapSyncSource::listAllItems(SyncSourceRevisions::RevisionMap_t &revisions)
void MapSyncSource::RevMap2SubRevMap(const SyncSourceRevisions::RevisionMap_t &revisions,
SubSyncSource::SubRevisionMap_t &subrevisions)
{
SubSyncSource::SubRevisionMap_t subrevisions;
m_sub->listAllSubItems(subrevisions);
BOOST_FOREACH(const SubSyncSource::SubRevisionMap_t::value_type &subentry,
subrevisions) {
const std::string &mainid = subentry.first;
const SubSyncSource::SubRevisionEntry &entry = subentry.second;
BOOST_FOREACH(const std::string &subid, entry.m_subids) {
std::string luid = createLUID(mainid, subid);
revisions[luid] = entry.m_revision;
}
}
}
void MapSyncSource::setAllItems(const SyncSourceRevisions::RevisionMap_t &revisions)
{
SubSyncSource::SubRevisionMap_t subrevisions;
BOOST_FOREACH(const SyncSourceRevisions::RevisionMap_t::value_type &entry,
revisions) {
const std::string &luid = entry.first;
@ -267,9 +252,44 @@ void MapSyncSource::setAllItems(const SyncSourceRevisions::RevisionMap_t &revisi
}
subentry.m_subids.insert(ids.second);
}
m_sub->setAllSubItems(subrevisions);
}
void MapSyncSource::SubRevMap2RevMap(const SubSyncSource::SubRevisionMap_t &subrevisions,
SyncSourceRevisions::RevisionMap_t &revisions)
{
BOOST_FOREACH(const SubSyncSource::SubRevisionMap_t::value_type &subentry,
subrevisions) {
const std::string &mainid = subentry.first;
const SubSyncSource::SubRevisionEntry &entry = subentry.second;
BOOST_FOREACH(const std::string &subid, entry.m_subids) {
std::string luid = createLUID(mainid, subid);
revisions[luid] = entry.m_revision;
}
}
}
void MapSyncSource::listAllItems(SyncSourceRevisions::RevisionMap_t &revisions)
{
SubSyncSource::SubRevisionMap_t subrevisions;
m_sub->listAllSubItems(subrevisions);
SubRevMap2RevMap(subrevisions, revisions);
}
void MapSyncSource::updateAllItems(SyncSourceRevisions::RevisionMap_t &revisions)
{
SubSyncSource::SubRevisionMap_t subrevisions;
RevMap2SubRevMap(revisions, subrevisions);
m_sub->updateAllSubItems(subrevisions);
revisions.clear();
SubRevMap2RevMap(subrevisions, revisions);
}
void MapSyncSource::setAllItems(const SyncSourceRevisions::RevisionMap_t &revisions)
{
SubSyncSource::SubRevisionMap_t subrevisions;
RevMap2SubRevMap(revisions, subrevisions);
m_sub->setAllSubItems(subrevisions);
}
SyncSourceRaw::InsertItemResult MapSyncSource::insertItem(const std::string &luid, const std::string &item, bool raw)
{

View file

@ -128,14 +128,27 @@ class SubSyncSource : virtual public SyncSourceBase
virtual std::string subDatabaseRevision() { return ""; }
/**
* Either listAllSubItems() or setAllSubItems() will be called after begin().
* Either listAllSubItems(), setAllSubItems(), or updateAllSubitems()
* will be called after begin().
*
* In the first case, the sub source is expected to provide a full list
* of its items. In the second case, the caller was able to determine
* that its cached copy of that list is still correct and provides it
* the the source.
* the the source. In the third case, some revision information is know,
* but it may be obsolete (revision string and/or subids changed or removed)
* or incomplete (new items missing). The callee then must update the
* information, possibly by falling back to listAllSubItems().
*/
virtual void listAllSubItems(SubRevisionMap_t &revisions) = 0;
/**
* Called instead of listAllSubItems().
*/
virtual void updateAllSubItems(SubRevisionMap_t &revisions) {
revisions.clear();
listAllSubItems(revisions);
}
/**
* Called instead of listAllSubItems().
*/
@ -229,6 +242,7 @@ class MapSyncSource : public TrackingSyncSource,
virtual std::string endSync(bool success) { m_sub->endSubSync(success); return TrackingSyncSource::endSync(success); }
virtual bool isEmpty() { return dynamic_cast<SyncSource &>(*m_sub).getOperations().m_isEmpty(); }
virtual void listAllItems(SyncSourceRevisions::RevisionMap_t &revisions);
virtual void updateAllItems(SyncSourceRevisions::RevisionMap_t &revisions);
virtual void setAllItems(const SyncSourceRevisions::RevisionMap_t &revisions);
virtual std::string databaseRevision() { return m_sub->subDatabaseRevision(); }
virtual InsertItemResult insertItem(const std::string &luid, const std::string &item, bool raw);
@ -253,6 +267,11 @@ class MapSyncSource : public TrackingSyncSource,
* first.
*/
void checkFlush(const std::string &luid);
void SubRevMap2RevMap(const SubSyncSource::SubRevisionMap_t &subrevisions,
SyncSourceRevisions::RevisionMap_t &revisions);
void RevMap2SubRevMap(const SyncSourceRevisions::RevisionMap_t &revisions,
SubSyncSource::SubRevisionMap_t &subrevisions);
};
SE_END_CXX

View file

@ -936,6 +936,33 @@ void SyncSourceRevisions::detectChanges(ConfigNode &trackingNode, ChangeMode mod
return;
}
if (!m_revisionsSet &&
mode == CHANGES_FULL) {
ConfigProps props;
trackingNode.readProperties(props);
if (!props.empty()) {
// We were not asked to throw away all old information and
// there is some that may be worth salvaging, so let's give
// our derived class a chance to update it instead of having
// to reread everything.
//
// The exact number of items at which the update method is
// more efficient depends on the derived class; here we assume
// that even a single item makes it worthwhile. The derived
// class can always ignore the information if it has different
// tradeoffs.
//
// TODO (?): an API which only provides the information
// on demand...
m_revisions.clear();
m_revisions.insert(props.begin(), props.end());
updateAllItems(m_revisions);
// continue with m_revisions initialized below
m_revisionsSet = true;
}
}
// traditional, slow fallback follows...
initRevisions();
// Delay setProperty calls until after checking all uids.

View file

@ -1695,7 +1695,7 @@ class SyncSourceRevisions : virtual public SyncSourceChanges, virtual public Syn
/**
* Called by SyncSourceRevisions::detectChanges() to tell
* the derived class about the cached information if (and only
* if) listAllItems() was not called. The derived class
* if) listAllItems() and updateAllItems() were not called. The derived class
* might not need this information, so the default implementation
* simply ignores.
*
@ -1704,6 +1704,20 @@ class SyncSourceRevisions : virtual public SyncSourceChanges, virtual public Syn
*/
virtual void setAllItems(const RevisionMap_t &revisions) {}
/**
* updates the revision map to reflect the current state
*
* May be called instead of listAllItems() if the caller has
* a valid list to start from. If the implementor
* cannot update the list, it must start from scratch by
* reseting the list and calling listAllItems(). The default
* implementation of this method does that.
*/
virtual void updateAllItems(SyncSourceRevisions::RevisionMap_t &revisions) {
revisions.clear();
listAllItems(revisions);
}
/**
* Tells detectChanges() how to do its job.
*/

View file

@ -150,6 +150,32 @@ class TrackingSyncSource : public TestingSyncSource,
*/
virtual void listAllItems(SyncSourceRevisions::RevisionMap_t &revisions) = 0;
/**
* Called at the start of the sync session to tell
* the derived class about the cached information if (and only
* if) listAllItems() and updateAllItems() were not called. The derived class
* might not need this information, so the default implementation
* simply ignores.
*
* A more complex API could have been defined to only prepare the
* information when needed, but that seemed unnecessarily complex.
*/
virtual void setAllItems(const RevisionMap_t &revisions) {}
/**
* updates the revision map to reflect the current state
*
* May be called instead of listAllItems() if the caller has
* a valid list to start from. If the implementor
* cannot update the list, it must start from scratch by
* reseting the list and calling listAllItems(). The default
* implementation of this method does that.
*/
virtual void updateAllItems(SyncSourceRevisions::RevisionMap_t &revisions) {
revisions.clear();
listAllItems(revisions);
}
/**
* Create or modify an item.
*