|
|
@@ -314,11 +314,31 @@ std::string MemoryStore::insert(const std::string& collection, Document doc) {
|
|
|
std::optional<Document> MemoryStore::get(const std::string& collection, const std::string& id) const {
|
|
|
auto startTime = std::chrono::steady_clock::now();
|
|
|
|
|
|
+ // v1.9.2 — under hard or emergency memory pressure, evicted-doc reads
|
|
|
+ // go through the read-only `peekEvictedDocument` path. Pre-1.9.2 we
|
|
|
+ // always used `loadEvictedDocument`, which hot-loads the doc back
|
|
|
+ // into `coll->documents`. That's a warm-cache optimization that
|
|
|
+ // helps when there's headroom but actively hurts under pressure:
|
|
|
+ // the hot-loaded doc bumps `estimatedMemoryBytes_` over the cap,
|
|
|
+ // eviction immediately evicts it again, the next read hot-loads
|
|
|
+ // again — a thrash cycle that maps onto the replica-eviction
|
|
|
+ // test's 4002/5000 read-deadline misses. The peek path doesn't
|
|
|
+ // touch memory state, so concurrent reads under pressure don't
|
|
|
+ // contend with the eviction loop.
|
|
|
+ auto evictedReadFn = [this](const std::string& c, const std::string& i)
|
|
|
+ -> std::optional<Document> {
|
|
|
+ const MemoryPressure p = pressure();
|
|
|
+ if (p == MemoryPressure::Hard || p == MemoryPressure::Emergency) {
|
|
|
+ return peekEvictedDocument(c, i);
|
|
|
+ }
|
|
|
+ return const_cast<MemoryStore*>(this)->loadEvictedDocument(c, i);
|
|
|
+ };
|
|
|
+
|
|
|
const CollectionData* coll = getCollection(collection);
|
|
|
if (!coll) {
|
|
|
// Check if document was evicted (collection may not exist in memory)
|
|
|
if (const_cast<MemoryStore*>(this)->isDocumentEvicted(collection, id)) {
|
|
|
- auto result = const_cast<MemoryStore*>(this)->loadEvictedDocument(collection, id);
|
|
|
+ auto result = evictedReadFn(collection, id);
|
|
|
// Record timing
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
|
std::chrono::steady_clock::now() - startTime).count();
|
|
|
@@ -337,7 +357,7 @@ std::optional<Document> MemoryStore::get(const std::string& collection, const st
|
|
|
lock.unlock();
|
|
|
// Check if document was evicted
|
|
|
if (const_cast<MemoryStore*>(this)->isDocumentEvicted(collection, id)) {
|
|
|
- auto result = const_cast<MemoryStore*>(this)->loadEvictedDocument(collection, id);
|
|
|
+ auto result = evictedReadFn(collection, id);
|
|
|
// Record timing
|
|
|
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
|
std::chrono::steady_clock::now() - startTime).count();
|