File indexing completed on 2024-05-12 05:11:21
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in> 0003 * 0004 * SPDX-License-Identifier: LGPL-2.1-or-later 0005 * 0006 */ 0007 0008 #include "xapiandatabase.h" 0009 #include "xapiandocument.h" 0010 0011 #include "akonadi_search_xapian_debug.h" 0012 #include <QDir> 0013 0014 #if defined(HAVE_MALLOC_H) 0015 #include <malloc.h> 0016 #endif 0017 0018 #include <chrono> 0019 #include <thread> 0020 0021 using namespace Akonadi::Search; 0022 0023 XapianDatabase::XapianDatabase(const QString &path, bool writeOnly) 0024 : m_writeOnly(writeOnly) 0025 { 0026 QDir().mkpath(path); 0027 m_path = path.toStdString(); 0028 0029 if (!writeOnly) { 0030 try { 0031 createWritableDb(); 0032 m_db = new Xapian::Database(m_path); 0033 } catch (const Xapian::DatabaseError &err) { 0034 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Serious Error: " << err.get_error_string(); 0035 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_msg().c_str() << err.get_context().c_str() << err.get_description().c_str(); 0036 } 0037 0038 // Possible errors - DatabaseLock error 0039 // Corrupt and InvalidID error 0040 } else { 0041 m_wDb = createWritableDb(); 0042 } 0043 } 0044 0045 XapianDatabase::~XapianDatabase() 0046 { 0047 delete m_db; 0048 } 0049 0050 void XapianDatabase::replaceDocument(uint id, const XapianDocument &doc) 0051 { 0052 replaceDocument(id, doc.doc()); 0053 } 0054 0055 void XapianDatabase::replaceDocument(uint id, const Xapian::Document &doc) 0056 { 0057 if (m_writeOnly) { 0058 try { 0059 m_wDb.replace_document(id, doc); 0060 } catch (const Xapian::Error &) { 0061 } 0062 return; 0063 } 0064 m_docsToAdd << qMakePair(id, doc); 0065 } 0066 0067 void XapianDatabase::deleteDocument(uint id) 0068 { 0069 if (id == 0) { 0070 return; 0071 } 0072 0073 if (m_writeOnly) { 0074 try { 0075 m_wDb.delete_document(id); 0076 } catch (const Xapian::Error &) { 0077 } 0078 return; 0079 } 0080 m_docsToRemove << id; 0081 } 0082 0083 bool XapianDatabase::haveChanges() const 0084 { 0085 return !m_docsToAdd.isEmpty() || !m_docsToRemove.isEmpty(); 0086 } 0087 0088 void XapianDatabase::commit() 0089 { 0090 if (m_writeOnly) { 0091 try { 0092 m_wDb.commit(); 0093 } catch (const Xapian::Error &err) { 0094 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string(); 0095 } 0096 return; 0097 } 0098 0099 if (!haveChanges()) { 0100 return; 0101 } 0102 0103 Xapian::WritableDatabase wdb = createWritableDb(); 0104 0105 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Adding:" << m_docsToAdd.size() << "docs"; 0106 for (const DocIdPair &doc : std::as_const(m_docsToAdd)) { 0107 try { 0108 wdb.replace_document(doc.first, doc.second); 0109 } catch (const Xapian::Error &) { 0110 } 0111 } 0112 0113 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Removing:" << m_docsToRemove.size() << "docs"; 0114 for (Xapian::docid id : std::as_const(m_docsToRemove)) { 0115 try { 0116 wdb.delete_document(id); 0117 } catch (const Xapian::Error &) { 0118 } 0119 } 0120 0121 try { 0122 wdb.commit(); 0123 m_db->reopen(); 0124 } catch (const Xapian::Error &err) { 0125 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string(); 0126 } 0127 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << "Xapian Committed"; 0128 0129 m_docsToAdd.clear(); 0130 m_docsToRemove.clear(); 0131 0132 #if defined(HAVE_MALLOC_TRIM) 0133 malloc_trim(0); 0134 #endif 0135 } 0136 0137 XapianDocument XapianDatabase::document(uint id) 0138 { 0139 try { 0140 Xapian::Document xdoc; 0141 if (m_writeOnly) { 0142 xdoc = m_wDb.get_document(id); 0143 } else { 0144 xdoc = m_db->get_document(id); 0145 } 0146 return XapianDocument(xdoc); 0147 } catch (const Xapian::DatabaseModifiedError &) { 0148 m_db->reopen(); 0149 return document(id); 0150 } catch (const Xapian::Error &) { 0151 return {}; 0152 } 0153 } 0154 0155 Xapian::WritableDatabase XapianDatabase::createWritableDb() 0156 { 0157 // We need to keep sleeping for a required amount, until we reach 0158 // a threshold. That's when we decide to abort? 0159 for (int i = 1; i <= 20; ++i) { 0160 try { 0161 Xapian::WritableDatabase wdb(m_path, Xapian::DB_CREATE_OR_OPEN); 0162 return wdb; 0163 } catch (const Xapian::DatabaseLockError &) { 0164 std::this_thread::sleep_for(std::chrono::milliseconds(i * 50)); 0165 } catch (const Xapian::DatabaseModifiedError &) { 0166 std::this_thread::sleep_for(std::chrono::milliseconds(i * 50)); 0167 } catch (const Xapian::DatabaseCreateError &err) { 0168 qCDebug(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string(); 0169 return {}; 0170 } catch (const Xapian::DatabaseCorruptError &err) { 0171 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Database Corrupted - What did you do?"; 0172 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << err.get_error_string(); 0173 return {}; 0174 } catch (...) { 0175 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Bananana Error"; 0176 return {}; 0177 } 0178 } 0179 0180 qCWarning(AKONADI_SEARCH_XAPIAN_LOG) << "Could not obtain lock for Xapian Database. This is bad"; 0181 return {}; 0182 }