Akonadi Search

xapiandatabase.cpp
1 /*
2  * SPDX-FileCopyrightText: 2014 Vishesh Handa <[email protected]>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  *
6  */
7 
8 #include "xapiandatabase.h"
9 #include "xapiandocument.h"
10 
11 #include "akonadi_search_xapian_debug.h"
12 #include <QDir>
13 
14 #if defined(HAVE_MALLOC_H)
15 #include <malloc.h>
16 #endif
17 
18 #include <chrono>
19 #include <thread>
20 
21 using namespace Akonadi::Search;
22 
23 XapianDatabase::XapianDatabase(const QString &path, bool writeOnly)
24  : m_writeOnly(writeOnly)
25 {
26  QDir().mkpath(path);
27  m_path = path.toStdString();
28 
29  if (!writeOnly) {
30  try {
31  createWritableDb();
32  m_db = new Xapian::Database(m_path);
33  } catch (const Xapian::DatabaseError &err) {
34  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Serious Error: " << err.get_error_string();
35  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_msg().c_str() << err.get_context().c_str() << err.get_description().c_str();
36  }
37 
38  // Possible errors - DatabaseLock error
39  // Corrupt and InvalidID error
40  } else {
41  m_wDb = createWritableDb();
42  }
43 }
44 
45 XapianDatabase::~XapianDatabase()
46 {
47  delete m_db;
48 }
49 
50 void XapianDatabase::replaceDocument(uint id, const XapianDocument &doc)
51 {
52  replaceDocument(id, doc.doc());
53 }
54 
55 void XapianDatabase::replaceDocument(uint id, const Xapian::Document &doc)
56 {
57  if (m_writeOnly) {
58  try {
59  m_wDb.replace_document(id, doc);
60  } catch (const Xapian::Error &) {
61  }
62  return;
63  }
64  m_docsToAdd << qMakePair(id, doc);
65 }
66 
67 void XapianDatabase::deleteDocument(uint id)
68 {
69  if (id == 0) {
70  return;
71  }
72 
73  if (m_writeOnly) {
74  try {
75  m_wDb.delete_document(id);
76  } catch (const Xapian::Error &) {
77  }
78  return;
79  }
80  m_docsToRemove << id;
81 }
82 
84 {
85  return !m_docsToAdd.isEmpty() || !m_docsToRemove.isEmpty();
86 }
87 
89 {
90  if (m_writeOnly) {
91  try {
92  m_wDb.commit();
93  } catch (const Xapian::Error &err) {
94  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
95  }
96  return;
97  }
98 
99  if (!haveChanges()) {
100  return;
101  }
102 
103  Xapian::WritableDatabase wdb = createWritableDb();
104 
105  qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Adding:" << m_docsToAdd.size() << "docs";
106  for (const DocIdPair &doc : std::as_const(m_docsToAdd)) {
107  try {
108  wdb.replace_document(doc.first, doc.second);
109  } catch (const Xapian::Error &) {
110  }
111  }
112 
113  qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Removing:" << m_docsToRemove.size() << "docs";
114  for (Xapian::docid id : std::as_const(m_docsToRemove)) {
115  try {
116  wdb.delete_document(id);
117  } catch (const Xapian::Error &) {
118  }
119  }
120 
121  try {
122  wdb.commit();
123  m_db->reopen();
124  } catch (const Xapian::Error &err) {
125  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
126  }
127  qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Xapian Committed";
128 
129  m_docsToAdd.clear();
130  m_docsToRemove.clear();
131 
132 #if defined(HAVE_MALLOC_TRIM)
133  malloc_trim(0);
134 #endif
135 }
136 
137 XapianDocument XapianDatabase::document(uint id)
138 {
139  try {
140  Xapian::Document xdoc;
141  if (m_writeOnly) {
142  xdoc = m_wDb.get_document(id);
143  } else {
144  xdoc = m_db->get_document(id);
145  }
146  return XapianDocument(xdoc);
147  } catch (const Xapian::DatabaseModifiedError &) {
148  m_db->reopen();
149  return document(id);
150  } catch (const Xapian::Error &) {
151  return {};
152  }
153 }
154 
155 Xapian::WritableDatabase XapianDatabase::createWritableDb()
156 {
157  // We need to keep sleeping for a required amount, until we reach
158  // a threshold. That's when we decide to abort?
159  for (int i = 1; i <= 20; ++i) {
160  try {
161  Xapian::WritableDatabase wdb(m_path, Xapian::DB_CREATE_OR_OPEN);
162  return wdb;
163  } catch (const Xapian::DatabaseLockError &) {
164  std::this_thread::sleep_for(std::chrono::milliseconds(i * 50));
165  } catch (const Xapian::DatabaseModifiedError &) {
166  std::this_thread::sleep_for(std::chrono::milliseconds(i * 50));
167  } catch (const Xapian::DatabaseCreateError &err) {
168  qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
169  return {};
170  } catch (const Xapian::DatabaseCorruptError &err) {
171  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Database Corrupted - What did you do?";
172  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string();
173  return {};
174  } catch (...) {
175  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Bananana Error";
176  return {};
177  }
178  }
179 
180  qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Could not obtain lock for Xapian Database. This is bad";
181  return {};
182 }
bool haveChanges() const
Returns true if the XapianDatabase has changes which need to be committed.
void commit()
Commit all the pending changes.
int size() const const
This class is just a light wrapper over Xapian::Document which provides nice Qt apis.
Akonadi search infrastructure.
Definition: core/query.h:20
std::string toStdString() const const
bool mkpath(const QString &dirPath) const const
bool isEmpty() const const
XapianDatabase(const QString &path, bool writeOnly=false)
Create the Xapian db at path path.
void clear()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 04:08:49 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.