diff --git a/src/ServerMain.cpp b/src/ServerMain.cpp index 64406f08cf..f50d12cba2 100644 --- a/src/ServerMain.cpp +++ b/src/ServerMain.cpp @@ -82,6 +82,11 @@ int main(int argc, char** argv) { optionFactory.getProgramOption<"cache-max-size-single-entry">(), "Maximum size for a single cache entry. That is, " "results larger than this will not be cached unless pinned."); + add("lazy-result-max-cache-size,E", + optionFactory.getProgramOption<"lazy-result-max-cache-size">(), + "Maximum size up to which lazy results will be cached by aggregating " + "partial results. Caching does cause significant overhead for this " + "case."); add("cache-max-num-entries,k", optionFactory.getProgramOption<"cache-max-num-entries">(), "Maximum number of entries in the cache. If exceeded, remove " diff --git a/src/engine/Operation.cpp b/src/engine/Operation.cpp index cacba381ae..766037d965 100644 --- a/src/engine/Operation.cpp +++ b/src/engine/Operation.cpp @@ -191,11 +191,13 @@ CacheValue Operation::runComputationAndPrepareForCache( const QueryCacheKey& cacheKey, bool pinned) { auto& cache = _executionContext->getQueryTreeCache(); auto result = runComputation(timer, computationMode); - if (!result.isFullyMaterialized() && - !unlikelyToFitInCache(cache.getMaxSizeSingleEntry())) { + auto maxSize = + std::min(RuntimeParameters().get<"lazy-result-max-cache-size">(), + cache.getMaxSizeSingleEntry()); + if (!result.isFullyMaterialized() && !unlikelyToFitInCache(maxSize)) { AD_CONTRACT_CHECK(!pinned); result.cacheDuringConsumption( - [maxSize = cache.getMaxSizeSingleEntry()]( + [maxSize]( const std::optional& currentIdTablePair, const Result::IdTableVocabPair& newIdTable) { auto currentSize = diff --git a/src/global/RuntimeParameters.h b/src/global/RuntimeParameters.h index 67c3381c2b..8e60725ffe 100644 --- a/src/global/RuntimeParameters.h +++ b/src/global/RuntimeParameters.h @@ -50,7 +50,10 @@ inline auto& RuntimeParameters() { Bool<"group-by-disable-index-scan-optimizations">{false}, SizeT<"service-max-value-rows">{10'000}, SizeT<"query-planning-budget">{1500}, - Bool<"throw-on-unbound-variables">{false}}; + Bool<"throw-on-unbound-variables">{false}, + // Control up until which size lazy results should be cached. Caching + // does cause significant overhead for this case. + MemorySizeParameter<"lazy-result-max-cache-size">{5_MB}}; }(); return params; } diff --git a/test/OperationTest.cpp b/test/OperationTest.cpp index 800e0c3b2b..3cc938f0c2 100644 --- a/test/OperationTest.cpp +++ b/test/OperationTest.cpp @@ -8,9 +8,11 @@ #include "engine/NeutralElementOperation.h" #include "engine/ValuesForTesting.h" +#include "global/RuntimeParameters.h" #include "util/IdTableHelpers.h" #include "util/IndexTestHelpers.h" #include "util/OperationTestHelpers.h" +#include "util/RuntimeParametersTestHelpers.h" using namespace ad_utility::testing; using namespace ::testing; @@ -591,20 +593,23 @@ TEST(Operation, checkLazyOperationIsNotCachedIfTooLarge) { ad_utility::Timer timer{ad_utility::Timer::InitialStatus::Started}; - auto originalSize = qec->getQueryTreeCache().getMaxSizeSingleEntry(); - - // Too small for storage - qec->getQueryTreeCache().setMaxSizeSingleEntry(1_B); - - auto cacheValue = valuesForTesting.runComputationAndPrepareForCache( - timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), - false); - EXPECT_FALSE( - qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); - qec->getQueryTreeCache().setMaxSizeSingleEntry(originalSize); + std::optional cacheValue = std::nullopt; + { + // Too small for storage, make sure to change back before consuming + // generator to additionally assert sure it is not re-read on every + // iteration. + auto cleanup = + setRuntimeParameterForTest<"lazy-result-max-cache-size">(1_B); + + cacheValue = valuesForTesting.runComputationAndPrepareForCache( + timer, ComputationMode::LAZY_IF_SUPPORTED, makeQueryCacheKey("test"), + false); + EXPECT_FALSE( + qec->getQueryTreeCache().cacheContains(makeQueryCacheKey("test"))); + } for ([[maybe_unused]] Result::IdTableVocabPair& _ : - cacheValue.resultTable().idTables()) { + cacheValue->resultTable().idTables()) { } EXPECT_FALSE(