Skip to content

Commit 654aaec

Browse files
feat: Add eviction based on rss memory
Signed-off-by: Stepan Bagritsevich <[email protected]>
1 parent 485e08a commit 654aaec

File tree

4 files changed

+301
-63
lines changed

4 files changed

+301
-63
lines changed

src/server/dfly_main.cc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,12 @@ Usage: dragonfly [FLAGS]
823823
// export MIMALLOC_VERBOSE=1 to see the options before the override.
824824
mi_option_enable(mi_option_show_errors);
825825
mi_option_set(mi_option_max_warnings, 0);
826+
826827
mi_option_enable(mi_option_purge_decommits);
828+
DCHECK(mi_option_get(mi_option_reset_decommits) == 1);
829+
830+
mi_option_set(mi_option_purge_delay, 0);
831+
DCHECK(!mi_option_get(mi_option_reset_delay));
827832

828833
fb2::SetDefaultStackResource(&fb2::std_malloc_resource, kFiberDefaultStackSize);
829834

src/server/engine_shard.cc

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ extern "C" {
1919
#include "server/search/doc_index.h"
2020
#include "server/server_state.h"
2121
#include "server/tiered_storage.h"
22+
#include "server/tiering/common.h"
2223
#include "server/transaction.h"
2324
#include "util/fibers/proactor_base.h"
2425

2526
using namespace std;
27+
using namespace ::dfly::tiering::literals;
2628

2729
ABSL_FLAG(float, mem_defrag_threshold, 0.7,
2830
"Minimum percentage of used memory relative to maxmemory cap before running "
@@ -65,6 +67,9 @@ ABSL_FLAG(double, eviction_memory_budget_threshold, 0.1,
6567
"Eviction starts when the free memory (including RSS memory) drops below "
6668
"eviction_memory_budget_threshold * max_memory_limit.");
6769

70+
ABSL_FLAG(uint64_t, force_decommit_threshold, 8_MB,
71+
"The threshold of memory to force decommit when memory is under pressure.");
72+
6873
ABSL_DECLARE_FLAG(uint32_t, max_eviction_per_heartbeat);
6974

7075
namespace dfly {
@@ -216,45 +221,6 @@ size_t CalculateHowManyBytesToEvictOnShard(size_t global_memory_limit, size_t gl
216221
return shard_budget < shard_memory_threshold ? (shard_memory_threshold - shard_budget) : 0;
217222
}
218223

219-
/* Calculates the number of bytes to evict based on memory and rss memory usage. */
220-
size_t CalculateEvictionBytes() {
221-
const size_t shards_count = shard_set->size();
222-
const double eviction_memory_budget_threshold = GetFlag(FLAGS_eviction_memory_budget_threshold);
223-
224-
const size_t shard_memory_budget_threshold =
225-
size_t(max_memory_limit * eviction_memory_budget_threshold) / shards_count;
226-
227-
const size_t global_used_memory = used_mem_current.load(memory_order_relaxed);
228-
229-
// Calculate how many bytes we need to evict on this shard
230-
size_t goal_bytes = CalculateHowManyBytesToEvictOnShard(max_memory_limit, global_used_memory,
231-
shard_memory_budget_threshold);
232-
233-
// TODO: Eviction due to rss usage is not working well as it causes eviction
234-
// of to many keys untill we finally see decrease in rss. We need to improve
235-
// this logic before we enable it.
236-
/*
237-
const double rss_oom_deny_ratio = ServerState::tlocal()->rss_oom_deny_ratio;
238-
// If rss_oom_deny_ratio is set, we should evict depending on rss memory too
239-
if (rss_oom_deny_ratio > 0.0) {
240-
const size_t max_rss_memory = size_t(rss_oom_deny_ratio * max_memory_limit);
241-
// We start eviction when we have less than eviction_memory_budget_threshold * 100% of free rss
242-
memory const size_t shard_rss_memory_budget_threshold =
243-
size_t(max_rss_memory * eviction_memory_budget_threshold) / shards_count;
244-
245-
// Calculate how much rss memory is used by all shards
246-
const size_t global_used_rss_memory = rss_mem_current.load(memory_order_relaxed);
247-
248-
// Try to evict more bytes if we are close to the rss memory limit
249-
goal_bytes = std::max(
250-
goal_bytes, CalculateHowManyBytesToEvictOnShard(max_rss_memory, global_used_rss_memory,
251-
shard_rss_memory_budget_threshold));
252-
}
253-
*/
254-
255-
return goal_bytes;
256-
}
257-
258224
} // namespace
259225

260226
__thread EngineShard* EngineShard::shard_ = nullptr;
@@ -359,6 +325,18 @@ bool EngineShard::DefragTaskState::CheckRequired() {
359325
return false;
360326
}
361327

328+
std::optional<ShardMemUsage> shard_mem_usage;
329+
330+
if (GetFlag(FLAGS_enable_heartbeat_eviction)) {
331+
shard_mem_usage = ReadShardMemUsage(GetFlag(FLAGS_mem_defrag_page_utilization_threshold));
332+
const static double eviction_waste_threshold = 0.05;
333+
if (shard_mem_usage->wasted_mem >
334+
(uint64_t(shard_mem_usage->commited * eviction_waste_threshold))) {
335+
VLOG(1) << "memory issue found for memory " << shard_mem_usage.value();
336+
return true;
337+
}
338+
}
339+
362340
const std::size_t global_threshold = max_memory_limit * GetFlag(FLAGS_mem_defrag_threshold);
363341
if (global_threshold > rss_mem_current.load(memory_order_relaxed)) {
364342
return false;
@@ -373,11 +351,15 @@ bool EngineShard::DefragTaskState::CheckRequired() {
373351
}
374352
last_check_time = now;
375353

376-
ShardMemUsage usage = ReadShardMemUsage(GetFlag(FLAGS_mem_defrag_page_utilization_threshold));
354+
if (!shard_mem_usage) {
355+
shard_mem_usage = ReadShardMemUsage(GetFlag(FLAGS_mem_defrag_page_utilization_threshold));
356+
}
357+
358+
DCHECK(shard_mem_usage.has_value());
377359

378360
const double waste_threshold = GetFlag(FLAGS_mem_defrag_waste_threshold);
379-
if (usage.wasted_mem > (uint64_t(usage.commited * waste_threshold))) {
380-
VLOG(1) << "memory issue found for memory " << usage;
361+
if (shard_mem_usage->wasted_mem > (uint64_t(shard_mem_usage->commited * waste_threshold))) {
362+
VLOG(1) << "memory issue found for memory " << shard_mem_usage.value();
381363
return true;
382364
}
383365

@@ -812,6 +794,7 @@ void EngineShard::RetireExpiredAndEvict() {
812794
DbContext db_cntx;
813795
db_cntx.time_now_ms = GetCurrentTimeMs();
814796

797+
size_t deleted_bytes = 0;
815798
size_t eviction_goal = GetFlag(FLAGS_enable_heartbeat_eviction) ? CalculateEvictionBytes() : 0;
816799

817800
for (unsigned i = 0; i < db_slice.db_array_size(); ++i) {
@@ -823,6 +806,7 @@ void EngineShard::RetireExpiredAndEvict() {
823806
if (expt->size() > 0) {
824807
DbSlice::DeleteExpiredStats stats = db_slice.DeleteExpiredStep(db_cntx, ttl_delete_target);
825808

809+
deleted_bytes += stats.deleted_bytes;
826810
eviction_goal -= std::min(eviction_goal, size_t(stats.deleted_bytes));
827811
counter_[TTL_TRAVERSE].IncBy(stats.traversed);
828812
counter_[TTL_DELETE].IncBy(stats.deleted);
@@ -844,9 +828,71 @@ void EngineShard::RetireExpiredAndEvict() {
844828
<< " bytes. Max eviction per heartbeat: "
845829
<< GetFlag(FLAGS_max_eviction_per_heartbeat);
846830

831+
deleted_bytes += evicted_bytes;
847832
eviction_goal -= std::min(eviction_goal, evicted_bytes);
848833
}
849834
}
835+
836+
eviction_state_.deleted_bytes_before_rss_update += deleted_bytes;
837+
}
838+
839+
size_t EngineShard::CalculateEvictionBytes() {
840+
const size_t shards_count = shard_set->size();
841+
const double eviction_memory_budget_threshold = GetFlag(FLAGS_eviction_memory_budget_threshold);
842+
843+
const size_t shard_memory_budget_threshold =
844+
size_t(max_memory_limit * eviction_memory_budget_threshold) / shards_count;
845+
846+
const size_t global_used_memory = used_mem_current.load(memory_order_relaxed);
847+
848+
// Calculate how many bytes we need to evict on this shard
849+
size_t goal_bytes = CalculateHowManyBytesToEvictOnShard(max_memory_limit, global_used_memory,
850+
shard_memory_budget_threshold);
851+
852+
LOG_IF_EVERY_N(INFO, goal_bytes > 0, 50)
853+
<< "Memory goal bytes: " << goal_bytes << ", used memory: " << global_used_memory
854+
<< ", memory limit: " << max_memory_limit;
855+
856+
// If rss_oom_deny_ratio is set, we should evict depending on rss memory too
857+
const double rss_oom_deny_ratio = ServerState::tlocal()->rss_oom_deny_ratio;
858+
if (rss_oom_deny_ratio > 0.0) {
859+
const size_t max_rss_memory = size_t(rss_oom_deny_ratio * max_memory_limit);
860+
/* We start eviction when we have less than eviction_memory_budget_threshold * 100% of free rss
861+
* memory */
862+
const size_t shard_rss_memory_budget_threshold =
863+
size_t(max_rss_memory * eviction_memory_budget_threshold) / shards_count;
864+
865+
// Calculate how much rss memory is used by all shards
866+
const size_t global_used_rss_memory = rss_mem_current.load(memory_order_relaxed);
867+
868+
auto& global_rss_memory_at_prev_eviction = eviction_state_.global_rss_memory_at_prev_eviction;
869+
auto& deleted_bytes_before_rss_update = eviction_state_.deleted_bytes_before_rss_update;
870+
if (global_used_rss_memory < eviction_state_.global_rss_memory_at_prev_eviction) {
871+
deleted_bytes_before_rss_update -=
872+
std::min(deleted_bytes_before_rss_update,
873+
(global_rss_memory_at_prev_eviction - global_used_rss_memory) / shards_count);
874+
}
875+
876+
/* if (global_used_rss_memory == global_rss_memory_at_prev_eviction) {
877+
878+
} */
879+
880+
global_rss_memory_at_prev_eviction = global_used_rss_memory;
881+
882+
// Try to evict more bytes if we are close to the rss memory limit
883+
const size_t rss_goal_bytes = CalculateHowManyBytesToEvictOnShard(
884+
max_rss_memory, global_used_rss_memory - deleted_bytes_before_rss_update * shards_count,
885+
shard_rss_memory_budget_threshold);
886+
887+
LOG_IF_EVERY_N(INFO, rss_goal_bytes > 0, 50)
888+
<< "Rss memory goal bytes: " << rss_goal_bytes
889+
<< ", rss used memory: " << global_used_rss_memory
890+
<< ", rss memory limit: " << max_rss_memory
891+
<< ", deleted_bytes_before_rss_update: " << deleted_bytes_before_rss_update;
892+
893+
goal_bytes = std::max(goal_bytes, rss_goal_bytes);
894+
}
895+
return goal_bytes;
850896
}
851897

852898
void EngineShard::CacheStats() {

src/server/engine_shard.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ class EngineShard {
219219
void ResetScanState();
220220
};
221221

222+
struct EvictionTaskState {
223+
size_t deleted_bytes_before_rss_update = 0;
224+
size_t global_rss_memory_at_prev_eviction = 0;
225+
};
226+
222227
EngineShard(util::ProactorBase* pb, mi_heap_t* heap);
223228

224229
// blocks the calling fiber.
@@ -230,6 +235,9 @@ class EngineShard {
230235
void Heartbeat();
231236
void RetireExpiredAndEvict();
232237

238+
/* Calculates the number of bytes to evict based on memory and rss memory usage. */
239+
size_t CalculateEvictionBytes();
240+
233241
void CacheStats();
234242

235243
// We are running a task that checks whether we need to
@@ -270,6 +278,7 @@ class EngineShard {
270278
IntentLock shard_lock_;
271279

272280
uint32_t defrag_task_ = 0;
281+
EvictionTaskState eviction_state_; // Used on eviction fiber
273282
util::fb2::Fiber fiber_heartbeat_periodic_;
274283
util::fb2::Done fiber_heartbeat_periodic_done_;
275284

0 commit comments

Comments
 (0)