diff --git a/src/replica/replica_stub.cpp b/src/replica/replica_stub.cpp index e9289f89b6..1e4c12d471 100644 --- a/src/replica/replica_stub.cpp +++ b/src/replica/replica_stub.cpp @@ -95,9 +95,11 @@ replica_stub::replica_stub(replica_state_subscriber subscriber /*= nullptr*/, _is_running(false) { #ifdef DSN_ENABLE_GPERF + _is_releasing_memory = false; _release_tcmalloc_memory_command = nullptr; _get_tcmalloc_status_command = nullptr; _max_reserved_memory_percentage_command = nullptr; + _release_all_reserved_memory_command = nullptr; #endif _replica_state_subscriber = subscriber; _is_long_subscriber = is_long_subscriber; @@ -832,7 +834,7 @@ void replica_stub::initialize_start() _mem_release_timer_task = tasking::enqueue_timer(LPC_MEM_RELEASE, &_tracker, - std::bind(&replica_stub::gc_tcmalloc_memory, this), + std::bind(&replica_stub::gc_tcmalloc_memory, this, false), std::chrono::milliseconds(_options.mem_release_check_interval_ms), 0, std::chrono::milliseconds(_options.mem_release_check_interval_ms)); @@ -2311,8 +2313,8 @@ void replica_stub::register_ctrl_command() _get_tcmalloc_status_command = ::dsn::command_manager::instance().register_command( {"replica.get-tcmalloc-status"}, - "replica.get-tcmalloc-status", "replica.get-tcmalloc-status - get status of tcmalloc", + "get status of tcmalloc", [](const std::vector &args) { char buf[4096]; MallocExtension::instance()->GetStats(buf, 4096); @@ -2345,6 +2347,15 @@ void replica_stub::register_ctrl_command() } return result; }); + + _release_all_reserved_memory_command = ::dsn::command_manager::instance().register_command( + {"replica.release-all-reserved-memory"}, + "replica.release-all-reserved-memory - release tcmalloc all reserved-not-used memory", + "release tcmalloc all reserverd not-used memory back to operating system", + [this](const std::vector &args) { + auto release_bytes = gc_tcmalloc_memory(true); + return "OK, release_bytes=" + std::to_string(release_bytes); + }); #endif _max_concurrent_bulk_load_downloading_count_command = dsn::command_manager::instance().register_command( @@ -2506,6 +2517,7 @@ void replica_stub::close() UNREGISTER_VALID_HANDLER(_release_tcmalloc_memory_command); UNREGISTER_VALID_HANDLER(_get_tcmalloc_status_command); UNREGISTER_VALID_HANDLER(_max_reserved_memory_percentage_command); + UNREGISTER_VALID_HANDLER(_release_all_reserved_memory_command); #endif UNREGISTER_VALID_HANDLER(_max_concurrent_bulk_load_downloading_count_command); @@ -2520,6 +2532,7 @@ void replica_stub::close() _release_tcmalloc_memory_command = nullptr; _get_tcmalloc_status_command = nullptr; _max_reserved_memory_percentage_command = nullptr; + _release_all_reserved_memory_command = nullptr; #endif _max_concurrent_bulk_load_downloading_count_command = nullptr; @@ -2648,23 +2661,31 @@ static int64_t get_tcmalloc_numeric_property(const char *prop) return value; } -void replica_stub::gc_tcmalloc_memory() +uint64_t replica_stub::gc_tcmalloc_memory(bool release_all) { - int64_t tcmalloc_released_bytes = 0; + auto tcmalloc_released_bytes = 0; if (!_release_tcmalloc_memory) { + _is_releasing_memory.store(false); _counter_tcmalloc_release_memory_size->set(tcmalloc_released_bytes); - return; + return tcmalloc_released_bytes; } + if (_is_releasing_memory.load()) { + dwarn_f("This node is releasing memory..."); + return tcmalloc_released_bytes; + } + + _is_releasing_memory.store(true); int64_t total_allocated_bytes = get_tcmalloc_numeric_property("generic.current_allocated_bytes"); int64_t reserved_bytes = get_tcmalloc_numeric_property("tcmalloc.pageheap_free_bytes"); if (total_allocated_bytes == -1 || reserved_bytes == -1) { - return; + return tcmalloc_released_bytes; } int64_t max_reserved_bytes = - total_allocated_bytes * _mem_release_max_reserved_mem_percentage / 100.0; + release_all ? 0 + : (total_allocated_bytes * _mem_release_max_reserved_mem_percentage / 100.0); if (reserved_bytes > max_reserved_bytes) { int64_t release_bytes = reserved_bytes - max_reserved_bytes; tcmalloc_released_bytes = release_bytes; @@ -2677,6 +2698,8 @@ void replica_stub::gc_tcmalloc_memory() } } _counter_tcmalloc_release_memory_size->set(tcmalloc_released_bytes); + _is_releasing_memory.store(false); + return tcmalloc_released_bytes; } #endif diff --git a/src/replica/replica_stub.h b/src/replica/replica_stub.h index 5960348836..96a6b09313 100644 --- a/src/replica/replica_stub.h +++ b/src/replica/replica_stub.h @@ -299,7 +299,8 @@ class replica_stub : public serverlet, public ref_counter #ifdef DSN_ENABLE_GPERF // Try to release tcmalloc memory back to operating system - void gc_tcmalloc_memory(); + // If release_all = true, it will release all reserved-not-used memory + uint64_t gc_tcmalloc_memory(bool release_all); #endif private: @@ -370,6 +371,7 @@ class replica_stub : public serverlet, public ref_counter dsn_handle_t _release_tcmalloc_memory_command; dsn_handle_t _get_tcmalloc_status_command; dsn_handle_t _max_reserved_memory_percentage_command; + dsn_handle_t _release_all_reserved_memory_command; #endif dsn_handle_t _max_concurrent_bulk_load_downloading_count_command; @@ -402,6 +404,10 @@ class replica_stub : public serverlet, public ref_counter bool _is_running; +#ifdef DSN_ENABLE_GPERF + std::atomic_bool _is_releasing_memory{false}; +#endif + // performance counters perf_counter_wrapper _counter_replicas_count; perf_counter_wrapper _counter_replicas_opening_count;