Skip to content

Commit e1d05c4

Browse files
Meryama03MongoDB Bot
authored andcommitted
SERVER-82367 Enqueued refreshes under the same inProgressLookup entry are always called with the same cached value in the ReadThroughCache - BACKPORT-24020 (#31615)
GitOrigin-RevId: 8193b1195989c5e90001b21f638d6441c843fefc
1 parent 29b4253 commit e1d05c4

File tree

2 files changed

+70
-1
lines changed

2 files changed

+70
-1
lines changed

src/mongo/util/read_through_cache.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,14 @@ class ReadThroughCache : public ReadThroughCacheBase {
574574
!inProgressLookup.empty(ul));
575575
}();
576576

577-
if (!mustDoAnotherLoop)
577+
if (!mustDoAnotherLoop) {
578578
_inProgressLookups.erase(it);
579+
} else if (result.isOK()) {
580+
// The fetched value can not satisfy all the enqueued refresh requests over the nss, but
581+
// it can still be leveraged as a base version to perform the lookups that are still
582+
// pending, optimizing the delta between the cached value and the remote one.
583+
inProgressLookup.updateCachedValue(ul, ValueHandle(result.getValue()));
584+
}
579585
ul.unlock();
580586

581587
// The only reason this loop pops the values as it goes and std::moves into the last value
@@ -746,6 +752,10 @@ class ReadThroughCache<Key, Value, Time>::InProgressLookup {
746752
_cancelToken->tryCancel();
747753
}
748754

755+
void updateCachedValue(WithLock, ValueHandle cachedValue) {
756+
_cachedValue = std::move(cachedValue);
757+
}
758+
749759
private:
750760
// The owning cache, from which mutex, lookupFn, async task scheduling, etc. will be used. It is
751761
// the responsibility of the owning cache to join all outstanding lookups at destruction time.

src/mongo/util/read_through_cache_test.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,18 @@
2727
* it in the license file.
2828
*/
2929

30+
#include <absl/container/node_hash_map.h>
31+
#include <boost/move/utility_core.hpp>
32+
#include <boost/none.hpp>
33+
#include <boost/optional/optional.hpp>
34+
#include <boost/smart_ptr.hpp>
35+
#include <cstddef>
36+
#include <fmt/format.h>
3037
#include <string>
3138

39+
#include "mongo/base/string_data.h"
40+
#include "mongo/bson/timestamp.h"
41+
#include "mongo/db/client.h"
3242
#include "mongo/db/concurrency/locker_noop_service_context_test_fixture.h"
3343
#include "mongo/db/operation_context.h"
3444
#include "mongo/unittest/barrier.h"
@@ -803,5 +813,54 @@ TEST_F(ReadThroughCacheAsyncTest, CacheSizeZero) {
803813
}));
804814
}
805815

816+
TEST_F(ReadThroughCacheAsyncTest, EnqueuedRequestLeveragesTheResultOfAnEarlierLookup) {
817+
MockThreadPool threadPool;
818+
boost::optional<CausallyConsistentCache::LookupResult> nextToReturn;
819+
boost::optional<int> valueFetchedByPreviousRequest;
820+
821+
CausallyConsistentCache cache(getServiceContext(),
822+
threadPool,
823+
1,
824+
[&](OperationContext*,
825+
const std::string&,
826+
const CausallyConsistentCache::ValueHandle& cachedValue,
827+
const Timestamp&) {
828+
if (valueFetchedByPreviousRequest.has_value()) {
829+
ASSERT_EQ(cachedValue->counter,
830+
*valueFetchedByPreviousRequest);
831+
} else {
832+
ASSERT(!cachedValue);
833+
}
834+
return std::move(*nextToReturn);
835+
});
836+
// Issue two requests, advancing the time in store so that each of them will require a "remote
837+
// lookup".
838+
ASSERT(cache.advanceTimeInStore("TestKey", Timestamp(100)));
839+
auto futureAtTS100 = cache.acquireAsync("TestKey", CacheCausalConsistency::kLatestKnown);
840+
ASSERT(!futureAtTS100.isReady());
841+
842+
ASSERT(cache.advanceTimeInStore("TestKey", Timestamp(200)));
843+
auto futureAtTS200 = cache.acquireAsync("TestKey", CacheCausalConsistency::kLatestKnown);
844+
ASSERT(!futureAtTS100.isReady());
845+
ASSERT(!futureAtTS200.isReady());
846+
847+
// Serve the first request - and verify that the received result only unblocks one future (with
848+
// an already stale value).
849+
nextToReturn.emplace(CachedValue(100), Timestamp(100));
850+
valueFetchedByPreviousRequest = boost::none;
851+
threadPool.runMostRecentTask();
852+
ASSERT_EQ(100, futureAtTS100.get()->counter);
853+
ASSERT(!futureAtTS100.get().isValid());
854+
ASSERT(!futureAtTS200.isReady());
855+
856+
// Serve the second request - and verify that the cache's lookupFn receives the value retrieved
857+
// by the first one to compute an "incremental remote lookup".
858+
nextToReturn.emplace(CachedValue(200), Timestamp(200));
859+
valueFetchedByPreviousRequest.emplace(100);
860+
threadPool.runMostRecentTask();
861+
ASSERT_EQ(200, futureAtTS200.get()->counter);
862+
ASSERT(futureAtTS200.get().isValid());
863+
}
864+
806865
} // namespace
807866
} // namespace mongo

0 commit comments

Comments
 (0)