mirror of
https://github.com/XRPLF/rippled.git
synced 2026-07-31 02:50:24 +00:00
Combines four related changes:
1. "Decrease `shouldRelay` limit to 30s." Pretty self-explanatory. Currently, the limit is 5 minutes, by which point the `HashRouter` entry could have expired, making this transaction look brand new (and thus causing it to be relayed back to peers which have sent it to us recently).
2. "Give a transaction more chances to be retried." Will put a transaction into `LedgerMaster`'s held transactions if the transaction gets a `ter`, `tel`, or `tef` result. Old behavior was just `ter`.
* Additionally, to prevent a transaction from being repeatedly held indefinitely, it must meet some extra conditions. (Documented in a comment in the code.)
3. "Pop all transactions with sequential sequences, or tickets." When a transaction is processed successfully, currently, one held transaction for the same account (if any) will be popped out of the held transactions list, and queued up for the next transaction batch. This change pops all transactions for the account, but only if they have sequential sequences (for non-ticket transactions) or use a ticket. This issue was identified from interactions with @mtrippled's #4504, which was merged, but unfortunately reverted later by #4852. When the batches were spaced out, it could potentially take a very long time for a large number of held transactions for an account to get processed through. However, whether batched or not, this change will help get held transactions cleared out, particularly if a missing earlier transaction is what held them up.
4. "Process held transactions through existing NetworkOPs batching." In the current processing, at the end of each consensus round, all held transactions are directly applied to the open ledger, then the held list is reset. This bypasses all of the logic in `NetworkOPs::apply` which, among other things, broadcasts successful transactions to peers. This means that the transaction may not get broadcast to peers for a really long time (5 minutes in the current implementation, or 30 seconds with this first commit). If the node is a bottleneck (either due to network configuration, or because the transaction was submitted locally), the transaction may not be seen by any other nodes or validators before it expires or causes other problems.
252 lines
7.1 KiB
C++
252 lines
7.1 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#ifndef RIPPLE_APP_MISC_HASHROUTER_H_INCLUDED
|
|
#define RIPPLE_APP_MISC_HASHROUTER_H_INCLUDED
|
|
|
|
#include <xrpl/basics/CountedObject.h>
|
|
#include <xrpl/basics/UnorderedContainers.h>
|
|
#include <xrpl/basics/base_uint.h>
|
|
#include <xrpl/basics/chrono.h>
|
|
#include <xrpl/beast/container/aged_unordered_map.h>
|
|
|
|
#include <optional>
|
|
|
|
namespace ripple {
|
|
|
|
// TODO convert these macros to int constants or an enum
|
|
#define SF_BAD 0x02 // Temporarily bad
|
|
#define SF_SAVED 0x04
|
|
#define SF_HELD 0x08 // Held by LedgerMaster after potential processing failure
|
|
#define SF_TRUSTED 0x10 // comes from trusted source
|
|
|
|
// Private flags, used internally in apply.cpp.
|
|
// Do not attempt to read, set, or reuse.
|
|
#define SF_PRIVATE1 0x0100
|
|
#define SF_PRIVATE2 0x0200
|
|
#define SF_PRIVATE3 0x0400
|
|
#define SF_PRIVATE4 0x0800
|
|
#define SF_PRIVATE5 0x1000
|
|
#define SF_PRIVATE6 0x2000
|
|
|
|
class Config;
|
|
|
|
/** Routing table for objects identified by hash.
|
|
|
|
This table keeps track of which hashes have been received by which peers.
|
|
It is used to manage the routing and broadcasting of messages in the peer
|
|
to peer overlay.
|
|
*/
|
|
class HashRouter
|
|
{
|
|
public:
|
|
// The type here *MUST* match the type of Peer::id_t
|
|
using PeerShortID = std::uint32_t;
|
|
|
|
/** Structure used to customize @ref HashRouter behavior.
|
|
*
|
|
* Even though these items are configurable, they are undocumented. Don't
|
|
* change them unless there is a good reason, and network-wide coordination
|
|
* to do it.
|
|
*
|
|
* Configuration is processed in setup_HashRouter.
|
|
*/
|
|
struct Setup
|
|
{
|
|
/// Default constructor
|
|
explicit Setup() = default;
|
|
|
|
using seconds = std::chrono::seconds;
|
|
|
|
/** Expiration time for a hash entry
|
|
*/
|
|
seconds holdTime{300};
|
|
|
|
/** Amount of time required before a relayed item will be relayed again.
|
|
*/
|
|
seconds relayTime{30};
|
|
};
|
|
|
|
private:
|
|
/** An entry in the routing table.
|
|
*/
|
|
class Entry : public CountedObject<Entry>
|
|
{
|
|
public:
|
|
Entry()
|
|
{
|
|
}
|
|
|
|
void
|
|
addPeer(PeerShortID peer)
|
|
{
|
|
if (peer != 0)
|
|
peers_.insert(peer);
|
|
}
|
|
|
|
int
|
|
getFlags(void) const
|
|
{
|
|
return flags_;
|
|
}
|
|
|
|
void
|
|
setFlags(int flagsToSet)
|
|
{
|
|
flags_ |= flagsToSet;
|
|
}
|
|
|
|
/** Return set of peers we've relayed to and reset tracking */
|
|
std::set<PeerShortID>
|
|
releasePeerSet()
|
|
{
|
|
return std::move(peers_);
|
|
}
|
|
|
|
/** Return seated relay time point if the message has been relayed */
|
|
std::optional<Stopwatch::time_point>
|
|
relayed() const
|
|
{
|
|
return relayed_;
|
|
}
|
|
|
|
/** Determines if this item should be relayed.
|
|
|
|
Checks whether the item has been recently relayed.
|
|
If it has, return false. If it has not, update the
|
|
last relay timestamp and return true.
|
|
*/
|
|
bool
|
|
shouldRelay(
|
|
Stopwatch::time_point const& now,
|
|
std::chrono::seconds relayTime)
|
|
{
|
|
if (relayed_ && *relayed_ + relayTime > now)
|
|
return false;
|
|
relayed_.emplace(now);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
shouldProcess(Stopwatch::time_point now, std::chrono::seconds interval)
|
|
{
|
|
if (processed_ && ((*processed_ + interval) > now))
|
|
return false;
|
|
processed_.emplace(now);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
int flags_ = 0;
|
|
std::set<PeerShortID> peers_;
|
|
// This could be generalized to a map, if more
|
|
// than one flag needs to expire independently.
|
|
std::optional<Stopwatch::time_point> relayed_;
|
|
std::optional<Stopwatch::time_point> processed_;
|
|
};
|
|
|
|
public:
|
|
HashRouter(Setup const& setup, Stopwatch& clock)
|
|
: setup_(setup), suppressionMap_(clock)
|
|
{
|
|
}
|
|
|
|
HashRouter&
|
|
operator=(HashRouter const&) = delete;
|
|
|
|
virtual ~HashRouter() = default;
|
|
|
|
// VFALCO TODO Replace "Supression" terminology with something more
|
|
// semantically meaningful.
|
|
void
|
|
addSuppression(uint256 const& key);
|
|
|
|
bool
|
|
addSuppressionPeer(uint256 const& key, PeerShortID peer);
|
|
|
|
/** Add a suppression peer and get message's relay status.
|
|
* Return pair:
|
|
* element 1: true if the peer is added.
|
|
* element 2: optional is seated to the relay time point or
|
|
* is unseated if has not relayed yet. */
|
|
std::pair<bool, std::optional<Stopwatch::time_point>>
|
|
addSuppressionPeerWithStatus(uint256 const& key, PeerShortID peer);
|
|
|
|
bool
|
|
addSuppressionPeer(uint256 const& key, PeerShortID peer, int& flags);
|
|
|
|
// Add a peer suppression and return whether the entry should be processed
|
|
bool
|
|
shouldProcess(
|
|
uint256 const& key,
|
|
PeerShortID peer,
|
|
int& flags,
|
|
std::chrono::seconds tx_interval);
|
|
|
|
/** Set the flags on a hash.
|
|
|
|
@return `true` if the flags were changed. `false` if unchanged.
|
|
*/
|
|
bool
|
|
setFlags(uint256 const& key, int flags);
|
|
|
|
int
|
|
getFlags(uint256 const& key);
|
|
|
|
/** Determines whether the hashed item should be relayed.
|
|
|
|
Effects:
|
|
|
|
If the item should be relayed, this function will not
|
|
return a seated optional again until the relay time has expired.
|
|
The internal set of peers will also be reset.
|
|
|
|
@return A `std::optional` set of peers which do not need to be
|
|
relayed to. If the result is unseated, the item should
|
|
_not_ be relayed.
|
|
*/
|
|
std::optional<std::set<PeerShortID>>
|
|
shouldRelay(uint256 const& key);
|
|
|
|
private:
|
|
// pair.second indicates whether the entry was created
|
|
std::pair<Entry&, bool>
|
|
emplace(uint256 const&);
|
|
|
|
std::mutex mutable mutex_;
|
|
|
|
// Configurable parameters
|
|
Setup const setup_;
|
|
|
|
// Stores all suppressed hashes and their expiration time
|
|
beast::aged_unordered_map<
|
|
uint256,
|
|
Entry,
|
|
Stopwatch::clock_type,
|
|
hardened_hash<strong_hash>>
|
|
suppressionMap_;
|
|
};
|
|
|
|
HashRouter::Setup
|
|
setup_HashRouter(Config const&);
|
|
|
|
} // namespace ripple
|
|
|
|
#endif
|