1 /*
2 * This file is part of the KDE Akonadi Search Project
3 * SPDX-FileCopyrightText: 2013 Vishesh Handa <me@vhanda.in>
4 *
5 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 *
7 */
8
9 #include "emailindexer.h"
10
11 #include <QApplication>
12 #include <QDebug>
13 #include <QElapsedTimer>
14 #include <QFile>
15 #include <QTimer>
16
17 #include <Akonadi/Collection>
18 #include <Akonadi/CollectionFetchJob>
19 #include <Akonadi/Item>
20 #include <Akonadi/ItemFetchJob>
21 #include <Akonadi/ItemFetchScope>
22 #include <chrono>
23
24 using namespace std::chrono_literals;
25 class App : public QApplication
26 {
27 Q_OBJECT
28 public:
29 App(int &argc, char **argv, int flags = ApplicationFlags);
30
31 private Q_SLOTS:
32 void main();
33
34 void slotRootCollectionsFetched(KJob *job);
35 void indexNextCollection();
36 void itemReceived(const Akonadi::Item::List &item);
37 void slotIndexed();
38 void slotCommitTimerElapsed();
39
40 private:
41 Akonadi::Collection::List m_collections;
42 EmailIndexer m_indexer;
43
44 QElapsedTimer m_totalTime;
45 int m_indexTime;
46 int m_numEmails;
47
48 QTimer m_commitTimer;
49 };
50
main(int argc,char ** argv)51 int main(int argc, char **argv)
52 {
53 App app(argc, argv);
54 return app.exec();
55 }
56
App(int & argc,char ** argv,int flags)57 App::App(int &argc, char **argv, int flags)
58 : QApplication(argc, argv, flags)
59 , m_indexer(QStringLiteral("/tmp/xap"), QStringLiteral("/tmp/xapC"))
60 {
61 QTimer::singleShot(0, this, &App::main);
62 }
63
main()64 void App::main()
65 {
66 m_commitTimer.setInterval(1s);
67 connect(&m_commitTimer, &QTimer::timeout, this, &App::slotCommitTimerElapsed);
68 m_commitTimer.start();
69
70 auto job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
71 connect(job, &Akonadi::CollectionFetchJob::finished, this, &App::slotRootCollectionsFetched);
72 job->start();
73
74 m_numEmails = 0;
75 m_indexTime = 0;
76 m_totalTime.start();
77 }
78
slotRootCollectionsFetched(KJob * kjob)79 void App::slotRootCollectionsFetched(KJob *kjob)
80 {
81 auto job = qobject_cast<Akonadi::CollectionFetchJob *>(kjob);
82 m_collections = job->collections();
83
84 QMutableVectorIterator<Akonadi::Collection> it(m_collections);
85 while (it.hasNext()) {
86 const Akonadi::Collection &c = it.next();
87 const QStringList mimeTypes = c.contentMimeTypes();
88 if (!c.contentMimeTypes().contains(QLatin1String("message/rfc822"))) {
89 it.remove();
90 }
91 }
92
93 if (m_collections.size()) {
94 indexNextCollection();
95 } else {
96 qDebug() << "No collections to index";
97 }
98 }
99
indexNextCollection()100 void App::indexNextCollection()
101 {
102 auto fetchJob = new Akonadi::ItemFetchJob(m_collections.takeFirst(), this);
103 fetchJob->fetchScope().fetchAllAttributes(true);
104 fetchJob->fetchScope().fetchFullPayload(true);
105 fetchJob->fetchScope().setFetchModificationTime(false);
106 fetchJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
107 fetchJob->setDeliveryOption(Akonadi::ItemFetchJob::EmitItemsIndividually);
108
109 connect(fetchJob, &Akonadi::ItemFetchJob::itemsReceived, this, &App::itemReceived);
110 connect(fetchJob, &Akonadi::ItemFetchJob::result, this, &App::slotIndexed);
111 }
112
itemReceived(const Akonadi::Item::List & itemList)113 void App::itemReceived(const Akonadi::Item::List &itemList)
114 {
115 QElapsedTimer timer;
116 timer.start();
117
118 for (const Akonadi::Item &item : itemList) {
119 m_indexer.index(item);
120 }
121
122 m_indexTime += timer.elapsed();
123 m_numEmails += itemList.size();
124 }
125
slotCommitTimerElapsed()126 void App::slotCommitTimerElapsed()
127 {
128 QElapsedTimer timer;
129 timer.start();
130
131 m_indexer.commit();
132 m_indexTime += timer.elapsed();
133
134 qDebug() << "Emails:" << m_numEmails;
135 qDebug() << "Total Time:" << m_totalTime.elapsed() / 1000.0 << " seconds";
136 qDebug() << "Index Time:" << m_indexTime / 1000.0 << " seconds";
137 }
138
slotIndexed()139 void App::slotIndexed()
140 {
141 if (!m_collections.isEmpty()) {
142 QTimer::singleShot(0, this, &App::indexNextCollection);
143 return;
144 }
145
146 m_indexer.commit();
147
148 qDebug() << "Emails:" << m_numEmails;
149 qDebug() << "Total Time:" << m_totalTime.elapsed() / 1000.0 << " seconds";
150 qDebug() << "Index Time:" << m_indexTime / 1000.0 << " seconds";
151
152 // Print the io usage
153 QFile file(QStringLiteral("/proc/self/io"));
154 file.open(QIODevice::ReadOnly | QIODevice::Text);
155
156 QTextStream fs(&file);
157 QString str = fs.readAll();
158
159 qDebug() << "------- IO ---------";
160 QTextStream stream(&str);
161 while (!stream.atEnd()) {
162 QString str = stream.readLine();
163
164 QString rchar(QStringLiteral("rchar: "));
165 if (str.startsWith(rchar)) {
166 ulong amt = str.midRef(rchar.size()).toULong();
167 qDebug() << "Read:" << amt / 1024 << "kb";
168 }
169
170 QString wchar(QStringLiteral("wchar: "));
171 if (str.startsWith(wchar)) {
172 ulong amt = str.midRef(wchar.size()).toULong();
173 qDebug() << "Write:" << amt / 1024 << "kb";
174 }
175
176 QString read(QStringLiteral("read_bytes: "));
177 if (str.startsWith(read)) {
178 ulong amt = str.midRef(read.size()).toULong();
179 qDebug() << "Actual Reads:" << amt / 1024 << "kb";
180 }
181
182 QString write(QStringLiteral("write_bytes: "));
183 if (str.startsWith(write)) {
184 ulong amt = str.midRef(write.size()).toULong();
185 qDebug() << "Actual Writes:" << amt / 1024 << "kb";
186 }
187 }
188 qDebug() << "\nThe actual read/writes may be 0 because of an existing"
189 << "cache and /tmp being memory mapped";
190 quit();
191 }
192
193 #include "emailtest.moc"
194