Commit 43d5659a authored by Daniel Vrátil's avatar Daniel Vrátil 🤖

ItemSync: use RID merge by default, allow optional switch to GID merge

GID is not guaranteed to be actually unique and is not in our control, which means
that we can easilly run to the multiple merge candidates problem, which breaks
ItemSync. This patch switching ItemSync to use RID, which (although not enforced) is
basically guaranteed to be unique per Collection, thus the multiple merge candidates
error should not happen anymore.

Some resources however mandate GID-based merging (when RID is not stable), so we have
a new API that allows resources to switch the merging mode of ItemSync optionally.

(cherry picked from commit 44296191)
parent 1b9e833b
......@@ -451,14 +451,18 @@ private Q_SLOTS:
AKVERIFYEXEC(syncer);
Item::List resultItems = fetchItems(col);
QCOMPARE(resultItems.count(), 2);
QCOMPARE(resultItems.count(), 3);
ItemFetchJob *fetchJob = new ItemFetchJob(modifiedItem);
Item item;
item.setGid(QStringLiteral("gid2"));
ItemFetchJob *fetchJob = new ItemFetchJob(item);
fetchJob->fetchScope().fetchFullPayload();
AKVERIFYEXEC(fetchJob);
QCOMPARE(fetchJob->items().size(), 1);
QCOMPARE(fetchJob->items().size(), 2);
QCOMPARE(fetchJob->items().first().payload<QByteArray>(), QByteArray("payload2"));
QCOMPARE(fetchJob->items().first().remoteId(), QString::fromLatin1("rid3"));
QCOMPARE(fetchJob->items().at(1).payload<QByteArray>(), QByteArray("payload1"));
QCOMPARE(fetchJob->items().at(1).remoteId(), QStringLiteral("rid2"));
}
/*
......
......@@ -76,6 +76,7 @@ public:
, mItemSyncer(0)
, mItemSyncFetchScope(0)
, mItemTransactionMode(ItemSync::SingleTransaction)
, mItemMergeMode(ItemSync::RIDMerge)
, mCollectionSyncer(0)
, mTagSyncer(0)
, mRelationSyncer(0)
......@@ -189,6 +190,7 @@ public:
mItemSyncer = new ItemSync(q->currentCollection());
mItemSyncer->setTransactionMode(mItemTransactionMode);
mItemSyncer->setBatchSize(mItemSyncBatchSize);
mItemSyncer->setMergeMode(mItemMergeMode);
if (mItemSyncFetchScope) {
mItemSyncer->setFetchScope(*mItemSyncFetchScope);
}
......@@ -456,6 +458,7 @@ public:
ItemSync *mItemSyncer;
ItemFetchScope *mItemSyncFetchScope;
ItemSync::TransactionMode mItemTransactionMode;
ItemSync::MergeMode mItemMergeMode;
CollectionSync *mCollectionSyncer;
TagSync *mTagSyncer;
RelationSync *mRelationSyncer;
......@@ -1379,6 +1382,12 @@ void ResourceBase::setItemSynchronizationFetchScope(const ItemFetchScope &fetchS
*(d->mItemSyncFetchScope) = fetchScope;
}
void ResourceBase::setItemMergingMode(ItemSync::MergeMode mode)
{
Q_D(ResourceBase);
d->mItemMergeMode = mode;
}
void ResourceBase::setAutomaticProgressReporting(bool enabled)
{
Q_D(ResourceBase);
......
......@@ -546,6 +546,20 @@ protected:
*/
void setItemTransactionMode(ItemSync::TransactionMode mode);
/**
* Set merge mode for item sync'ing.
*
* Default merge mode is RIDMerge.
*
* @note This method must be called before first call to itemRetrieved(),
* itemsRetrieved() or itemsRetrievedIncremental().
*
* @param mode Item merging mode (see ItemCreateJob for details on item merging)
* @see Akonadi::ItemSync::MergeMode
* @ince 4.14.11
*/
void setItemMergingMode(ItemSync::MergeMode mode);
/**
* Set the fetch scope applied for item synchronization.
* By default, the one set on the changeRecorder() is used. However, it can make sense
......
......@@ -61,6 +61,7 @@ public:
, mProcessingBatch(false)
, mDisableAutomaticDeliveryDone(false)
, mBatchSize(10)
, mMergeMode(Akonadi::ItemSync::RIDMerge)
{
// we want to fetch all data by default
mFetchScope.fetchFullPayload();
......@@ -116,6 +117,7 @@ public:
bool mDisableAutomaticDeliveryDone;
int mBatchSize;
Akonadi::ItemSync::MergeMode mMergeMode;
};
void ItemSyncPrivate::createOrMerge(const Item &item)
......@@ -127,11 +129,13 @@ void ItemSyncPrivate::createOrMerge(const Item &item)
}
mPendingJobs++;
ItemCreateJob *create = new ItemCreateJob(item, mSyncCollection, subjobParent());
if (!item.gid().isEmpty()) {
create->setMerge(ItemCreateJob::GID | ItemCreateJob::Silent);
} else {
create->setMerge(ItemCreateJob::RID | ItemCreateJob::Silent);
ItemCreateJob::MergeOptions merge = ItemCreateJob::Silent;
if (mMergeMode == ItemSync::RIDMerge) {
merge |= ItemCreateJob::RID;
} else if (mMergeMode == ItemSync::GIDMerge && !item.gid().isEmpty()) {
merge |= ItemCreateJob::GID;
}
create->setMerge(merge);
q->connect(create, SIGNAL(result(KJob*)), q, SLOT(slotLocalChangeDone(KJob*)));
}
......@@ -535,4 +539,16 @@ void ItemSync::setBatchSize(int size)
d->mBatchSize = size;
}
ItemSync::MergeMode ItemSync::mergeMode() const
{
Q_D(const ItemSync);
return d->mMergeMode;
}
void ItemSync::setMergeMode(MergeMode mergeMode)
{
Q_D(ItemSync);
d->mMergeMode = mergeMode;
}
#include "moc_itemsync.cpp"
......@@ -56,6 +56,12 @@ class AKONADICORE_EXPORT ItemSync : public Job
Q_OBJECT
public:
enum MergeMode
{
RIDMerge,
GIDMerge
};
/**
* Creates a new item synchronizer.
*
......@@ -209,6 +215,27 @@ public:
*/
void setDisableAutomaticDeliveryDone(bool disable);
/**
* Returns current merge mode
*
* @see setMergeMode()
* @since 5.1
*/
MergeMode mergeMode() const;
/**
* Set what merge method should be used for next ItemSync run
*
* By default ItemSync uses RIDMerge method.
*
* See ItemCreateJob for details on Item merging.
*
* @note You must call this method before starting the sync, changes afterwards lead to undefined results.
* @see mergeMode
* @since 4.14.11
*/
void setMergeMode(MergeMode mergeMode);
Q_SIGNALS:
/**
* Signals the resource that new items can be delivered.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment