From 7df23b8afc72a178711ae8223eb3fd7f039c6849 Mon Sep 17 00:00:00 2001 From: Max Kellermann Date: Mon, 2 Sep 2024 21:48:59 +0200 Subject: [PATCH] lb/TranslationCache: use class IntrusiveCache instead of StaticCache The StaticCache class has a huge memory usage even when it's empty and is badly tuned for large installations. Let's use the new IntrusiveCache class which allocates items dynamically, preparing for making the cache size configurable. --- debian/changelog | 1 + src/lb/TranslationCache.cxx | 31 ++++++++++++-------------- src/lb/TranslationCache.hxx | 42 +++++++++++++++++++++++++++++------ src/lb/TranslationHandler.cxx | 3 ++- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/debian/changelog b/debian/changelog index 883059781..7079b7055 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ cm4all-beng-proxy (18.2) unstable; urgency=low * spawn: do not log "failed to kill: No such process" * lb/translation: store ANALYTICS_ID and GENERATOR in the cache + * lb/translation: dynamic cache allocation -- diff --git a/src/lb/TranslationCache.cxx b/src/lb/TranslationCache.cxx index c2709ff9f..fe29fc63e 100644 --- a/src/lb/TranslationCache.cxx +++ b/src/lb/TranslationCache.cxx @@ -134,8 +134,9 @@ class LbTranslationCacheKeyIterator { } }; -LbTranslationCache::Item::Item(const TranslateResponse &response) noexcept - :status(response.status), +LbTranslationCache::Item::Item(const char *_key, const TranslateResponse &response) noexcept + :key(_key), + status(response.status), https_only(response.https_only) { if (response.redirect != nullptr) @@ -163,11 +164,7 @@ LbTranslationCache::Item::Item(const TranslateResponse &response) noexcept CacheStats LbTranslationCache::GetStats() const noexcept { - size_t size = 0; - - cache.ForEach([&size](const std::string &key, const Item &item){ - size += key.length() + item.GetAllocatedMemory(); - }); + const std::size_t size = cache.GetTotalSize(); stats.allocator = { .brutto_size = size, @@ -180,7 +177,7 @@ LbTranslationCache::GetStats() const noexcept void LbTranslationCache::Clear() noexcept { - cache.Clear(); + cache.clear(); seen_vary.Clear(); } @@ -232,15 +229,14 @@ LbTranslationCache::Invalidate(const TranslationInvalidateRequest &request) noex return; if (request.site != nullptr) - per_site.remove_and_dispose_key_if(request.site, [this, &request](const Item &item){ - const auto &key = cache.KeyOf(item); - return MatchKey(key.c_str(), request) && MatchItem(item, request); + per_site.remove_and_dispose_key_if(request.site, [&request](const Item &item){ + return MatchKey(item.key.c_str(), request) && MatchItem(item, request); }, [this](Item *item){ cache.RemoveItem(*item); }); else - cache.RemoveIf([&request](const std::string &key, const Item &item){ - return MatchKey(key.c_str(), request) && MatchItem(item, request); + cache.RemoveIf([&request](const Item &item){ + return MatchKey(item.key.c_str(), request) && MatchItem(item, request); }); } @@ -275,7 +271,7 @@ LbTranslationCache::Put(const IncomingHttpRequest &request, const Vary vary(response); - if (!vary && !cache.IsEmpty()) { + if (!vary && !cache.empty()) { logger(4, "VARY disappeared, clearing cache"); Clear(); } @@ -288,7 +284,8 @@ LbTranslationCache::Put(const IncomingHttpRequest &request, logger(4, "store '", key, "'"); ++stats.stores; - auto &item = cache.PutOrReplace(std::string_view{key}, response); - if (!item.site.empty()) - per_site.insert(item); + auto *item = new Item(key, response); + if (!item->site.empty()) + per_site.insert(*item); + cache.Put(*item); } diff --git a/src/lb/TranslationCache.hxx b/src/lb/TranslationCache.hxx index ddc1c31f1..97db5a072 100644 --- a/src/lb/TranslationCache.hxx +++ b/src/lb/TranslationCache.hxx @@ -6,7 +6,8 @@ #include "stats/CacheStats.hxx" #include "io/Logger.hxx" -#include "util/StaticCache.hxx" +#include "util/DeleteDisposer.hxx" +#include "util/IntrusiveCache.hxx" #include "util/IntrusiveList.hxx" #include @@ -24,28 +25,46 @@ class LbTranslationCache final { public: struct Item { + IntrusiveCacheHook cache_hook; IntrusiveHashSetHook per_site_hook; + std::string key; + HttpStatus status = {}; uint16_t https_only = 0; std::string redirect, message, pool, canonical_host, site, analytics_id, generator; [[nodiscard]] - explicit Item(const TranslateResponse &response) noexcept; + explicit Item(const char *_key, const TranslateResponse &response) noexcept; [[gnu::pure]] size_t GetAllocatedMemory() const noexcept { - return sizeof(*this) + redirect.length() + message.length() + + return sizeof(*this) + key.length() + + redirect.length() + message.length() + pool.length() + canonical_host.length() + site.length() + analytics_id.length() + generator.length(); } + struct GetKey { + [[gnu::pure]] + std::string_view operator()(const Item &item) const noexcept { + return item.key; + } + }; + struct GetSite { [[gnu::pure]] std::string_view operator()(const Item &item) const noexcept { return item.site; } }; + + struct SizeOf { + [[gnu::pure]] + std::size_t operator()(const Item &item) const noexcept { + return item.GetAllocatedMemory(); + } + }; }; struct Vary { @@ -88,8 +107,17 @@ private: std::equal_to>, IntrusiveHashSetMemberHookTraits<&Item::per_site_hook>> per_site; - using Cache = StaticCache, std::equal_to>; + using Cache = + IntrusiveCache, + std::equal_to, + Item::SizeOf, + DeleteDisposer>, + IntrusiveCacheMemberHookTraits<&Item::cache_hook>>; + Cache cache; Vary seen_vary; @@ -98,8 +126,8 @@ private: public: [[nodiscard]] - LbTranslationCache() noexcept - :logger("tcache") {} + explicit LbTranslationCache(std::size_t max_size) noexcept + :logger("tcache"), cache(max_size) {} [[gnu::pure]] CacheStats GetStats() const noexcept; diff --git a/src/lb/TranslationHandler.cxx b/src/lb/TranslationHandler.cxx index 7afc7965b..0cfac0162 100644 --- a/src/lb/TranslationHandler.cxx +++ b/src/lb/TranslationHandler.cxx @@ -154,7 +154,8 @@ LbTranslationHandler::PutCache(const IncomingHttpRequest &request, return; if (!cache) - cache.reset(new LbTranslationCache()); + // TODO configurable cache size + cache.reset(new LbTranslationCache(256 * std::size_t{1024 * 1024})); cache->Put(request, listener_tag, response); }