summaryrefslogtreecommitdiffstats
path: root/examples/maildirresource/maildirresource.cpp
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2018-01-25 16:29:00 +0100
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-02-06 08:38:08 +0100
commit9b84aff4b68c3cef3328c85ac12418048b169cee (patch)
tree7014f685e6a8c850f0be7965e1e656d3de72a15d /examples/maildirresource/maildirresource.cpp
parent0a0c197ed487c343675b62dff8456932c8d5ff7f (diff)
downloadsink-9b84aff4b68c3cef3328c85ac12418048b169cee.tar.gz
sink-9b84aff4b68c3cef3328c85ac12418048b169cee.zip
Store all BLOB properties inline.
BLOB properties had a couple of intended purposes: * Allow large payloads to be streamed directly to disk, and then be handled by reference. * Allow zero-copy handling. * Keep the database values compact so we can avoid traversing large BLOBS. However, they came at the cost of code-complexity, and we lost all the benefits of our storage layer, such as transactions. Measurements showed, that for email (the intended primary usecase), the overhead is hardly measurable, with most parts performing better, or at least not worse. We additionally also gain file-system independence, which may help on other platforms. The biggest drawback is probably that large payloads need to be written to disk twice, because of the synchronizer queue (once for the queue, once for the actual data).
Diffstat (limited to 'examples/maildirresource/maildirresource.cpp')
-rw-r--r--examples/maildirresource/maildirresource.cpp90
1 files changed, 60 insertions, 30 deletions
diff --git a/examples/maildirresource/maildirresource.cpp b/examples/maildirresource/maildirresource.cpp
index 735665a..4c94d1d 100644
--- a/examples/maildirresource/maildirresource.cpp
+++ b/examples/maildirresource/maildirresource.cpp
@@ -54,8 +54,7 @@ static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath)
54 QDir dir(path); 54 QDir dir(path);
55 const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files); 55 const QFileInfoList list = dir.entryInfoList(QStringList() << (key+"*"), QDir::Files);
56 if (list.size() != 1) { 56 if (list.size() != 1) {
57 SinkWarning() << "Failed to find message " << mimeMessagePath; 57 SinkWarning() << "Failed to find message. Property value:" << mimeMessagePath << "Assembled path: " << path;
58 SinkWarning() << "Failed to find message " << path;
59 return QString(); 58 return QString();
60 } 59 }
61 return list.first().filePath(); 60 return list.first().filePath();
@@ -63,10 +62,25 @@ static QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath)
63 62
64class MaildirMailPropertyExtractor : public MailPropertyExtractor 63class MaildirMailPropertyExtractor : public MailPropertyExtractor
65{ 64{
65 void update(Sink::ApplicationDomain::Mail &mail)
66 {
67 QFile file{::getFilePathFromMimeMessagePath(mail.getMimeMessage())};
68 if (file.open(QIODevice::ReadOnly)) {
69 updatedIndexedProperties(mail, file.readAll());
70 } else {
71 SinkWarning() << "Failed to open file message " << mail.getMimeMessage();
72 }
73 }
74
66protected: 75protected:
67 virtual QString getFilePathFromMimeMessagePath(const QString &mimeMessagePath) const Q_DECL_OVERRIDE 76 void newEntity(Sink::ApplicationDomain::Mail &mail) Q_DECL_OVERRIDE
68 { 77 {
69 return ::getFilePathFromMimeMessagePath(mimeMessagePath); 78 update(mail);
79 }
80
81 void modifiedEntity(const Sink::ApplicationDomain::Mail &oldMail, Sink::ApplicationDomain::Mail &newMail) Q_DECL_OVERRIDE
82 {
83 update(newMail);
70 } 84 }
71}; 85};
72 86
@@ -92,6 +106,18 @@ public:
92 return folderPath; 106 return folderPath;
93 } 107 }
94 108
109 QString storeMessage(const QByteArray &data, const QByteArray &folder)
110 {
111 const auto path = getPath(folder);
112 KPIM::Maildir maildir(path, false);
113 if (!maildir.isValid(true)) {
114 SinkWarning() << "Maildir is not existing: " << path;
115 }
116 SinkWarning() << "Storing message: " << data;
117 auto identifier = maildir.addEntry(data);
118 return path + "/" + identifier;
119 }
120
95 QString moveMessage(const QString &oldPath, const QByteArray &folder) 121 QString moveMessage(const QString &oldPath, const QByteArray &folder)
96 { 122 {
97 if (oldPath.startsWith(Sink::temporaryFileLocation())) { 123 if (oldPath.startsWith(Sink::temporaryFileLocation())) {
@@ -125,15 +151,21 @@ public:
125 } 151 }
126 } 152 }
127 153
154 bool isPath(const QByteArray &data)
155 {
156 return data.startsWith('/');
157 }
158
128 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE 159 void newEntity(Sink::ApplicationDomain::ApplicationDomainType &newEntity) Q_DECL_OVERRIDE
129 { 160 {
130 auto mail = newEntity.cast<ApplicationDomain::Mail>(); 161 auto mail = newEntity.cast<ApplicationDomain::Mail>();
131 const auto mimeMessage = mail.getMimeMessagePath(); 162 const auto mimeMessage = mail.getMimeMessage();
132 if (!mimeMessage.isNull()) { 163 if (!mimeMessage.isNull()) {
133 const auto path = moveMessage(mimeMessage, mail.getFolder()); 164 if (isPath(mimeMessage)) {
134 auto blob = ApplicationDomain::BLOB{path}; 165 mail.setMimeMessage(moveMessage(mimeMessage, mail.getFolder()).toUtf8());
135 blob.isExternal = false; 166 } else {
136 mail.setProperty(ApplicationDomain::Mail::MimeMessage::name, QVariant::fromValue(blob)); 167 mail.setMimeMessage(storeMessage(mimeMessage, mail.getFolder()).toUtf8());
168 }
137 } 169 }
138 } 170 }
139 171
@@ -141,28 +173,28 @@ public:
141 { 173 {
142 auto newMail = newEntity.cast<ApplicationDomain::Mail>(); 174 auto newMail = newEntity.cast<ApplicationDomain::Mail>();
143 const ApplicationDomain::Mail oldMail{oldEntity}; 175 const ApplicationDomain::Mail oldMail{oldEntity};
144 const auto mimeMessage = newMail.getMimeMessagePath();
145 const auto newFolder = newMail.getFolder(); 176 const auto newFolder = newMail.getFolder();
146 const bool mimeMessageChanged = !mimeMessage.isNull() && mimeMessage != oldMail.getMimeMessagePath();
147 const bool folderChanged = !newFolder.isNull() && newFolder != oldMail.getFolder(); 177 const bool folderChanged = !newFolder.isNull() && newFolder != oldMail.getFolder();
148 if (mimeMessageChanged || folderChanged) { 178 if (!newMail.getMimeMessage().isNull() || folderChanged) {
149 SinkTrace() << "Moving mime message: " << mimeMessageChanged << folderChanged; 179 const auto data = newMail.getMimeMessage();
150 auto newPath = moveMessage(mimeMessage, newMail.getFolder()); 180 if (isPath(data)) {
151 if (newPath != oldMail.getMimeMessagePath()) { 181 auto newPath = moveMessage(data, newMail.getFolder());
152 const auto oldPath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath()); 182 if (newPath != oldMail.getMimeMessage()) {
153 auto blob = ApplicationDomain::BLOB{newPath}; 183 newMail.setMimeMessage(newPath.toUtf8());
154 blob.isExternal = false; 184 //Remove the olde mime message if there is a new one
155 newMail.setProperty(ApplicationDomain::Mail::MimeMessage::name, QVariant::fromValue(blob)); 185 QFile::remove(getFilePathFromMimeMessagePath(oldMail.getMimeMessage()));
186 }
187 } else {
188 newMail.setMimeMessage(storeMessage(data, newMail.getFolder()).toUtf8());
156 //Remove the olde mime message if there is a new one 189 //Remove the olde mime message if there is a new one
157 QFile::remove(oldPath); 190 QFile::remove(getFilePathFromMimeMessagePath(oldMail.getMimeMessage()));
158 } 191 }
159 } 192 }
160 193
161 auto mimeMessagePath = newMail.getMimeMessagePath(); 194 auto mimeMessagePath = newMail.getMimeMessage();
162 const auto maildirPath = getPath(newMail.getFolder()); 195 const auto maildirPath = getPath(newMail.getFolder());
163 KPIM::Maildir maildir(maildirPath, false); 196 KPIM::Maildir maildir(maildirPath, false);
164 const auto file = getFilePathFromMimeMessagePath(mimeMessagePath); 197 QString identifier = KPIM::Maildir::getKeyFromFile(getFilePathFromMimeMessagePath(mimeMessagePath));
165 QString identifier = KPIM::Maildir::getKeyFromFile(file);
166 198
167 //get flags from 199 //get flags from
168 KPIM::Maildir::Flags flags; 200 KPIM::Maildir::Flags flags;
@@ -179,7 +211,7 @@ public:
179 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE 211 void deletedEntity(const Sink::ApplicationDomain::ApplicationDomainType &oldEntity) Q_DECL_OVERRIDE
180 { 212 {
181 const ApplicationDomain::Mail oldMail{oldEntity}; 213 const ApplicationDomain::Mail oldMail{oldEntity};
182 const auto filePath = getFilePathFromMimeMessagePath(oldMail.getMimeMessagePath()); 214 const auto filePath = getFilePathFromMimeMessagePath(oldMail.getMimeMessage());
183 QFile::remove(filePath); 215 QFile::remove(filePath);
184 } 216 }
185 QByteArray mResourceInstanceIdentifier; 217 QByteArray mResourceInstanceIdentifier;
@@ -327,9 +359,7 @@ public:
327 mail.setFolder(folderLocalId); 359 mail.setFolder(folderLocalId);
328 //We only store the directory path + key, so we facade can add the changing bits (flags) 360 //We only store the directory path + key, so we facade can add the changing bits (flags)
329 auto path = KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey; 361 auto path = KPIM::Maildir::getDirectoryFromFile(filePath) + maildirKey;
330 auto blob = ApplicationDomain::BLOB{path}; 362 mail.setMimeMessage(path.toUtf8());
331 blob.isExternal = false;
332 mail.setProperty(ApplicationDomain::Mail::MimeMessage::name, QVariant::fromValue(blob));
333 mail.setUnread(!flags.testFlag(KPIM::Maildir::Seen)); 363 mail.setUnread(!flags.testFlag(KPIM::Maildir::Seen));
334 mail.setImportant(flags.testFlag(KPIM::Maildir::Flagged)); 364 mail.setImportant(flags.testFlag(KPIM::Maildir::Flagged));
335 mail.setExtractedFullPayloadAvailable(true); 365 mail.setExtractedFullPayloadAvailable(true);
@@ -396,7 +426,7 @@ public:
396 KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE 426 KAsync::Job<QByteArray> replay(const ApplicationDomain::Mail &mail, Sink::Operation operation, const QByteArray &oldRemoteId, const QList<QByteArray> &changedProperties) Q_DECL_OVERRIDE
397 { 427 {
398 if (operation == Sink::Operation_Creation) { 428 if (operation == Sink::Operation_Creation) {
399 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); 429 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessage());
400 SinkTrace() << "Mail created: " << remoteId; 430 SinkTrace() << "Mail created: " << remoteId;
401 return KAsync::value(remoteId.toUtf8()); 431 return KAsync::value(remoteId.toUtf8());
402 } else if (operation == Sink::Operation_Removal) { 432 } else if (operation == Sink::Operation_Removal) {
@@ -404,7 +434,7 @@ public:
404 return KAsync::null<QByteArray>(); 434 return KAsync::null<QByteArray>();
405 } else if (operation == Sink::Operation_Modification) { 435 } else if (operation == Sink::Operation_Modification) {
406 SinkTrace() << "Modifying a mail: " << oldRemoteId; 436 SinkTrace() << "Modifying a mail: " << oldRemoteId;
407 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); 437 const auto remoteId = getFilePathFromMimeMessagePath(mail.getMimeMessage());
408 return KAsync::value(remoteId.toUtf8()); 438 return KAsync::value(remoteId.toUtf8());
409 } 439 }
410 return KAsync::null<QByteArray>(); 440 return KAsync::null<QByteArray>();
@@ -460,7 +490,7 @@ protected:
460 490
461 if (domainType == ENTITY_TYPE_MAIL) { 491 if (domainType == ENTITY_TYPE_MAIL) {
462 auto mail = entityStore.readLatest<Sink::ApplicationDomain::Mail>(entityId); 492 auto mail = entityStore.readLatest<Sink::ApplicationDomain::Mail>(entityId);
463 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessagePath()); 493 const auto filePath = getFilePathFromMimeMessagePath(mail.getMimeMessage());
464 494
465 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) { 495 if (inspectionType == Sink::ResourceControl::Inspection::PropertyInspectionType) {
466 if (property == "unread") { 496 if (property == "unread") {