Skip to content

title: transaction/TxPruner.hpp summary: PRUNE-04 real prune-cycle service: the first production eraser.


transaction/TxPruner.hpp

PRUNE-04 real prune-cycle service: the first production eraser. More...

Namespaces

Name
sgns
sgns::crdt

Classes

Name
struct sgns::PruneCycleStats
Counters of ONE prune cycle (RECL-03; counts only).
struct sgns::PruneStatus
Queryable pruning status (RECL-04).
class sgns::TxPruner
The real prune-cycle service (PRUNE-04).

Detailed Description

PRUNE-04 real prune-cycle service: the first production eraser.

Date: 2026-10-21

Owns the idempotent, crash-consistent erase CYCLE and nothing else: tick gate (Light node, TransactionManager READY, empty missing set, settle window elapsed — D-C2), a per-cycle PruneEligibility::Evaluate() reused VERBATIM (the window arithmetic's one home — never re-implemented here), a block-grouped K-bounded erase budget taken whole or deferred whole (D-04 / Pitfall 1 — same-block siblings are never stranded behind an exhausted K), a FRESH per-outpoint spent-ness re-verify immediately before every erase (D-01), the atomic per-tx erase driver TransactionManager::EraseTransactionEntryPruned (value XOR marker in one commit, 18-03), and the per-cycle observability contract (RECL-03 log + RECL-04 queryable PruneStatus).

Deliberately NOT a GeniusNode member or lambda (the god-class drift the milestone research forbids): the service depends on the transaction/crdt/account-service CONTRACTS only, and every CRDT/service dependency is passed PER CYCLE (D-C4) — the instance holds only config, the node role, the mutex-guarded PruneStatus, and the settle clock, so it is generation-safe to keep alive across Phase-13 account switches. There is no persistent cursor and no cached evaluator: erased transactions vanish from enumeration, so the next evaluation simply stops proposing them.

Source code

#ifndef _SGNS_TX_PRUNER_HPP_
#define _SGNS_TX_PRUNER_HPP_

#include <chrono>
#include <cstdint>
#include <functional>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <system_error>

#include "account/NodeType.hpp"
#include "base/logger.hpp"
#include "outcome/outcome.hpp"
#include "transaction/PruneEligibility.hpp"

namespace sgns
{
    namespace crdt
    {
        class GlobalDB;
    }

    class TransactionManager;
    class UTXOManager;

    struct PruneCycleStats
    {
        uint64_t examined = 0;

        uint64_t eligible = 0;

        uint64_t erased = 0;

        uint64_t held_blocks = 0;

        uint64_t skipped = 0;

        uint64_t errors = 0;

        uint64_t duration_ms = 0;

        uint64_t generation = 0;

        bool generation_superseded = false;
    };

    struct PruneStatus
    {
        std::optional<uint64_t> watermark;

        PruneCycleStats last_cycle;
    };

    class TxPruner
    {
    public:
        static outcome::result<std::shared_ptr<TxPruner>> New( NodeType node_type, const PruningConfig &config );

        outcome::result<PruneCycleStats> RunCycle( crdt::GlobalDB     &globaldb,
                                                   TransactionManager &transaction_manager,
                                                   UTXOManager        &utxo_manager,
                                                   uint64_t            generation );

        PruneStatus GetPruneStatus() const;

        static bool IsPermanentStop( const std::error_code &error );

        static constexpr int kInterruptAfterEvaluation = 1;
        static constexpr int kInterruptBeforeErase = 2;
        static constexpr int kInterruptBeforeStatus = 3;
        static constexpr int kInterruptAfterStatus = 4;

        static void SetCycleInterruptHookForTest( std::function<bool( int step )> hook );

    private:
        TxPruner( NodeType node_type, PruningConfig config );

        void RecordCompletedCycle( crdt::GlobalDB                       &globaldb,
                                   PruneCycleStats                      &stats,
                                   std::chrono::steady_clock::time_point cycle_start );

        bool TickGateSettled( TransactionManager &transaction_manager, std::chrono::steady_clock::time_point now );

        bool OutputsRemainGenuinelyConsumed( crdt::GlobalDB             &globaldb,
                                             UTXOManager                &utxo_manager,
                                             const PruneCandidateResult &candidate ) const;

        static bool InterruptRequested( int step );

        PruningConfig config_m;

        NodeType node_type_m;

        mutable std::mutex status_mutex_;

        PruneStatus status_m;

        std::optional<std::chrono::steady_clock::time_point> gate_held_since_;

        static std::function<bool( int step )> cycle_interrupt_hook_for_test_;

        base::Logger logger_ = base::createLogger( "TxPruner" );
    };
} // namespace sgns

#endif

Updated on 2026-10-22 at 19:28:25 +0000