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