File indexing completed on 2025-01-05 04:47:02

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003     SPDX-FileCopyrightText: 2014 Daniel Vrátil <dvratil@redhat.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "cachecleaner.h"
0009 #include "akonadi.h"
0010 #include "akonadiserver_debug.h"
0011 #include "storage/datastore.h"
0012 #include "storage/entity.h"
0013 #include "storage/parthelper.h"
0014 #include "storage/selectquerybuilder.h"
0015 
0016 #include "private/protocol_p.h"
0017 
0018 #include <QDateTime>
0019 
0020 using namespace Akonadi::Server;
0021 
0022 QMutex CacheCleanerInhibitor::sLock;
0023 int CacheCleanerInhibitor::sInhibitCount = 0;
0024 
0025 CacheCleanerInhibitor::CacheCleanerInhibitor(AkonadiServer &akonadi, bool doInhibit)
0026     : mCleaner(akonadi.cacheCleaner())
0027 {
0028     if (doInhibit) {
0029         inhibit();
0030     }
0031 }
0032 
0033 CacheCleanerInhibitor::~CacheCleanerInhibitor()
0034 {
0035     if (mInhibited) {
0036         uninhibit();
0037     }
0038 }
0039 
0040 void CacheCleanerInhibitor::inhibit()
0041 {
0042     if (mInhibited) {
0043         qCCritical(AKONADISERVER_LOG) << "Cannot recursively inhibit an inhibitor";
0044         return;
0045     }
0046 
0047     sLock.lock();
0048     if (++sInhibitCount == 1) {
0049         if (mCleaner) {
0050             mCleaner->inhibit(true);
0051         }
0052     }
0053     sLock.unlock();
0054     mInhibited = true;
0055 }
0056 
0057 void CacheCleanerInhibitor::uninhibit()
0058 {
0059     if (!mInhibited) {
0060         qCCritical(AKONADISERVER_LOG) << "Cannot uninhibit an uninhibited inhibitor"; // aaaw yeah
0061         return;
0062     }
0063     mInhibited = false;
0064 
0065     sLock.lock();
0066     Q_ASSERT(sInhibitCount > 0);
0067     if (--sInhibitCount == 0) {
0068         if (mCleaner) {
0069             mCleaner->inhibit(false);
0070         }
0071     }
0072     sLock.unlock();
0073 }
0074 
0075 CacheCleaner::CacheCleaner(QObject *parent)
0076     : CollectionScheduler(QStringLiteral("CacheCleaner"), QThread::IdlePriority, parent)
0077 {
0078     setMinimumInterval(5);
0079 }
0080 
0081 CacheCleaner::~CacheCleaner()
0082 {
0083     quitThread();
0084 }
0085 
0086 int CacheCleaner::collectionScheduleInterval(const Collection &collection)
0087 {
0088     return collection.cachePolicyCacheTimeout();
0089 }
0090 
0091 bool CacheCleaner::hasChanged(const Collection &collection, const Collection &changed)
0092 {
0093     return collection.cachePolicyLocalParts() != changed.cachePolicyLocalParts() || collection.cachePolicyCacheTimeout() != changed.cachePolicyCacheTimeout()
0094         || collection.cachePolicyInherit() != changed.cachePolicyInherit();
0095 }
0096 
0097 bool CacheCleaner::shouldScheduleCollection(const Collection &collection)
0098 {
0099     return collection.cachePolicyLocalParts() != QLatin1StringView("ALL") && collection.cachePolicyCacheTimeout() >= 0
0100         && (collection.enabled() || (collection.displayPref() == Collection::True) || (collection.syncPref() == Collection::True)
0101             || (collection.indexPref() == Collection::True))
0102         && collection.resourceId() > 0;
0103 }
0104 
0105 void CacheCleaner::collectionExpired(const Collection &collection)
0106 {
0107     SelectQueryBuilder<Part> qb;
0108     qb.addJoin(QueryBuilder::InnerJoin, PimItem::tableName(), Part::pimItemIdColumn(), PimItem::idFullColumnName());
0109     qb.addJoin(QueryBuilder::InnerJoin, PartType::tableName(), Part::partTypeIdFullColumnName(), PartType::idFullColumnName());
0110     qb.addValueCondition(PimItem::collectionIdFullColumnName(), Query::Equals, collection.id());
0111     qb.addValueCondition(PimItem::atimeFullColumnName(), Query::Less, QDateTime::currentDateTimeUtc().addSecs(-60 * collection.cachePolicyCacheTimeout()));
0112     qb.addValueCondition(Part::dataFullColumnName(), Query::IsNot, QVariant());
0113     qb.addValueCondition(PartType::nsFullColumnName(), Query::Equals, QLatin1StringView("PLD"));
0114     qb.addValueCondition(PimItem::dirtyFullColumnName(), Query::Equals, false);
0115 
0116     const QStringList partNames = collection.cachePolicyLocalParts().split(QLatin1Char(' '));
0117     for (QString partName : partNames) {
0118         if (partName.startsWith(QLatin1StringView(AKONADI_PARAM_PLD))) {
0119             partName.remove(0, 4);
0120         }
0121         qb.addValueCondition(PartType::nameFullColumnName(), Query::NotEquals, partName);
0122     }
0123     if (qb.exec()) {
0124         const Part::List parts = qb.result();
0125         if (!parts.isEmpty()) {
0126             qCInfo(AKONADISERVER_LOG) << "CacheCleaner found" << parts.count() << "item parts to expire in collection" << collection.name();
0127             // clear data field
0128             for (Part part : parts) {
0129                 try {
0130                     if (!PartHelper::truncate(part)) {
0131                         qCWarning(AKONADISERVER_LOG) << "CacheCleaner failed to expire item part" << part.id();
0132                     }
0133                 } catch (const PartHelperException &e) {
0134                     qCCritical(AKONADISERVER_LOG) << e.type() << e.what();
0135                 }
0136             }
0137         }
0138     }
0139 }
0140 
0141 #include "moc_cachecleaner.cpp"