mirror of
https://github.com/XRPLF/rippled.git
synced 2026-08-21 14:20:56 +00:00
Two suspects from the 3.3.0 slowdown investigation had no signal. Both were already computing the numbers and throwing them away, so this exposes them rather than adding measurement. Per-sweep heap trim. The trim runs after every cache sweep, and its cost scales with resident heap, so it is the leading explanation for a node with a populated database syncing slower than a fresh one. The report already carried duration, fault deltas and reclaimed pages, but the whole measurement sat behind a debug-journal check, so an ordinary node measured nothing, and the call site discarded the result. The measurement now always runs and only the log line stays gated. Records trim duration, minor faults and reclaimed kilobytes. Measured cost of the always-on path is about six microseconds per sweep against a trim costing milliseconds, at a cadence of ten to a hundred and twenty seconds. Honest limit, stated in the runbook: the fault delta spans only the trim call, so it shows the trim itself faulting but not the faults that follow as caches refill. The duration is the signal to correlate against sweep-job queueing. Rotation writes. Rotation copies archive-served reads forward and re-stores nodes missing from both backends, both of which compete with sync I/O and only happen on a populated online_delete database. The copy-forward count existed but was reset by the rotation's own log line, so a metric reading it would drop to zero on every swap; a never-reset total sits beside it now. The re-store count was not measured at all. Rotation duration is deliberately not recorded: the health throttle sleeps at eight points inside the sequence and dominates exactly when the node is unhealthy, so the number would conflate work with waiting. Nothing added for the other two suspects. Get-object serving is already covered by the handler label, the lookup histogram and the deferred and saturation gauges; peer churn by the disconnect-reason counter. Also replaces nine per-file cspell ignores with one ignoreRegExpList entry for the telemetry macro names, and picks up the levelization baseline for the consensus span-name test. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
90 lines
3.7 KiB
C++
90 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include <xrpl/beast/utility/Journal.h>
|
|
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <string_view>
|
|
|
|
namespace xrpl {
|
|
|
|
// cSpell:ignore ptmalloc statm
|
|
// "statm" is the /proc/self/statm filename the RSS readings come from; it is a
|
|
// kernel path, not prose, so it cannot be respelled. Same directive as the two
|
|
// MallocTrim .cpp files.
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Allocator interaction note:
|
|
// - This facility invokes glibc's malloc_trim(0) on Linux/glibc to request that
|
|
// ptmalloc return free heap pages to the OS.
|
|
// - If an alternative allocator (e.g. jemalloc or tcmalloc) is linked or
|
|
// preloaded (LD_PRELOAD), calling glibc's malloc_trim typically has no effect
|
|
// on the *active* heap. The call is harmless but may not reclaim memory
|
|
// because those allocators manage their own arenas.
|
|
// - Only glibc sbrk/arena space is eligible for trimming; large mmap-backed
|
|
// allocations are usually returned to the OS on free regardless of trimming.
|
|
// - Call at known reclamation points (e.g., after cache sweeps / online delete)
|
|
// and consider rate limiting to avoid churn.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
struct MallocTrimReport
|
|
{
|
|
bool supported{false};
|
|
int trimResult{-1};
|
|
std::int64_t rssBeforeKB{-1};
|
|
std::int64_t rssAfterKB{-1};
|
|
std::chrono::microseconds durationUs{-1};
|
|
std::int64_t minfltDelta{-1};
|
|
std::int64_t majfltDelta{-1};
|
|
|
|
[[nodiscard]] std::int64_t
|
|
deltaKB() const noexcept
|
|
{
|
|
if (rssBeforeKB < 0 || rssAfterKB < 0)
|
|
return 0;
|
|
return rssAfterKB - rssBeforeKB;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* @brief Attempt to return freed memory to the operating system.
|
|
*
|
|
* On Linux with glibc malloc, this issues ::malloc_trim(0), which may release
|
|
* free space from ptmalloc arenas back to the kernel. On other platforms, or if
|
|
* a different allocator is in use, this function is a no-op and the report will
|
|
* indicate that trimming is unsupported or had no effect.
|
|
*
|
|
* @param tag Identifier for logging/debugging purposes.
|
|
* @param journal Journal for diagnostic logging.
|
|
* @return Report containing before/after metrics and the trim result.
|
|
*
|
|
* @note If an alternative allocator (jemalloc/tcmalloc) is linked or preloaded,
|
|
* calling glibc's malloc_trim may have no effect on the active heap. The
|
|
* call is harmless but typically does not reclaim memory under those
|
|
* allocators.
|
|
*
|
|
* @note Only memory served from glibc's sbrk/arena heaps is eligible for trim.
|
|
* Large allocations satisfied via mmap are usually returned on free
|
|
* independently of trimming.
|
|
*
|
|
* @note Intended for use after operations that free significant memory (e.g.,
|
|
* cache sweeps, ledger cleanup, online delete). Consider rate limiting.
|
|
*
|
|
* @note Every report field is populated on every call, whatever the journal's
|
|
* severity. Only the diagnostic JLOG is severity-gated. Measuring costs
|
|
* about 6 us (two /proc/self/statm reads and two getrusage calls) against
|
|
* a trim that costs milliseconds on a large heap, so callers on a cold
|
|
* path -- the cache sweep is the intended one -- can record the numbers
|
|
* unconditionally. A caller on a hot path should not use this function at
|
|
* all rather than expect the measurement to disappear.
|
|
*
|
|
* @note `minfltDelta` covers the trim call ONLY. It shows that the trim itself
|
|
* faults; it does NOT capture the faults later taken when the caches
|
|
* refill and touch the pages the trim returned. Do not read it as the
|
|
* total cost of trimming.
|
|
*/
|
|
MallocTrimReport
|
|
mallocTrim(std::string_view tag, beast::Journal journal);
|
|
|
|
} // namespace xrpl
|