Files
rippled/include/xrpl/nodestore/DatabaseRotating.h
Pratik Mankawde 70ae3ff922 Merge branch 'pratik/otel-phase10-workload-validation' into pratik/otel-sync-diagnostics
Phase-10 brought in the upstream nodestore/peerfinder/consensus reorganisation
along with its own write-path telemetry, which collided with the sync-diagnostic
signals on this branch. Twelve files conflicted; every resolution keeps both
intents rather than picking a side.

The nodestore write timing was implemented twice, independently. Both sides
added getStoreDurationUs()/getFetchDurationUs() to Database and both timed the
backend call in each concrete store(). Keeping both would have added twice to
storeDurationUs_ per store while storeStats() still counted one, so the mean
write latency would have read double on every dashboard -- silently, since no
test on either side asserts an exact microsecond figure. Resolved to one
accumulator API: recordStoreDuration(), which takes a duration, clamps a
sub-microsecond sample to zero and uses a relaxed atomic add. Phase-10's
storeDurationStats() is gone and its two call sites now use the survivor, so
all three store paths -- both store() overrides and importInternal() -- add
exactly once.

SlotCensus and its pure virtual moved from src/xrpld/peerfinder/ to
include/xrpl/peerfinder/PeerfinderManager.h, following the Manager interface
upstream relocated. The xrpld header is now phase-10's makeConfig shim, and
Overlay.h, MetricMacros.cpp and the getSlotCensus() override chain point at the
new location. ConsensusSpanNames.h and peerfinder Slot.h/Config.h include paths
followed their headers into libxrpl the same way.

InboundLedger gained phase-10's AcquireStats counters next to this branch's
span activations in both the destructor abort path and done(); neither
displaces the other. nodestore_state keeps the constant-based name this branch
requires of it and phase-10's fuller description.

Upstream #7292 deleted src/test/nodestore/Database_test.cpp, which held this
branch's testDurationAccessors. Phase-10 restored the per-store half of that
coverage in DatabaseConfig_test, but nothing covered importInternal -- it writes
through storeBatch() and never through store(), so it is a third store path that
has to time itself. That half is ported to a GTest in
src/tests/libxrpl/nodestore/Database.cpp, keeping the exact zero-before and
accumulate-after assertions and the per-instance negative check.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-28 11:17:53 +01:00

93 lines
3.1 KiB
C++

#pragma once
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/nodestore/Backend.h>
#include <xrpl/nodestore/Database.h>
#include <xrpl/nodestore/Scheduler.h>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
namespace xrpl::node_store {
/* This class has two key-value store Backend objects for persisting SHAMap
* records. This facilitates online deletion of data. New backends are
* rotated in. Old ones are rotated out and deleted.
*/
class DatabaseRotating : public Database
{
public:
DatabaseRotating(
Scheduler& scheduler,
int readThreads,
Section const& config,
beast::Journal journal)
: Database(scheduler, readThreads, config, journal)
{
}
/**
* Rotates the backends.
*
* @param newBackend New writable backend
* @param f A function executed after the rotation outside of lock. The
* values passed to f will be the new backend database names _after_
* rotation.
*/
virtual void
rotate(
std::unique_ptr<node_store::Backend>&& newBackend,
std::function<void(std::string const& writableName, std::string const& archiveName)> const&
f) = 0;
/**
* Marks an online-delete rotation as in progress (or completed).
*
* While in flight, a read served by the archive backend is copied
* forward into the writable backend even for ordinary
* (duplicate == false) fetches: the archive is about to be deleted,
* and a node body canonicalized into caches during the rotation
* window would otherwise survive only in RAM once the archive is
* dropped.
*/
virtual void
setRotationInFlight(bool inFlight) = 0;
/**
* Whether an online-delete rotation is in progress right now.
*
* A rotation's extra writes only happen inside this window, so a panel
* reading the copy-forward total needs this to know when to expect it to
* move. Outside the window a flat total is correct, not a broken signal.
*
* @return true between the cache-freshen phase starting and rotate()
* completing.
*/
[[nodiscard]] virtual bool
isRotationInFlight() const = 0;
/**
* Nodes copied forward from the archive backend into the writable one
* during rotation windows, since this process started.
*
* These are writes an ordinary fetch would not have performed: the archive
* is about to be deleted, so a body it served has to be rewritten to
* survive. The count therefore scales with how much of the archive is read
* during a rotation, which is why it appears only on a populated,
* already-rotated online_delete database.
*
* Cumulative for the lifetime of the process, deliberately: the per-rotation
* tally that `rotate()` logs is reset on every swap, and a counter that goes
* backwards cannot be rated. A panel takes the rate of this instead.
*
* @return Monotonic count of copy-forward writes.
*/
[[nodiscard]] virtual std::uint64_t
copyForwardTotal() const = 0;
};
} // namespace xrpl::node_store