|
|
@@ -1559,6 +1559,55 @@ uint32_t MemoryStore::pressurePercent() const {
|
|
|
return static_cast<uint32_t>(std::min<uint64_t>(100, used * 100 / max));
|
|
|
}
|
|
|
|
|
|
+MemoryStore::MemoryStatsSnapshot MemoryStore::getMemoryStatsSnapshot() const {
|
|
|
+ MemoryStatsSnapshot s;
|
|
|
+ s.totalMemoryBytes = estimatedMemoryBytes_.load(std::memory_order_relaxed);
|
|
|
+ s.maxMemoryBytes = config_.maxMemoryBytes;
|
|
|
+ s.pressurePercent = pressurePercent();
|
|
|
+ s.pressureLevel = pressure();
|
|
|
+
|
|
|
+ // Snapshot last-eviction event under statsMutex_ — evictOneChunk writes
|
|
|
+ // these fields at the end of each chunk under the same mutex.
|
|
|
+ {
|
|
|
+ std::lock_guard<std::mutex> lock(statsMutex_);
|
|
|
+ s.lastEvictionTimestamp = stats_.lastEvictionTimestamp;
|
|
|
+ s.lastEvictionDocs = stats_.lastEvictionDocs;
|
|
|
+ s.lastEvictionBytesFreed = stats_.lastEvictionBytesFreed;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Per-collection: documents + estimated size live under the coll's own
|
|
|
+ // mutex; evicted-stub counts live in the store-level evictedDocs_ map.
|
|
|
+ // Grab a shared lock on each — callers accept the O(docs) scan cost.
|
|
|
+ std::unordered_map<std::string, uint64_t> evictedCounts;
|
|
|
+ {
|
|
|
+ std::shared_lock<std::shared_mutex> elock(evictionMutex_);
|
|
|
+ for (const auto& [collName, stubs] : evictedDocs_) {
|
|
|
+ evictedCounts[collName] = stubs.size();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ std::shared_lock<std::shared_mutex> glock(globalMutex_);
|
|
|
+ s.collections.reserve(collections_.size());
|
|
|
+ for (const auto& [name, collPtr] : collections_) {
|
|
|
+ CollectionStats cs;
|
|
|
+ cs.collection = name;
|
|
|
+ cs.priority = collPtr->options.memoryPriority;
|
|
|
+ {
|
|
|
+ std::shared_lock<std::shared_mutex> clock(collPtr->mutex);
|
|
|
+ cs.documentCount = collPtr->documents.size();
|
|
|
+ for (const auto& [id, doc] : collPtr->documents) {
|
|
|
+ cs.estimatedBytes += estimateDocumentSize(doc);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ auto eIt = evictedCounts.find(name);
|
|
|
+ if (eIt != evictedCounts.end()) {
|
|
|
+ cs.evictedStubCount = eIt->second;
|
|
|
+ }
|
|
|
+ s.collections.push_back(std::move(cs));
|
|
|
+ }
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
// ===== Version History =====
|
|
|
|
|
|
void MemoryStore::saveToHistory(CollectionData& coll, const Document& currentDoc) {
|
|
|
@@ -2627,6 +2676,16 @@ uint64_t MemoryStore::evictOneChunk() {
|
|
|
uint64_t actuallyFreed = (bytesFreedBefore > bytesFreedAfter)
|
|
|
? (bytesFreedBefore - bytesFreedAfter) : 0;
|
|
|
|
|
|
+ // v1.7.0 T9: record last-eviction event for observability (GetMemoryStats).
|
|
|
+ // Only update when we actually evicted something, so "last eviction" means
|
|
|
+ // "most recent real eviction" rather than "most recent attempt".
|
|
|
+ if (evictedCount > 0) {
|
|
|
+ std::lock_guard<std::mutex> statsLock(statsMutex_);
|
|
|
+ stats_.lastEvictionTimestamp = currentTimeMs();
|
|
|
+ stats_.lastEvictionDocs = evictedCount;
|
|
|
+ stats_.lastEvictionBytesFreed = actuallyFreed;
|
|
|
+ }
|
|
|
+
|
|
|
spdlog::info("Eviction chunk: {} docs evicted, {} bytes freed, pressure={}",
|
|
|
evictedCount, actuallyFreed, memoryPressureToString(pressure()));
|
|
|
|