diff --git a/src/app/options/qgsoptions.cpp b/src/app/options/qgsoptions.cpp index f85c7bbafe51c..5e1c6dbd9c4ac 100644 --- a/src/app/options/qgsoptions.cpp +++ b/src/app/options/qgsoptions.cpp @@ -486,10 +486,10 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QListsetPlaceholderText( QStandardPaths::writableLocation( QStandardPaths::CacheLocation ) ); mCacheSize->setMinimum( 0 ); mCacheSize->setMaximum( std::numeric_limits::max() ); - mCacheSize->setSingleStep( 1024 ); - qint64 cacheSize = mSettings->value( QStringLiteral( "cache/size" ), 256 * 1024 * 1024 ).toLongLong(); - mCacheSize->setValue( static_cast( cacheSize / 1024 ) ); - mCacheSize->setClearValue( 50 * 1024 ); + mCacheSize->setSingleStep( 50 ); + qint64 cacheSize = mSettings->value( QStringLiteral( "cache/size2" ), 0 ).toLongLong(); + mCacheSize->setValue( static_cast( cacheSize / 1024 / 1024 ) ); + mCacheSize->setClearValue( 0 ); connect( mBrowseCacheDirectory, &QAbstractButton::clicked, this, &QgsOptions::browseCacheDirectory ); connect( mClearCache, &QAbstractButton::clicked, this, &QgsOptions::clearCache ); @@ -1583,7 +1583,7 @@ void QgsOptions::saveOptions() else mSettings->remove( QStringLiteral( "cache/directory" ) ); - mSettings->setValue( QStringLiteral( "cache/size" ), QVariant::fromValue( mCacheSize->value() * 1024LL ) ); + mSettings->setValue( QStringLiteral( "cache/size2" ), QVariant::fromValue( mCacheSize->value() * 1024LL * 1024LL ) ); //url with no proxy at all QStringList noProxyUrls; diff --git a/src/core/network/qgsnetworkaccessmanager.cpp b/src/core/network/qgsnetworkaccessmanager.cpp index 9f976e49f93cf..de5a9830decf6 100644 --- a/src/core/network/qgsnetworkaccessmanager.cpp +++ b/src/core/network/qgsnetworkaccessmanager.cpp @@ -752,8 +752,13 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache( Qt::ConnectionType conn QString cacheDirectory = settings.value( QStringLiteral( "cache/directory" ) ).toString(); if ( cacheDirectory.isEmpty() ) cacheDirectory = QStandardPaths::writableLocation( QStandardPaths::CacheLocation ); - const qint64 cacheSize = settings.value( QStringLiteral( "cache/size" ), 256 * 1024 * 1024 ).toLongLong(); newcache->setCacheDirectory( cacheDirectory ); + qint64 cacheSize = settings.value( QStringLiteral( "cache/size2" ), 0 ).toLongLong(); + if ( cacheSize == 0 ) + { + // Calculatre maximum cache size based on available free space + cacheSize = QgsNetworkDiskCache::smartCacheSize( cacheDirectory ); + } newcache->setMaximumCacheSize( cacheSize ); QgsDebugMsgLevel( QStringLiteral( "cacheDirectory: %1" ).arg( newcache->cacheDirectory() ), 4 ); QgsDebugMsgLevel( QStringLiteral( "maximumCacheSize: %1" ).arg( newcache->maximumCacheSize() ), 4 ); diff --git a/src/core/network/qgsnetworkdiskcache.cpp b/src/core/network/qgsnetworkdiskcache.cpp index 3e4c78cf8f2be..eb1364a2c617e 100644 --- a/src/core/network/qgsnetworkdiskcache.cpp +++ b/src/core/network/qgsnetworkdiskcache.cpp @@ -18,6 +18,9 @@ #include "qgsnetworkdiskcache.h" +#include +#include + ///@cond PRIVATE ExpirableNetworkDiskCache QgsNetworkDiskCache::sDiskCache; ///@endcond @@ -111,3 +114,73 @@ void QgsNetworkDiskCache::clear() const QMutexLocker lock( &sDiskCacheMutex ); return sDiskCache.clear(); } + +qint64 QgsNetworkDiskCache::smartCacheSize( const QString &cacheDir ) +{ + static qint64 cacheSize = 0; + static std::once_flag initialized; + std::call_once( initialized, [ = ] + { + std::function dirSize; + dirSize = [&dirSize]( const QString & dirPath ) -> qint64 + { + qint64 size = 0; + QDir dir( dirPath ); + + const QStringList filePaths = dir.entryList( QDir::Files | QDir::System | QDir::Hidden ); + for ( const QString &filePath : filePaths ) + { + QFileInfo fi( dir, filePath ); + size += fi.size(); + } + + const QStringList childDirPaths = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot | QDir::System | QDir::Hidden ); + for ( const QString &childDirPath : childDirPaths ) + { + size += dirSize( dirPath + QDir::separator() + childDirPath ); + } + + return size; + }; + + qint64 bytesFree; + QStorageInfo storageInfo( cacheDir ); + bytesFree = storageInfo.bytesFree() + dirSize( cacheDir ); + + // Logic taken from Firefox's smart cache size handling + qint64 available10MB = bytesFree / 1024 / ( 1024LL * 10 ); + if ( available10MB > 2500 ) + { + // Cap the cache size to 1GB + cacheSize = 100; + } + else + { + if ( available10MB > 700 ) + { + // Add 2.5% of the free space above 7GB + cacheSize += static_cast( ( available10MB - 700 ) * 0.025 ); + available10MB = 700; + } + if ( available10MB > 50 ) + { + // Add 7.5% of free sapce between 500MB to 7GB + cacheSize += static_cast( ( available10MB - 50 ) * 0.075 ); + available10MB = 50; + } + +#if defined( Q_OS_ANDROID ) + // On Android, smaller/older devices may have very little storage + + // Add 16% of free space up to 500 MB + cacheSize += std::max( 2, static_cast( available10MB * 0.16 ) ); +#else + // Add 30% of free space up to 500 MB + cacheSize += std::max( 5, static_cast( available10MB * 0.30 ) ); +#endif + } + cacheSize = cacheSize * 10 * 1024 * 1024; + } ); + + return cacheSize; +} diff --git a/src/core/network/qgsnetworkdiskcache.h b/src/core/network/qgsnetworkdiskcache.h index b666f0bdac209..f802492ccbd77 100644 --- a/src/core/network/qgsnetworkdiskcache.h +++ b/src/core/network/qgsnetworkdiskcache.h @@ -20,6 +20,8 @@ #define SIP_NO_FILE +#include "qgis_core.h" + #include #include @@ -45,7 +47,7 @@ class ExpirableNetworkDiskCache : public QNetworkDiskCache * * \note not available in Python bindings */ -class QgsNetworkDiskCache : public QNetworkDiskCache +class CORE_EXPORT QgsNetworkDiskCache : public QNetworkDiskCache { Q_OBJECT @@ -87,6 +89,12 @@ class QgsNetworkDiskCache : public QNetworkDiskCache //! \see QNetworkDiskCache::fileMetaData() QNetworkCacheMetaData fileMetaData( const QString &fileName ) const; + /** + * Returns a smart cache size based on available megabytes. + * \since QGIS 3.40 + */ + static qint64 smartCacheSize( const QString &path ); + public slots: //! \see QNetworkDiskCache::clear() void clear() override; diff --git a/src/core/qgstiledownloadmanager.cpp b/src/core/qgstiledownloadmanager.cpp index a0a906cdacdca..7336318a605cb 100644 --- a/src/core/qgstiledownloadmanager.cpp +++ b/src/core/qgstiledownloadmanager.cpp @@ -19,6 +19,7 @@ #include "qgslogger.h" #include "qgsnetworkaccessmanager.h" +#include "qgsnetworkdiskcache.h" #include "qgsrangerequestcache.h" #include "qgssettings.h" @@ -198,9 +199,13 @@ QgsTileDownloadManager::QgsTileDownloadManager() cacheDirectory.push_back( QDir::separator() ); } cacheDirectory += QLatin1String( "http-ranges" ); - const qint64 cacheSize = settings.value( QStringLiteral( "cache/size" ), 256 * 1024 * 1024 ).toLongLong(); - mRangesCache->setCacheDirectory( cacheDirectory ); + qint64 cacheSize = settings.value( QStringLiteral( "cache/size2" ), 0 ).toLongLong(); + if ( cacheSize == 0 ) + { + // Calculatre maximum cache size based on available free space + cacheSize = QgsNetworkDiskCache::smartCacheSize( cacheDirectory ); + } mRangesCache->setCacheSize( cacheSize ); } diff --git a/src/server/qgsserver.cpp b/src/server/qgsserver.cpp index 52475fb5d5c39..0af2788433c44 100644 --- a/src/server/qgsserver.cpp +++ b/src/server/qgsserver.cpp @@ -31,6 +31,7 @@ #include "qgslogger.h" #include "qgsmapserviceexception.h" #include "qgsnetworkaccessmanager.h" +#include "qgsnetworkdiskcache.h" #include "qgsserverlogger.h" #include "qgsserverrequest.h" #include "qgsfilterresponsedecorator.h" @@ -88,9 +89,14 @@ void QgsServer::setupNetworkAccessManager() const QSettings settings; QgsNetworkAccessManager *nam = QgsNetworkAccessManager::instance(); QNetworkDiskCache *cache = new QNetworkDiskCache( nullptr ); - const qint64 cacheSize = sSettings()->cacheSize(); const QString cacheDirectory = sSettings()->cacheDirectory(); cache->setCacheDirectory( cacheDirectory ); + qint64 cacheSize = sSettings()->cacheSize(); + if ( cacheSize == 0 ) + { + // Calculatre maximum cache size based on available free space + cacheSize = QgsNetworkDiskCache::smartCacheSize( cacheDirectory ); + } cache->setMaximumCacheSize( cacheSize ); QgsMessageLog::logMessage( QStringLiteral( "cacheDirectory: %1" ).arg( cache->cacheDirectory() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info ); QgsMessageLog::logMessage( QStringLiteral( "maximumCacheSize: %1" ).arg( cache->maximumCacheSize() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Info ); diff --git a/src/server/qgsserversettings.cpp b/src/server/qgsserversettings.cpp index 001e23600a563..2f7d6184b8a97 100644 --- a/src/server/qgsserversettings.cpp +++ b/src/server/qgsserversettings.cpp @@ -124,9 +124,9 @@ void QgsServerSettings::initSettings() const Setting sCacheSize = { QgsServerSettingsEnv::QGIS_SERVER_CACHE_SIZE, QgsServerSettingsEnv::DEFAULT_VALUE, QStringLiteral( "Specify the cache size" ), - QStringLiteral( "/cache/size" ), + QStringLiteral( "/cache/size2" ), QMetaType::Type::LongLong, - QVariant( 256 * 1024 * 1024 ), + 0, QVariant() }; mSettings[ sCacheSize.envVar ] = sCacheSize; diff --git a/src/ui/qgsoptionsbase.ui b/src/ui/qgsoptionsbase.ui index 38c325754a927..8de6aaac130dd 100644 --- a/src/ui/qgsoptionsbase.ui +++ b/src/ui/qgsoptionsbase.ui @@ -4862,7 +4862,7 @@ The bigger the number, the faster zooming with the mouse wheel will be. - Size [KiB] + Size @@ -4877,7 +4877,29 @@ The bigger the number, the faster zooming with the mouse wheel will be. - + + + MB + + + 0 + + + 10000 + + + 100 + + + 0 + + + true + + + Smart cache size + + diff --git a/tests/src/python/test_qgsserver_settings.py b/tests/src/python/test_qgsserver_settings.py index 4d77f033513f9..00ab68bc8284d 100644 --- a/tests/src/python/test_qgsserver_settings.py +++ b/tests/src/python/test_qgsserver_settings.py @@ -20,7 +20,7 @@ from utilities import unitTestDataPath -DEFAULT_CACHE_SIZE = 256 * 1024 * 1024 +DEFAULT_CACHE_SIZE = 0 # Smart cache size class TestQgsServerSettings(unittest.TestCase):