Commit e8a9d532 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Port local mail resources to new retrieveItems() API

The Maildir, MBox and MixedMaildir resources are most affected by
the poor performance of ResourceBase::retrieveItem(). This change
ports those resources to the new retrieveItems() API which provides
batch retrieval of missing payload parts.
parent 4331088e
......@@ -57,7 +57,7 @@ set(QT_REQUIRED_VERSION "5.5.0")
set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}")
set(KDEPIMRUNTIME_LIB_SOVERSION "5")
set(AKONADI_VERSION "5.3.40")
set(AKONADI_VERSION "5.3.42")
set(KCONTACTS_LIB_VERSION "5.3.40")
set(KCALENDARCORE_LIB_VERSION "5.3.40")
......
......@@ -198,26 +198,32 @@ MaildirResource::~ MaildirResource()
delete mSettings;
}
bool MaildirResource::retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts)
bool MaildirResource::retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts)
{
Q_UNUSED(parts);
const Maildir md = maildirForCollection(item.parentCollection());
const Maildir md = maildirForCollection(items.at(0).parentCollection());
if (!md.isValid()) {
cancelTask(i18n("Unable to fetch item: The maildir folder \"%1\" is not valid.",
md.path()));
return false;
}
const QByteArray data = md.readEntry(item.remoteId());
KMime::Message *mail = new KMime::Message();
mail->setContent(KMime::CRLFtoLF(data));
mail->parse();
Akonadi::Item::List rv;
rv.reserve(items.count());
for (const Akonadi::Item &item : items) {
const QByteArray data = md.readEntry(item.remoteId());
KMime::Message *mail = new KMime::Message();
mail->setContent(KMime::CRLFtoLF(data));
mail->parse();
Item i(item);
i.setPayload(KMime::Message::Ptr(mail));
Akonadi::MessageFlags::copyMessageFlags(*mail, i);
itemRetrieved(i);
Item i(item);
i.setPayload(KMime::Message::Ptr(mail));
Akonadi::MessageFlags::copyMessageFlags(*mail, i);
rv.push_back(i);
}
itemsRetrieved(rv);
return true;
}
......
......@@ -50,7 +50,7 @@ public Q_SLOTS:
protected Q_SLOTS:
void retrieveCollections() Q_DECL_OVERRIDE;
void retrieveItems(const Akonadi::Collection &col) Q_DECL_OVERRIDE;
bool retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts) Q_DECL_OVERRIDE;
bool retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts) Q_DECL_OVERRIDE;
protected:
virtual QString itemMimeType() const;
......
......@@ -149,7 +149,7 @@ void MboxResource::retrieveItems(const Akonadi::Collection &col)
itemsRetrieved(items);
}
bool MboxResource::retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts)
bool MboxResource::retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts)
{
Q_UNUSED(parts);
......@@ -162,18 +162,23 @@ bool MboxResource::retrieveItem(const Akonadi::Item &item, const QSet<QByteArray
return false;
}
const QString rid = item.remoteId();
const quint64 offset = itemOffset(rid);
KMime::Message *mail = mMBox->readMessage(KMBox::MBoxEntry(offset));
if (!mail) {
Q_EMIT error(i18n("Failed to read message with uid '%1'.", rid));
return false;
Akonadi::Item::List rv;
rv.reserve(items.count());
for (const auto &item : items) {
const QString rid = item.remoteId();
const quint64 offset = itemOffset(rid);
KMime::Message *mail = mMBox->readMessage(KMBox::MBoxEntry(offset));
if (!mail) {
Q_EMIT error(i18n("Failed to read message with uid '%1'.", rid));
return false;
}
Item i(item);
i.setPayload(KMime::Message::Ptr(mail));
Akonadi::MessageFlags::copyMessageFlags(*mail, i);
rv.push_back(i);
}
Item i(item);
i.setPayload(KMime::Message::Ptr(mail));
Akonadi::MessageFlags::copyMessageFlags(*mail, i);
itemRetrieved(i);
itemsRetrieved(rv);
return true;
}
......
......@@ -37,7 +37,7 @@ public:
~MboxResource();
protected Q_SLOTS:
bool retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts) Q_DECL_OVERRIDE;
bool retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts) Q_DECL_OVERRIDE;
void retrieveItems(const Akonadi::Collection &col) Q_DECL_OVERRIDE;
protected:
......
......@@ -266,11 +266,11 @@ void MixedMaildirResource::retrieveItems(const Collection &col)
status(Running, i18nc("@info:status", "Synchronizing email folder %1", col.name()));
}
bool MixedMaildirResource::retrieveItem(const Item &item, const QSet<QByteArray> &parts)
bool MixedMaildirResource::retrieveItems(const Item::List &items, const QSet<QByteArray> &parts)
{
Q_UNUSED(parts);
FileStore::ItemFetchJob *job = mStore->fetchItem(item);
FileStore::ItemFetchJob *job = mStore->fetchItems(items);
if (parts.contains(Item::FullPayload)) {
job->fetchScope().fetchFullPayload(true);
} else {
......@@ -565,7 +565,7 @@ void MixedMaildirResource::retrieveItemResult(KJob *job)
Q_ASSERT(fetchJob != 0);
Q_ASSERT(!fetchJob->items().isEmpty());
itemRetrieved(fetchJob->items().at(0));
itemsRetrieved(fetchJob->items());
}
void MixedMaildirResource::itemAddedResult(KJob *job)
......
......@@ -43,7 +43,7 @@ public Q_SLOTS:
protected Q_SLOTS:
void retrieveCollections() Q_DECL_OVERRIDE;
void retrieveItems(const Akonadi::Collection &col) Q_DECL_OVERRIDE;
bool retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts) Q_DECL_OVERRIDE;
bool retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts) Q_DECL_OVERRIDE;
protected:
void aboutToQuit() Q_DECL_OVERRIDE;
......
......@@ -1660,7 +1660,10 @@ bool MixedMaildirStore::Private::visit(FileStore::ItemFetchJob *job)
scope.payloadParts().contains(MessagePart::Envelope);
const bool fetchSingleItem = job->collection().remoteId().isEmpty();
const Collection collection = fetchSingleItem ? job->item().parentCollection() : job->collection();
const bool fetchItemsBatch = !job->items().isEmpty() && !job->item().isValid();
const Collection collection = fetchSingleItem ? job->item().parentCollection() :
fetchItemsBatch ? job->items().at(0).parentCollection() :
job->collection();
QString path;
QString errorText;
......@@ -1675,7 +1678,7 @@ bool MixedMaildirStore::Private::visit(FileStore::ItemFetchJob *job)
if (folderType == MBoxFolder) {
MBoxHash::iterator findIt = mMBoxes.find(path);
if (findIt == mMBoxes.end() || !fetchSingleItem) {
if (findIt == mMBoxes.end() || (!fetchSingleItem || !fetchItemsBatch)) {
MBoxPtr mbox = findIt != mMBoxes.end() ? findIt.value() : MBoxPtr(new MBoxContext);
if (!mbox->load(path)) {
errorText = i18nc("@info:status", "Failed to load MBox folder %1", path);
......@@ -1695,6 +1698,8 @@ bool MixedMaildirStore::Private::visit(FileStore::ItemFetchJob *job)
Item::List items;
if (fetchSingleItem) {
items << job->item();
} else if (fetchItemsBatch) {
items = job->items();
} else {
listCollection(job, findIt.value(), collection, items);
}
......@@ -1734,6 +1739,8 @@ bool MixedMaildirStore::Private::visit(FileStore::ItemFetchJob *job)
Item::List items;
if (fetchSingleItem) {
items << job->item();
} else if (fetchItemsBatch) {
items = job->items();
} else {
listCollection(job, mdPtr, collection, items);
}
......
......@@ -563,22 +563,25 @@ FileStore::ItemFetchJob *FileStore::AbstractLocalStore::fetchItems(const Collect
return job;
}
FileStore::ItemFetchJob *FileStore::AbstractLocalStore::fetchItem(const Item &item) const
{
FileStore::ItemFetchJob *job = new FileStore::ItemFetchJob(item, d->mSession);
if (d->mTopLevelCollection.remoteId().isEmpty()) {
const QString message = i18nc("@info:status", "Configured storage location is empty");
qCritical() << message;
qCritical() << "Item(remoteId=" << item.remoteId() << ", mimeType=" << item.mimeType()
<< ", parentCollection=" << item.parentCollection().remoteId() << ")";
d->mSession->setError(job, FileStore::Job::InvalidStoreState, message);
} else if (item.remoteId().isEmpty()) {
const QString message = i18nc("@info:status", "Given item identifier is empty");
qCritical() << message;
qCritical() << "Item(remoteId=" << item.remoteId() << ", mimeType=" << item.mimeType()
<< ", parentCollection=" << item.parentCollection().remoteId() << ")";
d->mSession->setError(job, FileStore::Job::InvalidJobContext, message);
FileStore::ItemFetchJob *FileStore::AbstractLocalStore::fetchItems(const Item::List &items) const
{
FileStore::ItemFetchJob *job = new FileStore::ItemFetchJob(items, d->mSession);
if (items.size() == 1) {
const Akonadi::Item &item = items[0];
if (d->mTopLevelCollection.remoteId().isEmpty()) {
const QString message = i18nc("@info:status", "Configured storage location is empty");
qCritical() << message;
qCritical() << "Item(remoteId=" << item.remoteId() << ", mimeType=" << item.mimeType()
<< ", parentCollection=" << item.parentCollection().remoteId() << ")";
d->mSession->setError(job, FileStore::Job::InvalidStoreState, message);
} else if (item.remoteId().isEmpty()) {
const QString message = i18nc("@info:status", "Given item identifier is empty");
qCritical() << message;
qCritical() << "Item(remoteId=" << item.remoteId() << ", mimeType=" << item.mimeType()
<< ", parentCollection=" << item.parentCollection().remoteId() << ")";
d->mSession->setError(job, FileStore::Job::InvalidJobContext, message);
}
}
int errorCode = 0;
......@@ -591,6 +594,11 @@ FileStore::ItemFetchJob *FileStore::AbstractLocalStore::fetchItem(const Item &it
return job;
}
FileStore::ItemFetchJob *FileStore::AbstractLocalStore::fetchItem(const Item &item) const
{
return fetchItems({ item });
}
FileStore::ItemCreateJob *FileStore::AbstractLocalStore::createItem(const Item &item, const Collection &collection)
{
FileStore::ItemCreateJob *job = new FileStore::ItemCreateJob(item, collection, d->mSession);
......
......@@ -63,6 +63,8 @@ public:
ItemFetchJob *fetchItems(const Collection &collection) const Q_DECL_OVERRIDE;
ItemFetchJob *fetchItems(const Item::List &items) const Q_DECL_OVERRIDE;
ItemFetchJob *fetchItem(const Item &item) const Q_DECL_OVERRIDE;
ItemCreateJob *createItem(const Item &item, const Collection &collection) Q_DECL_OVERRIDE;
......
......@@ -52,6 +52,15 @@ FileStore::ItemFetchJob::ItemFetchJob(const Item &item, FileStore::AbstractJobSe
session->addJob(this);
}
FileStore::ItemFetchJob::ItemFetchJob(const Item::List &items, FileStore::AbstractJobSession *session)
: FileStore::Job(session), d(new Private())
{
d->mItems = items;
session->addJob(this);
}
FileStore::ItemFetchJob::~ItemFetchJob()
{
delete d;
......
......@@ -46,6 +46,8 @@ public:
explicit ItemFetchJob(const Item &item, AbstractJobSession *session = Q_NULLPTR);
explicit ItemFetchJob(const Item::List &items, AbstractJobSession *session = Q_NULLPTR);
virtual ~ItemFetchJob();
Collection collection() const;
......
......@@ -22,6 +22,8 @@
#include "akonadi-filestore_export.h"
#include <QVector>
// TODO not nice, collection fetch type should probably be in its own header
#include "collectionfetchjob.h"
......@@ -65,6 +67,8 @@ public:
virtual ItemFetchJob *fetchItems(const Collection &collection) const = 0;
virtual ItemFetchJob *fetchItems(const QVector<Item> &items) const = 0;
virtual ItemFetchJob *fetchItem(const Item &item) const = 0;
virtual ItemCreateJob *createItem(const Item &item, const Collection &collection) = 0;
......
Supports Markdown
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