File indexing completed on 2025-01-05 04:47:01
0001 /* 0002 SPDX-FileCopyrightText: 2012 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "querycache.h" 0008 #include "datastore.h" 0009 #include "dbtype.h" 0010 0011 #include <QHash> 0012 #include <QSqlDriver> 0013 #include <QSqlQuery> 0014 #include <QThreadStorage> 0015 #include <QTimer> 0016 0017 #include <chrono> 0018 #include <list> 0019 0020 using namespace std::chrono_literals; 0021 using namespace Akonadi; 0022 using namespace Akonadi::Server; 0023 0024 namespace 0025 { 0026 // After these seconds without activity the cache is cleaned 0027 constexpr auto CleanupTimeout = 60s; 0028 constexpr int MaxCacheSize = 50; 0029 0030 /// LRU cache with limited size and auto-cleanup after given 0031 /// period of time 0032 class Cache 0033 { 0034 public: 0035 Cache() 0036 { 0037 QObject::connect(&m_cleanupTimer, &QTimer::timeout, &m_cleanupTimer, std::bind(&Cache::cleanup, this)); 0038 m_cleanupTimer.setSingleShot(true); 0039 } 0040 0041 std::optional<QSqlQuery> query(const QString &queryStatement) 0042 { 0043 m_cleanupTimer.start(CleanupTimeout); 0044 auto it = m_keys.find(queryStatement); 0045 if (it == m_keys.end()) { 0046 return std::nullopt; 0047 } 0048 0049 auto node = **it; 0050 m_queries.erase(*it); 0051 m_queries.push_front(node); 0052 *it = m_queries.begin(); 0053 return node.query; 0054 } 0055 0056 void insert(const QString &queryStatement, const QSqlQuery &query) 0057 { 0058 if (m_queries.size() >= MaxCacheSize) { 0059 m_keys.remove(m_queries.back().queryStatement); 0060 m_queries.pop_back(); 0061 } 0062 0063 m_queries.emplace_front(Node{queryStatement, query}); 0064 m_keys.insert(queryStatement, m_queries.begin()); 0065 } 0066 0067 void cleanup() 0068 { 0069 m_keys.clear(); 0070 m_queries.clear(); 0071 } 0072 0073 public: // public, this is just a helper class 0074 struct Node { 0075 QString queryStatement; 0076 QSqlQuery query; 0077 }; 0078 std::list<Node> m_queries; 0079 QHash<QString, std::list<Node>::iterator> m_keys; 0080 QTimer m_cleanupTimer; 0081 }; 0082 0083 QThreadStorage<Cache *> g_queryCache; 0084 0085 Cache *perThreadCache() 0086 { 0087 if (!g_queryCache.hasLocalData()) { 0088 g_queryCache.setLocalData(new Cache()); 0089 } 0090 0091 return g_queryCache.localData(); 0092 } 0093 0094 } // namespace 0095 0096 std::optional<QSqlQuery> QueryCache::query(const QString &queryStatement) 0097 { 0098 return perThreadCache()->query(queryStatement); 0099 } 0100 0101 void QueryCache::insert(const QSqlDatabase &db, const QString &queryStatement, const QSqlQuery &query) 0102 { 0103 if (DbType::type(db) != DbType::Sqlite) { 0104 perThreadCache()->insert(queryStatement, query); 0105 } 0106 } 0107 0108 void QueryCache::clear() 0109 { 0110 if (!g_queryCache.hasLocalData()) { 0111 return; 0112 } 0113 0114 g_queryCache.localData()->cleanup(); 0115 }