mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-19 18:45:52 +00:00
Merge branch 'develop' into ximinez/fixed-ledger-entries
This commit is contained in:
36
.github/actions/dependencies/action.yml
vendored
36
.github/actions/dependencies/action.yml
vendored
@@ -2,33 +2,25 @@ name: dependencies
|
|||||||
inputs:
|
inputs:
|
||||||
configuration:
|
configuration:
|
||||||
required: true
|
required: true
|
||||||
# An implicit input is the environment variable `build_dir`.
|
# Implicit inputs are the environment variables `build_dir`, CONAN_REMOTE_URL,
|
||||||
|
# CONAN_REMOTE_USERNAME, and CONAN_REMOTE_PASSWORD. The latter two are only
|
||||||
|
# used to upload newly built dependencies to the Conan remote.
|
||||||
runs:
|
runs:
|
||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- name: add Conan remote
|
- name: add Conan remote
|
||||||
if: env.CONAN_URL != ''
|
if: ${{ env.CONAN_REMOTE_URL != '' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
if conan remote list | grep -q 'xrplf'; then
|
echo "Adding Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}."
|
||||||
conan remote update --index 0 --url ${CONAN_URL} xrplf
|
conan remote add --index 0 --force xrplf ${{ env.CONAN_REMOTE_URL }}
|
||||||
echo "Updated Conan remote 'xrplf' to ${CONAN_URL}."
|
echo "Listing Conan remotes."
|
||||||
else
|
conan remote list
|
||||||
conan remote add --index 0 xrplf ${CONAN_URL}
|
|
||||||
echo "Added new conan remote 'xrplf' at ${CONAN_URL}."
|
|
||||||
fi
|
|
||||||
- name: list missing binaries
|
|
||||||
id: binaries
|
|
||||||
shell: bash
|
|
||||||
# Print the list of dependencies that would need to be built locally.
|
|
||||||
# A non-empty list means we have "failed" to cache binaries remotely.
|
|
||||||
run: |
|
|
||||||
echo missing=$(conan info . --build missing --settings build_type=${{ inputs.configuration }} --json 2>/dev/null | grep '^\[') | tee ${GITHUB_OUTPUT}
|
|
||||||
- name: install dependencies
|
- name: install dependencies
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir ${build_dir}
|
mkdir -p ${{ env.build_dir }}
|
||||||
cd ${build_dir}
|
cd ${{ env.build_dir }}
|
||||||
conan install \
|
conan install \
|
||||||
--output-folder . \
|
--output-folder . \
|
||||||
--build missing \
|
--build missing \
|
||||||
@@ -36,3 +28,11 @@ runs:
|
|||||||
--options:host "&:xrpld=True" \
|
--options:host "&:xrpld=True" \
|
||||||
--settings:all build_type=${{ inputs.configuration }} \
|
--settings:all build_type=${{ inputs.configuration }} \
|
||||||
..
|
..
|
||||||
|
- name: upload dependencies
|
||||||
|
if: ${{ env.CONAN_REMOTE_URL != '' && env.CONAN_REMOTE_USERNAME != '' && env.CONAN_REMOTE_PASSWORD != '' && github.ref_type == 'branch' && github.ref_name == github.event.repository.default_branch }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "Logging into Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}."
|
||||||
|
conan remote login xrplf "${{ env.CONAN_REMOTE_USERNAME }}" --password "${{ env.CONAN_REMOTE_PASSWORD }}"
|
||||||
|
echo "Uploading dependencies."
|
||||||
|
conan upload '*' --confirm --check --remote xrplf
|
||||||
|
|||||||
12
.github/workflows/libxrpl.yml
vendored
12
.github/workflows/libxrpl.yml
vendored
@@ -1,8 +1,8 @@
|
|||||||
name: Check libXRPL compatibility with Clio
|
name: Check libXRPL compatibility with Clio
|
||||||
env:
|
env:
|
||||||
CONAN_URL: https://conan.ripplex.io
|
CONAN_REMOTE_URL: ${{ vars.CONAN_REMOTE_URL }}
|
||||||
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.CONAN_USERNAME }}
|
CONAN_LOGIN_USERNAME_XRPLF: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||||
CONAN_PASSWORD_XRPLF: ${{ secrets.CONAN_TOKEN }}
|
CONAN_PASSWORD_XRPLF: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
@@ -46,10 +46,10 @@ jobs:
|
|||||||
- name: Add Conan remote
|
- name: Add Conan remote
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
echo "Adding Conan remote 'xrplf' at ${{ env.CONAN_REMOTE_URL }}."
|
||||||
|
conan remote add xrplf ${{ env.CONAN_REMOTE_URL }} --insert 0 --force
|
||||||
|
echo "Listing Conan remotes."
|
||||||
conan remote list
|
conan remote list
|
||||||
conan remote remove xrplf || true
|
|
||||||
# Do not quote the URL. An empty string will be accepted (with a non-fatal warning), but a missing argument will not.
|
|
||||||
conan remote add xrplf ${{ env.CONAN_URL }} --insert 0
|
|
||||||
- name: Parse new version
|
- name: Parse new version
|
||||||
id: version
|
id: version
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
7
.github/workflows/macos.yml
vendored
7
.github/workflows/macos.yml
vendored
@@ -18,7 +18,12 @@ concurrency:
|
|||||||
# This part of Conan configuration is specific to this workflow only; we do not want
|
# This part of Conan configuration is specific to this workflow only; we do not want
|
||||||
# to pollute conan/profiles directory with settings which might not work for others
|
# to pollute conan/profiles directory with settings which might not work for others
|
||||||
env:
|
env:
|
||||||
CONAN_URL: https://conan.ripplex.io
|
CONAN_REMOTE_URL: ${{ vars.CONAN_REMOTE_URL }}
|
||||||
|
CONAN_REMOTE_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||||
|
CONAN_REMOTE_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||||
|
# This part of the Conan configuration is specific to this workflow only; we
|
||||||
|
# do not want to pollute the 'conan/profiles' directory with settings that
|
||||||
|
# might not work for other workflows.
|
||||||
CONAN_GLOBAL_CONF: |
|
CONAN_GLOBAL_CONF: |
|
||||||
core.download:parallel={{os.cpu_count()}}
|
core.download:parallel={{os.cpu_count()}}
|
||||||
core.upload:parallel={{os.cpu_count()}}
|
core.upload:parallel={{os.cpu_count()}}
|
||||||
|
|||||||
9
.github/workflows/nix.yml
vendored
9
.github/workflows/nix.yml
vendored
@@ -16,10 +16,13 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
# This part of Conan configuration is specific to this workflow only; we do not want
|
|
||||||
# to pollute conan/profiles directory with settings which might not work for others
|
|
||||||
env:
|
env:
|
||||||
CONAN_URL: https://conan.ripplex.io
|
CONAN_REMOTE_URL: ${{ vars.CONAN_REMOTE_URL }}
|
||||||
|
CONAN_REMOTE_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||||
|
CONAN_REMOTE_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||||
|
# This part of the Conan configuration is specific to this workflow only; we
|
||||||
|
# do not want to pollute the 'conan/profiles' directory with settings that
|
||||||
|
# might not work for other workflows.
|
||||||
CONAN_GLOBAL_CONF: |
|
CONAN_GLOBAL_CONF: |
|
||||||
core.download:parallel={{ os.cpu_count() }}
|
core.download:parallel={{ os.cpu_count() }}
|
||||||
core.upload:parallel={{ os.cpu_count() }}
|
core.upload:parallel={{ os.cpu_count() }}
|
||||||
|
|||||||
9
.github/workflows/windows.yml
vendored
9
.github/workflows/windows.yml
vendored
@@ -18,10 +18,13 @@ on:
|
|||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
# This part of Conan configuration is specific to this workflow only; we do not want
|
|
||||||
# to pollute conan/profiles directory with settings which might not work for others
|
|
||||||
env:
|
env:
|
||||||
CONAN_URL: https://conan.ripplex.io
|
CONAN_REMOTE_URL: ${{ vars.CONAN_REMOTE_URL }}
|
||||||
|
CONAN_REMOTE_USERNAME: ${{ secrets.CONAN_REMOTE_USERNAME }}
|
||||||
|
CONAN_REMOTE_PASSWORD: ${{ secrets.CONAN_REMOTE_PASSWORD }}
|
||||||
|
# This part of the Conan configuration is specific to this workflow only; we
|
||||||
|
# do not want to pollute the 'conan/profiles' directory with settings that
|
||||||
|
# might not work for other workflows.
|
||||||
CONAN_GLOBAL_CONF: |
|
CONAN_GLOBAL_CONF: |
|
||||||
core.download:parallel={{os.cpu_count()}}
|
core.download:parallel={{os.cpu_count()}}
|
||||||
core.upload:parallel={{os.cpu_count()}}
|
core.upload:parallel={{os.cpu_count()}}
|
||||||
|
|||||||
@@ -21,7 +21,6 @@
|
|||||||
#define RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED
|
#define RIPPLE_BASICS_SHAMAP_HASH_H_INCLUDED
|
||||||
|
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
|
||||||
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
|
|||||||
@@ -90,9 +90,6 @@ public:
|
|||||||
int
|
int
|
||||||
getCacheSize() const;
|
getCacheSize() const;
|
||||||
|
|
||||||
int
|
|
||||||
getTrackSize() const;
|
|
||||||
|
|
||||||
float
|
float
|
||||||
getHitRate();
|
getHitRate();
|
||||||
|
|
||||||
@@ -170,9 +167,6 @@ public:
|
|||||||
bool
|
bool
|
||||||
retrieve(key_type const& key, T& data);
|
retrieve(key_type const& key, T& data);
|
||||||
|
|
||||||
mutex_type&
|
|
||||||
peekMutex();
|
|
||||||
|
|
||||||
std::vector<key_type>
|
std::vector<key_type>
|
||||||
getKeys() const;
|
getKeys() const;
|
||||||
|
|
||||||
@@ -193,11 +187,14 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
SharedPointerType
|
SharedPointerType
|
||||||
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l);
|
initialFetch(key_type const& key);
|
||||||
|
|
||||||
void
|
void
|
||||||
collect_metrics();
|
collect_metrics();
|
||||||
|
|
||||||
|
Mutex&
|
||||||
|
lockPartition(key_type const& key) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Stats
|
struct Stats
|
||||||
{
|
{
|
||||||
@@ -300,8 +297,8 @@ private:
|
|||||||
[[maybe_unused]] clock_type::time_point const& now,
|
[[maybe_unused]] clock_type::time_point const& now,
|
||||||
typename KeyValueCacheType::map_type& partition,
|
typename KeyValueCacheType::map_type& partition,
|
||||||
SweptPointersVector& stuffToSweep,
|
SweptPointersVector& stuffToSweep,
|
||||||
std::atomic<int>& allRemovals,
|
std::atomic<int>& allRemoval,
|
||||||
std::lock_guard<std::recursive_mutex> const&);
|
Mutex& partitionLock);
|
||||||
|
|
||||||
[[nodiscard]] std::thread
|
[[nodiscard]] std::thread
|
||||||
sweepHelper(
|
sweepHelper(
|
||||||
@@ -310,14 +307,12 @@ private:
|
|||||||
typename KeyOnlyCacheType::map_type& partition,
|
typename KeyOnlyCacheType::map_type& partition,
|
||||||
SweptPointersVector&,
|
SweptPointersVector&,
|
||||||
std::atomic<int>& allRemovals,
|
std::atomic<int>& allRemovals,
|
||||||
std::lock_guard<std::recursive_mutex> const&);
|
Mutex& partitionLock);
|
||||||
|
|
||||||
beast::Journal m_journal;
|
beast::Journal m_journal;
|
||||||
clock_type& m_clock;
|
clock_type& m_clock;
|
||||||
Stats m_stats;
|
Stats m_stats;
|
||||||
|
|
||||||
mutex_type mutable m_mutex;
|
|
||||||
|
|
||||||
// Used for logging
|
// Used for logging
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
|
||||||
@@ -328,10 +323,11 @@ private:
|
|||||||
clock_type::duration const m_target_age;
|
clock_type::duration const m_target_age;
|
||||||
|
|
||||||
// Number of items cached
|
// Number of items cached
|
||||||
int m_cache_count;
|
std::atomic<int> m_cache_count;
|
||||||
cache_type m_cache; // Hold strong reference to recent objects
|
cache_type m_cache; // Hold strong reference to recent objects
|
||||||
std::uint64_t m_hits;
|
std::atomic<std::uint64_t> m_hits;
|
||||||
std::uint64_t m_misses;
|
std::atomic<std::uint64_t> m_misses;
|
||||||
|
mutable std::vector<mutex_type> partitionLocks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||||
#include <xrpl/basics/TaggedCache.h>
|
#include <xrpl/basics/TaggedCache.h>
|
||||||
|
#include <xrpl/beast/core/CurrentThreadName.h>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ inline TaggedCache<
|
|||||||
, m_hits(0)
|
, m_hits(0)
|
||||||
, m_misses(0)
|
, m_misses(0)
|
||||||
{
|
{
|
||||||
|
partitionLocks_ = std::vector<mutex_type>(m_cache.partitions());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -105,8 +107,13 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::size() const
|
Mutex>::size() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
std::size_t totalSize = 0;
|
||||||
return m_cache.size();
|
for (size_t i = 0; i < partitionLocks_.size(); ++i)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(partitionLocks_[i]);
|
||||||
|
totalSize += m_cache.map()[i].size();
|
||||||
|
}
|
||||||
|
return totalSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -129,32 +136,7 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::getCacheSize() const
|
Mutex>::getCacheSize() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
return m_cache_count.load(std::memory_order_relaxed);
|
||||||
return m_cache_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
|
||||||
class Key,
|
|
||||||
class T,
|
|
||||||
bool IsKeyCache,
|
|
||||||
class SharedWeakUnionPointer,
|
|
||||||
class SharedPointerType,
|
|
||||||
class Hash,
|
|
||||||
class KeyEqual,
|
|
||||||
class Mutex>
|
|
||||||
inline int
|
|
||||||
TaggedCache<
|
|
||||||
Key,
|
|
||||||
T,
|
|
||||||
IsKeyCache,
|
|
||||||
SharedWeakUnionPointer,
|
|
||||||
SharedPointerType,
|
|
||||||
Hash,
|
|
||||||
KeyEqual,
|
|
||||||
Mutex>::getTrackSize() const
|
|
||||||
{
|
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
return m_cache.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -177,9 +159,10 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::getHitRate()
|
Mutex>::getHitRate()
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
auto const hits = m_hits.load(std::memory_order_relaxed);
|
||||||
auto const total = static_cast<float>(m_hits + m_misses);
|
auto const misses = m_misses.load(std::memory_order_relaxed);
|
||||||
return m_hits * (100.0f / std::max(1.0f, total));
|
float const total = float(hits + misses);
|
||||||
|
return hits * (100.0f / std::max(1.0f, total));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -202,9 +185,12 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::clear()
|
Mutex>::clear()
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
for (auto& mutex : partitionLocks_)
|
||||||
|
mutex.lock();
|
||||||
m_cache.clear();
|
m_cache.clear();
|
||||||
m_cache_count = 0;
|
for (auto& mutex : partitionLocks_)
|
||||||
|
mutex.unlock();
|
||||||
|
m_cache_count.store(0, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -227,11 +213,9 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::reset()
|
Mutex>::reset()
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
clear();
|
||||||
m_cache.clear();
|
m_hits.store(0, std::memory_order_relaxed);
|
||||||
m_cache_count = 0;
|
m_misses.store(0, std::memory_order_relaxed);
|
||||||
m_hits = 0;
|
|
||||||
m_misses = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -255,7 +239,7 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::touch_if_exists(KeyComparable const& key)
|
Mutex>::touch_if_exists(KeyComparable const& key)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||||
auto const iter(m_cache.find(key));
|
auto const iter(m_cache.find(key));
|
||||||
if (iter == m_cache.end())
|
if (iter == m_cache.end())
|
||||||
{
|
{
|
||||||
@@ -297,8 +281,6 @@ TaggedCache<
|
|||||||
|
|
||||||
auto const start = std::chrono::steady_clock::now();
|
auto const start = std::chrono::steady_clock::now();
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
|
|
||||||
if (m_target_size == 0 ||
|
if (m_target_size == 0 ||
|
||||||
(static_cast<int>(m_cache.size()) <= m_target_size))
|
(static_cast<int>(m_cache.size()) <= m_target_size))
|
||||||
{
|
{
|
||||||
@@ -330,12 +312,13 @@ TaggedCache<
|
|||||||
m_cache.map()[p],
|
m_cache.map()[p],
|
||||||
allStuffToSweep[p],
|
allStuffToSweep[p],
|
||||||
allRemovals,
|
allRemovals,
|
||||||
lock));
|
partitionLocks_[p]));
|
||||||
}
|
}
|
||||||
for (std::thread& worker : workers)
|
for (std::thread& worker : workers)
|
||||||
worker.join();
|
worker.join();
|
||||||
|
|
||||||
m_cache_count -= allRemovals;
|
int removals = allRemovals.load(std::memory_order_relaxed);
|
||||||
|
m_cache_count.fetch_sub(removals, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
// At this point allStuffToSweep will go out of scope outside the lock
|
// At this point allStuffToSweep will go out of scope outside the lock
|
||||||
// and decrement the reference count on each strong pointer.
|
// and decrement the reference count on each strong pointer.
|
||||||
@@ -369,7 +352,8 @@ TaggedCache<
|
|||||||
{
|
{
|
||||||
// Remove from cache, if !valid, remove from map too. Returns true if
|
// Remove from cache, if !valid, remove from map too. Returns true if
|
||||||
// removed from cache
|
// removed from cache
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
|
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||||
|
|
||||||
auto cit = m_cache.find(key);
|
auto cit = m_cache.find(key);
|
||||||
|
|
||||||
@@ -382,7 +366,7 @@ TaggedCache<
|
|||||||
|
|
||||||
if (entry.isCached())
|
if (entry.isCached())
|
||||||
{
|
{
|
||||||
--m_cache_count;
|
m_cache_count.fetch_sub(1, std::memory_order_relaxed);
|
||||||
entry.ptr.convertToWeak();
|
entry.ptr.convertToWeak();
|
||||||
ret = true;
|
ret = true;
|
||||||
}
|
}
|
||||||
@@ -420,17 +404,16 @@ TaggedCache<
|
|||||||
{
|
{
|
||||||
// Return canonical value, store if needed, refresh in cache
|
// Return canonical value, store if needed, refresh in cache
|
||||||
// Return values: true=we had the data already
|
// Return values: true=we had the data already
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
|
|
||||||
|
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||||
auto cit = m_cache.find(key);
|
auto cit = m_cache.find(key);
|
||||||
|
|
||||||
if (cit == m_cache.end())
|
if (cit == m_cache.end())
|
||||||
{
|
{
|
||||||
m_cache.emplace(
|
m_cache.emplace(
|
||||||
std::piecewise_construct,
|
std::piecewise_construct,
|
||||||
std::forward_as_tuple(key),
|
std::forward_as_tuple(key),
|
||||||
std::forward_as_tuple(m_clock.now(), data));
|
std::forward_as_tuple(m_clock.now(), data));
|
||||||
++m_cache_count;
|
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,12 +462,12 @@ TaggedCache<
|
|||||||
data = cachedData;
|
data = cachedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
++m_cache_count;
|
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.ptr = data;
|
entry.ptr = data;
|
||||||
++m_cache_count;
|
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -560,10 +543,11 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::fetch(key_type const& key)
|
Mutex>::fetch(key_type const& key)
|
||||||
{
|
{
|
||||||
std::lock_guard<mutex_type> l(m_mutex);
|
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||||
auto ret = initialFetch(key, l);
|
|
||||||
|
auto ret = initialFetch(key);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
++m_misses;
|
m_misses.fetch_add(1, std::memory_order_relaxed);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -627,8 +611,8 @@ TaggedCache<
|
|||||||
Mutex>::insert(key_type const& key)
|
Mutex>::insert(key_type const& key)
|
||||||
-> std::enable_if_t<IsKeyCache, ReturnType>
|
-> std::enable_if_t<IsKeyCache, ReturnType>
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
clock_type::time_point const now(m_clock.now());
|
clock_type::time_point const now(m_clock.now());
|
||||||
|
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||||
auto [it, inserted] = m_cache.emplace(
|
auto [it, inserted] = m_cache.emplace(
|
||||||
std::piecewise_construct,
|
std::piecewise_construct,
|
||||||
std::forward_as_tuple(key),
|
std::forward_as_tuple(key),
|
||||||
@@ -668,29 +652,6 @@ TaggedCache<
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
|
||||||
class Key,
|
|
||||||
class T,
|
|
||||||
bool IsKeyCache,
|
|
||||||
class SharedWeakUnionPointer,
|
|
||||||
class SharedPointerType,
|
|
||||||
class Hash,
|
|
||||||
class KeyEqual,
|
|
||||||
class Mutex>
|
|
||||||
inline auto
|
|
||||||
TaggedCache<
|
|
||||||
Key,
|
|
||||||
T,
|
|
||||||
IsKeyCache,
|
|
||||||
SharedWeakUnionPointer,
|
|
||||||
SharedPointerType,
|
|
||||||
Hash,
|
|
||||||
KeyEqual,
|
|
||||||
Mutex>::peekMutex() -> mutex_type&
|
|
||||||
{
|
|
||||||
return m_mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <
|
template <
|
||||||
class Key,
|
class Key,
|
||||||
class T,
|
class T,
|
||||||
@@ -714,10 +675,13 @@ TaggedCache<
|
|||||||
std::vector<key_type> v;
|
std::vector<key_type> v;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
|
||||||
v.reserve(m_cache.size());
|
v.reserve(m_cache.size());
|
||||||
for (auto const& _ : m_cache)
|
for (std::size_t i = 0; i < partitionLocks_.size(); ++i)
|
||||||
v.push_back(_.first);
|
{
|
||||||
|
std::lock_guard<Mutex> lock(partitionLocks_[i]);
|
||||||
|
for (auto const& entry : m_cache.map()[i])
|
||||||
|
v.push_back(entry.first);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@@ -743,11 +707,12 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::rate() const
|
Mutex>::rate() const
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
auto const hits = m_hits.load(std::memory_order_relaxed);
|
||||||
auto const tot = m_hits + m_misses;
|
auto const misses = m_misses.load(std::memory_order_relaxed);
|
||||||
|
auto const tot = hits + misses;
|
||||||
if (tot == 0)
|
if (tot == 0)
|
||||||
return 0;
|
return 0.0;
|
||||||
return double(m_hits) / tot;
|
return double(hits) / tot;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <
|
||||||
@@ -771,18 +736,16 @@ TaggedCache<
|
|||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::fetch(key_type const& digest, Handler const& h)
|
Mutex>::fetch(key_type const& digest, Handler const& h)
|
||||||
{
|
{
|
||||||
{
|
std::lock_guard<Mutex> lock(lockPartition(digest));
|
||||||
std::lock_guard l(m_mutex);
|
|
||||||
if (auto ret = initialFetch(digest, l))
|
if (auto ret = initialFetch(digest))
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
auto sle = h();
|
auto sle = h();
|
||||||
if (!sle)
|
if (!sle)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::lock_guard l(m_mutex);
|
m_misses.fetch_add(1, std::memory_order_relaxed);
|
||||||
++m_misses;
|
|
||||||
auto const [it, inserted] =
|
auto const [it, inserted] =
|
||||||
m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle)));
|
m_cache.emplace(digest, Entry(m_clock.now(), std::move(sle)));
|
||||||
if (!inserted)
|
if (!inserted)
|
||||||
@@ -809,9 +772,10 @@ TaggedCache<
|
|||||||
SharedPointerType,
|
SharedPointerType,
|
||||||
Hash,
|
Hash,
|
||||||
KeyEqual,
|
KeyEqual,
|
||||||
Mutex>::
|
Mutex>::initialFetch(key_type const& key)
|
||||||
initialFetch(key_type const& key, std::lock_guard<mutex_type> const& l)
|
|
||||||
{
|
{
|
||||||
|
std::lock_guard<Mutex> lock(lockPartition(key));
|
||||||
|
|
||||||
auto cit = m_cache.find(key);
|
auto cit = m_cache.find(key);
|
||||||
if (cit == m_cache.end())
|
if (cit == m_cache.end())
|
||||||
return {};
|
return {};
|
||||||
@@ -819,7 +783,7 @@ TaggedCache<
|
|||||||
Entry& entry = cit->second;
|
Entry& entry = cit->second;
|
||||||
if (entry.isCached())
|
if (entry.isCached())
|
||||||
{
|
{
|
||||||
++m_hits;
|
m_hits.fetch_add(1, std::memory_order_relaxed);
|
||||||
entry.touch(m_clock.now());
|
entry.touch(m_clock.now());
|
||||||
return entry.ptr.getStrong();
|
return entry.ptr.getStrong();
|
||||||
}
|
}
|
||||||
@@ -827,12 +791,13 @@ TaggedCache<
|
|||||||
if (entry.isCached())
|
if (entry.isCached())
|
||||||
{
|
{
|
||||||
// independent of cache size, so not counted as a hit
|
// independent of cache size, so not counted as a hit
|
||||||
++m_cache_count;
|
m_cache_count.fetch_add(1, std::memory_order_relaxed);
|
||||||
entry.touch(m_clock.now());
|
entry.touch(m_clock.now());
|
||||||
return entry.ptr.getStrong();
|
return entry.ptr.getStrong();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cache.erase(cit);
|
m_cache.erase(cit);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -861,10 +826,11 @@ TaggedCache<
|
|||||||
{
|
{
|
||||||
beast::insight::Gauge::value_type hit_rate(0);
|
beast::insight::Gauge::value_type hit_rate(0);
|
||||||
{
|
{
|
||||||
std::lock_guard lock(m_mutex);
|
auto const hits = m_hits.load(std::memory_order_relaxed);
|
||||||
auto const total(m_hits + m_misses);
|
auto const misses = m_misses.load(std::memory_order_relaxed);
|
||||||
|
auto const total = hits + misses;
|
||||||
if (total != 0)
|
if (total != 0)
|
||||||
hit_rate = (m_hits * 100) / total;
|
hit_rate = (hits * 100) / total;
|
||||||
}
|
}
|
||||||
m_stats.hit_rate.set(hit_rate);
|
m_stats.hit_rate.set(hit_rate);
|
||||||
}
|
}
|
||||||
@@ -895,12 +861,16 @@ TaggedCache<
|
|||||||
typename KeyValueCacheType::map_type& partition,
|
typename KeyValueCacheType::map_type& partition,
|
||||||
SweptPointersVector& stuffToSweep,
|
SweptPointersVector& stuffToSweep,
|
||||||
std::atomic<int>& allRemovals,
|
std::atomic<int>& allRemovals,
|
||||||
std::lock_guard<std::recursive_mutex> const&)
|
Mutex& partitionLock)
|
||||||
{
|
{
|
||||||
return std::thread([&, this]() {
|
return std::thread([&, this]() {
|
||||||
|
beast::setCurrentThreadName("sweep-KVCache");
|
||||||
|
|
||||||
int cacheRemovals = 0;
|
int cacheRemovals = 0;
|
||||||
int mapRemovals = 0;
|
int mapRemovals = 0;
|
||||||
|
|
||||||
|
std::lock_guard<Mutex> lock(partitionLock);
|
||||||
|
|
||||||
// Keep references to all the stuff we sweep
|
// Keep references to all the stuff we sweep
|
||||||
// so that we can destroy them outside the lock.
|
// so that we can destroy them outside the lock.
|
||||||
stuffToSweep.reserve(partition.size());
|
stuffToSweep.reserve(partition.size());
|
||||||
@@ -984,12 +954,16 @@ TaggedCache<
|
|||||||
typename KeyOnlyCacheType::map_type& partition,
|
typename KeyOnlyCacheType::map_type& partition,
|
||||||
SweptPointersVector&,
|
SweptPointersVector&,
|
||||||
std::atomic<int>& allRemovals,
|
std::atomic<int>& allRemovals,
|
||||||
std::lock_guard<std::recursive_mutex> const&)
|
Mutex& partitionLock)
|
||||||
{
|
{
|
||||||
return std::thread([&, this]() {
|
return std::thread([&, this]() {
|
||||||
|
beast::setCurrentThreadName("sweep-KCache");
|
||||||
|
|
||||||
int cacheRemovals = 0;
|
int cacheRemovals = 0;
|
||||||
int mapRemovals = 0;
|
int mapRemovals = 0;
|
||||||
|
|
||||||
|
std::lock_guard<Mutex> lock(partitionLock);
|
||||||
|
|
||||||
// Keep references to all the stuff we sweep
|
// Keep references to all the stuff we sweep
|
||||||
// so that we can destroy them outside the lock.
|
// so that we can destroy them outside the lock.
|
||||||
{
|
{
|
||||||
@@ -1024,6 +998,29 @@ TaggedCache<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
class Key,
|
||||||
|
class T,
|
||||||
|
bool IsKeyCache,
|
||||||
|
class SharedWeakUnionPointer,
|
||||||
|
class SharedPointerType,
|
||||||
|
class Hash,
|
||||||
|
class KeyEqual,
|
||||||
|
class Mutex>
|
||||||
|
inline Mutex&
|
||||||
|
TaggedCache<
|
||||||
|
Key,
|
||||||
|
T,
|
||||||
|
IsKeyCache,
|
||||||
|
SharedWeakUnionPointer,
|
||||||
|
SharedPointerType,
|
||||||
|
Hash,
|
||||||
|
KeyEqual,
|
||||||
|
Mutex>::lockPartition(key_type const& key) const
|
||||||
|
{
|
||||||
|
return partitionLocks_[m_cache.partition_index(key)];
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ripple
|
} // namespace ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -277,6 +277,12 @@ public:
|
|||||||
return map_;
|
return map_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
partition_map_type const&
|
||||||
|
map() const
|
||||||
|
{
|
||||||
|
return map_;
|
||||||
|
}
|
||||||
|
|
||||||
iterator
|
iterator
|
||||||
begin()
|
begin()
|
||||||
{
|
{
|
||||||
@@ -321,6 +327,12 @@ public:
|
|||||||
return cend();
|
return cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
partition_index(key_type const& key) const
|
||||||
|
{
|
||||||
|
return partitioner(key);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <class T>
|
template <class T>
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <xrpl/basics/ByteUtilities.h>
|
#include <xrpl/basics/ByteUtilities.h>
|
||||||
#include <xrpl/basics/base_uint.h>
|
#include <xrpl/basics/base_uint.h>
|
||||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
|||||||
@@ -58,10 +58,10 @@ public:
|
|||||||
// Insert an item, retrieve it, and age it so it gets purged.
|
// Insert an item, retrieve it, and age it so it gets purged.
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 0);
|
BEAST_EXPECT(c.size() == 0);
|
||||||
BEAST_EXPECT(!c.insert(1, "one"));
|
BEAST_EXPECT(!c.insert(1, "one"));
|
||||||
BEAST_EXPECT(c.getCacheSize() == 1);
|
BEAST_EXPECT(c.getCacheSize() == 1);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string s;
|
std::string s;
|
||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
++clock;
|
++clock;
|
||||||
c.sweep();
|
c.sweep();
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 0);
|
BEAST_EXPECT(c.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert an item, maintain a strong pointer, age it, and
|
// Insert an item, maintain a strong pointer, age it, and
|
||||||
@@ -80,7 +80,7 @@ public:
|
|||||||
{
|
{
|
||||||
BEAST_EXPECT(!c.insert(2, "two"));
|
BEAST_EXPECT(!c.insert(2, "two"));
|
||||||
BEAST_EXPECT(c.getCacheSize() == 1);
|
BEAST_EXPECT(c.getCacheSize() == 1);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
|
|
||||||
{
|
{
|
||||||
auto p = c.fetch(2);
|
auto p = c.fetch(2);
|
||||||
@@ -88,14 +88,14 @@ public:
|
|||||||
++clock;
|
++clock;
|
||||||
c.sweep();
|
c.sweep();
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure its gone now that our reference is gone
|
// Make sure its gone now that our reference is gone
|
||||||
++clock;
|
++clock;
|
||||||
c.sweep();
|
c.sweep();
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 0);
|
BEAST_EXPECT(c.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the same key/value pair and make sure we get the same result
|
// Insert the same key/value pair and make sure we get the same result
|
||||||
@@ -111,7 +111,7 @@ public:
|
|||||||
++clock;
|
++clock;
|
||||||
c.sweep();
|
c.sweep();
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 0);
|
BEAST_EXPECT(c.size() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put an object in but keep a strong pointer to it, advance the clock a
|
// Put an object in but keep a strong pointer to it, advance the clock a
|
||||||
@@ -121,24 +121,24 @@ public:
|
|||||||
// Put an object in
|
// Put an object in
|
||||||
BEAST_EXPECT(!c.insert(4, "four"));
|
BEAST_EXPECT(!c.insert(4, "four"));
|
||||||
BEAST_EXPECT(c.getCacheSize() == 1);
|
BEAST_EXPECT(c.getCacheSize() == 1);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Keep a strong pointer to it
|
// Keep a strong pointer to it
|
||||||
auto const p1 = c.fetch(4);
|
auto const p1 = c.fetch(4);
|
||||||
BEAST_EXPECT(p1 != nullptr);
|
BEAST_EXPECT(p1 != nullptr);
|
||||||
BEAST_EXPECT(c.getCacheSize() == 1);
|
BEAST_EXPECT(c.getCacheSize() == 1);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
// Advance the clock a lot
|
// Advance the clock a lot
|
||||||
++clock;
|
++clock;
|
||||||
c.sweep();
|
c.sweep();
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
// Canonicalize a new object with the same key
|
// Canonicalize a new object with the same key
|
||||||
auto p2 = std::make_shared<std::string>("four");
|
auto p2 = std::make_shared<std::string>("four");
|
||||||
BEAST_EXPECT(c.canonicalize_replace_client(4, p2));
|
BEAST_EXPECT(c.canonicalize_replace_client(4, p2));
|
||||||
BEAST_EXPECT(c.getCacheSize() == 1);
|
BEAST_EXPECT(c.getCacheSize() == 1);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 1);
|
BEAST_EXPECT(c.size() == 1);
|
||||||
// Make sure we get the original object
|
// Make sure we get the original object
|
||||||
BEAST_EXPECT(p1.get() == p2.get());
|
BEAST_EXPECT(p1.get() == p2.get());
|
||||||
}
|
}
|
||||||
@@ -146,7 +146,7 @@ public:
|
|||||||
++clock;
|
++clock;
|
||||||
c.sweep();
|
c.sweep();
|
||||||
BEAST_EXPECT(c.getCacheSize() == 0);
|
BEAST_EXPECT(c.getCacheSize() == 0);
|
||||||
BEAST_EXPECT(c.getTrackSize() == 0);
|
BEAST_EXPECT(c.size() == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1136,6 +1136,10 @@ public:
|
|||||||
ConsensusParms p;
|
ConsensusParms p;
|
||||||
std::size_t peersUnchanged = 0;
|
std::size_t peersUnchanged = 0;
|
||||||
|
|
||||||
|
auto logs = std::make_unique<Logs>(beast::severities::kError);
|
||||||
|
auto j = logs->journal("Test");
|
||||||
|
auto clog = std::make_unique<std::stringstream>();
|
||||||
|
|
||||||
// Three cases:
|
// Three cases:
|
||||||
// 1 proposing, initial vote yes
|
// 1 proposing, initial vote yes
|
||||||
// 2 proposing, initial vote no
|
// 2 proposing, initial vote no
|
||||||
@@ -1172,10 +1176,15 @@ public:
|
|||||||
BEAST_EXPECT(proposingFalse.getOurVote() == false);
|
BEAST_EXPECT(proposingFalse.getOurVote() == false);
|
||||||
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
|
|
||||||
// I'm in the majority, my vote should not change
|
// I'm in the majority, my vote should not change
|
||||||
BEAST_EXPECT(!proposingTrue.updateVote(5, true, p));
|
BEAST_EXPECT(!proposingTrue.updateVote(5, true, p));
|
||||||
@@ -1189,10 +1198,15 @@ public:
|
|||||||
BEAST_EXPECT(!followingFalse.updateVote(10, false, p));
|
BEAST_EXPECT(!followingFalse.updateVote(10, false, p));
|
||||||
|
|
||||||
peersUnchanged = 2;
|
peersUnchanged = 2;
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
|
|
||||||
// Right now, the vote is 51%. The requirement is about to jump to
|
// Right now, the vote is 51%. The requirement is about to jump to
|
||||||
// 65%
|
// 65%
|
||||||
@@ -1282,10 +1296,15 @@ public:
|
|||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
|
|
||||||
peersUnchanged = 3;
|
peersUnchanged = 3;
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
|
|
||||||
// Threshold jumps to 95%
|
// Threshold jumps to 95%
|
||||||
BEAST_EXPECT(proposingTrue.updateVote(220, true, p));
|
BEAST_EXPECT(proposingTrue.updateVote(220, true, p));
|
||||||
@@ -1322,12 +1341,60 @@ public:
|
|||||||
|
|
||||||
for (peersUnchanged = 0; peersUnchanged < 6; ++peersUnchanged)
|
for (peersUnchanged = 0; peersUnchanged < 6; ++peersUnchanged)
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!proposingFalse.stalled(p, true, peersUnchanged));
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(!followingTrue.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(!followingFalse.stalled(p, false, peersUnchanged));
|
!proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(
|
||||||
|
!followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto expectStalled = [this, &clog](
|
||||||
|
int txid,
|
||||||
|
bool ourVote,
|
||||||
|
int ourTime,
|
||||||
|
int peerTime,
|
||||||
|
int support,
|
||||||
|
std::uint32_t line) {
|
||||||
|
using namespace std::string_literals;
|
||||||
|
|
||||||
|
auto const s = clog->str();
|
||||||
|
expect(s.find("stalled"), s, __FILE__, line);
|
||||||
|
expect(
|
||||||
|
s.starts_with("Transaction "s + std::to_string(txid)),
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.find("voting "s + (ourVote ? "YES" : "NO")) != s.npos,
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.find("for "s + std::to_string(ourTime) + " rounds."s) !=
|
||||||
|
s.npos,
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.find(
|
||||||
|
"votes in "s + std::to_string(peerTime) + " rounds.") !=
|
||||||
|
s.npos,
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
expect(
|
||||||
|
s.ends_with(
|
||||||
|
"has "s + std::to_string(support) + "% support. "s),
|
||||||
|
s,
|
||||||
|
__FILE__,
|
||||||
|
line);
|
||||||
|
clog = std::make_unique<std::stringstream>();
|
||||||
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 1; ++i)
|
for (int i = 0; i < 1; ++i)
|
||||||
{
|
{
|
||||||
BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p));
|
BEAST_EXPECT(!proposingTrue.updateVote(250 + 10 * i, true, p));
|
||||||
@@ -1342,22 +1409,34 @@ public:
|
|||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
|
|
||||||
// true vote has changed recently, so not stalled
|
// true vote has changed recently, so not stalled
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0));
|
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog));
|
||||||
|
BEAST_EXPECT(clog->str() == "");
|
||||||
// remaining votes have been unchanged in so long that we only
|
// remaining votes have been unchanged in so long that we only
|
||||||
// need to hit the second round at 95% to be stalled, regardless
|
// need to hit the second round at 95% to be stalled, regardless
|
||||||
// of peers
|
// of peers
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, 0));
|
BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, 0));
|
expectStalled(98, false, 11, 0, 2, __LINE__);
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, 0));
|
BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(97, true, 11, 0, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(96, false, 11, 0, 3, __LINE__);
|
||||||
|
|
||||||
// true vote has changed recently, so not stalled
|
// true vote has changed recently, so not stalled
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
|
!proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
BEAST_EXPECTS(clog->str() == "", clog->str());
|
||||||
// remaining votes have been unchanged in so long that we only
|
// remaining votes have been unchanged in so long that we only
|
||||||
// need to hit the second round at 95% to be stalled, regardless
|
// need to hit the second round at 95% to be stalled, regardless
|
||||||
// of peers
|
// of peers
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged));
|
proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged));
|
expectStalled(98, false, 11, 6, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(97, true, 11, 6, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(96, false, 11, 6, 3, __LINE__);
|
||||||
}
|
}
|
||||||
for (int i = 1; i < 3; ++i)
|
for (int i = 1; i < 3; ++i)
|
||||||
{
|
{
|
||||||
@@ -1374,19 +1453,31 @@ public:
|
|||||||
|
|
||||||
// true vote changed 2 rounds ago, and peers are changing, so
|
// true vote changed 2 rounds ago, and peers are changing, so
|
||||||
// not stalled
|
// not stalled
|
||||||
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0));
|
BEAST_EXPECT(!proposingTrue.stalled(p, true, 0, j, clog));
|
||||||
|
BEAST_EXPECTS(clog->str() == "", clog->str());
|
||||||
// still stalled
|
// still stalled
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, 0));
|
BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, 0));
|
expectStalled(98, false, 11 + i, 0, 2, __LINE__);
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, 0));
|
BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 0, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 0, 3, __LINE__);
|
||||||
|
|
||||||
// true vote changed 2 rounds ago, and peers are NOT changing,
|
// true vote changed 2 rounds ago, and peers are NOT changing,
|
||||||
// so stalled
|
// so stalled
|
||||||
BEAST_EXPECT(proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
|
proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
expectStalled(99, true, 1 + i, 6, 97, __LINE__);
|
||||||
// still stalled
|
// still stalled
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged));
|
proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged));
|
expectStalled(98, false, 11 + i, 6, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 6, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 6, 3, __LINE__);
|
||||||
}
|
}
|
||||||
for (int i = 3; i < 5; ++i)
|
for (int i = 3; i < 5; ++i)
|
||||||
{
|
{
|
||||||
@@ -1401,15 +1492,27 @@ public:
|
|||||||
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
BEAST_EXPECT(followingTrue.getOurVote() == true);
|
||||||
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
BEAST_EXPECT(followingFalse.getOurVote() == false);
|
||||||
|
|
||||||
BEAST_EXPECT(proposingTrue.stalled(p, true, 0));
|
BEAST_EXPECT(proposingTrue.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, 0));
|
expectStalled(99, true, 1 + i, 0, 97, __LINE__);
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, 0));
|
BEAST_EXPECT(proposingFalse.stalled(p, true, 0, j, clog));
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, 0));
|
expectStalled(98, false, 11 + i, 0, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(followingTrue.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 0, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(followingFalse.stalled(p, false, 0, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 0, 3, __LINE__);
|
||||||
|
|
||||||
BEAST_EXPECT(proposingTrue.stalled(p, true, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
BEAST_EXPECT(proposingFalse.stalled(p, true, peersUnchanged));
|
proposingTrue.stalled(p, true, peersUnchanged, j, clog));
|
||||||
BEAST_EXPECT(followingTrue.stalled(p, false, peersUnchanged));
|
expectStalled(99, true, 1 + i, 6, 97, __LINE__);
|
||||||
BEAST_EXPECT(followingFalse.stalled(p, false, peersUnchanged));
|
BEAST_EXPECT(
|
||||||
|
proposingFalse.stalled(p, true, peersUnchanged, j, clog));
|
||||||
|
expectStalled(98, false, 11 + i, 6, 2, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingTrue.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(97, true, 11 + i, 6, 97, __LINE__);
|
||||||
|
BEAST_EXPECT(
|
||||||
|
followingFalse.stalled(p, false, peersUnchanged, j, clog));
|
||||||
|
expectStalled(96, false, 11 + i, 6, 3, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ RCLValidationsAdaptor::acquire(LedgerHash const& hash)
|
|||||||
|
|
||||||
if (!ledger)
|
if (!ledger)
|
||||||
{
|
{
|
||||||
JLOG(j_.debug())
|
JLOG(j_.warn())
|
||||||
<< "Need validated ledger for preferred ledger analysis " << hash;
|
<< "Need validated ledger for preferred ledger analysis " << hash;
|
||||||
|
|
||||||
Application* pApp = &app_;
|
Application* pApp = &app_;
|
||||||
|
|||||||
@@ -63,8 +63,6 @@ LedgerHistory::insert(
|
|||||||
ledger->stateMap().getHash().isNonZero(),
|
ledger->stateMap().getHash().isNonZero(),
|
||||||
"ripple::LedgerHistory::insert : nonzero hash");
|
"ripple::LedgerHistory::insert : nonzero hash");
|
||||||
|
|
||||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
|
||||||
|
|
||||||
bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(
|
bool const alreadyHad = m_ledgers_by_hash.canonicalize_replace_cache(
|
||||||
ledger->info().hash, ledger);
|
ledger->info().hash, ledger);
|
||||||
if (validated)
|
if (validated)
|
||||||
@@ -76,7 +74,6 @@ LedgerHistory::insert(
|
|||||||
LedgerHash
|
LedgerHash
|
||||||
LedgerHistory::getLedgerHash(LedgerIndex index)
|
LedgerHistory::getLedgerHash(LedgerIndex index)
|
||||||
{
|
{
|
||||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
|
||||||
if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end())
|
if (auto it = mLedgersByIndex.find(index); it != mLedgersByIndex.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
return {};
|
return {};
|
||||||
@@ -86,13 +83,11 @@ std::shared_ptr<Ledger const>
|
|||||||
LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
|
||||||
auto it = mLedgersByIndex.find(index);
|
auto it = mLedgersByIndex.find(index);
|
||||||
|
|
||||||
if (it != mLedgersByIndex.end())
|
if (it != mLedgersByIndex.end())
|
||||||
{
|
{
|
||||||
uint256 hash = it->second;
|
uint256 hash = it->second;
|
||||||
sl.unlock();
|
|
||||||
return getLedgerByHash(hash);
|
return getLedgerByHash(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,7 +103,6 @@ LedgerHistory::getLedgerBySeq(LedgerIndex index)
|
|||||||
|
|
||||||
{
|
{
|
||||||
// Add this ledger to the local tracking by index
|
// Add this ledger to the local tracking by index
|
||||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
|
||||||
|
|
||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
ret->isImmutable(),
|
ret->isImmutable(),
|
||||||
@@ -458,8 +452,6 @@ LedgerHistory::builtLedger(
|
|||||||
XRPL_ASSERT(
|
XRPL_ASSERT(
|
||||||
!hash.isZero(), "ripple::LedgerHistory::builtLedger : nonzero hash");
|
!hash.isZero(), "ripple::LedgerHistory::builtLedger : nonzero hash");
|
||||||
|
|
||||||
std::unique_lock sl(m_consensus_validated.peekMutex());
|
|
||||||
|
|
||||||
auto entry = std::make_shared<cv_entry>();
|
auto entry = std::make_shared<cv_entry>();
|
||||||
m_consensus_validated.canonicalize_replace_client(index, entry);
|
m_consensus_validated.canonicalize_replace_client(index, entry);
|
||||||
|
|
||||||
@@ -500,8 +492,6 @@ LedgerHistory::validatedLedger(
|
|||||||
!hash.isZero(),
|
!hash.isZero(),
|
||||||
"ripple::LedgerHistory::validatedLedger : nonzero hash");
|
"ripple::LedgerHistory::validatedLedger : nonzero hash");
|
||||||
|
|
||||||
std::unique_lock sl(m_consensus_validated.peekMutex());
|
|
||||||
|
|
||||||
auto entry = std::make_shared<cv_entry>();
|
auto entry = std::make_shared<cv_entry>();
|
||||||
m_consensus_validated.canonicalize_replace_client(index, entry);
|
m_consensus_validated.canonicalize_replace_client(index, entry);
|
||||||
|
|
||||||
@@ -535,10 +525,9 @@ LedgerHistory::validatedLedger(
|
|||||||
bool
|
bool
|
||||||
LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
|
LedgerHistory::fixIndex(LedgerIndex ledgerIndex, LedgerHash const& ledgerHash)
|
||||||
{
|
{
|
||||||
std::unique_lock sl(m_ledgers_by_hash.peekMutex());
|
auto ledger = m_ledgers_by_hash.fetch(ledgerHash);
|
||||||
auto it = mLedgersByIndex.find(ledgerIndex);
|
auto it = mLedgersByIndex.find(ledgerIndex);
|
||||||
|
if (ledger && (it != mLedgersByIndex.end()) && (it->second != ledgerHash))
|
||||||
if ((it != mLedgersByIndex.end()) && (it->second != ledgerHash))
|
|
||||||
{
|
{
|
||||||
it->second = ledgerHash;
|
it->second = ledgerHash;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -139,11 +139,11 @@ checkConsensusReached(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only get stalled when every disputed transaction unequivocally has 80%
|
// We only get stalled when there are disputed transactions and all of them
|
||||||
// (minConsensusPct) agreement, either for or against. That is: either under
|
// unequivocally have 80% (minConsensusPct) agreement, either for or
|
||||||
// 20% or over 80% consensus (repectively "nay" or "yay"). This prevents
|
// against. That is: either under 20% or over 80% consensus (repectively
|
||||||
// manipulation by a minority of byzantine peers of which transactions make
|
// "nay" or "yay"). This prevents manipulation by a minority of byzantine
|
||||||
// the cut to get into the ledger.
|
// peers of which transactions make the cut to get into the ledger.
|
||||||
if (stalled)
|
if (stalled)
|
||||||
{
|
{
|
||||||
CLOG(clog) << "consensus stalled. ";
|
CLOG(clog) << "consensus stalled. ";
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ shouldCloseLedger(
|
|||||||
agree
|
agree
|
||||||
@param stalled the network appears to be stalled, where
|
@param stalled the network appears to be stalled, where
|
||||||
neither we nor our peers have changed their vote on any disputes in a
|
neither we nor our peers have changed their vote on any disputes in a
|
||||||
while. This is undesirable, and will cause us to end consensus
|
while. This is undesirable, and should be rare, and will cause us to
|
||||||
without 80% agreement.
|
end consensus without 80% agreement.
|
||||||
@param parms Consensus constant parameters
|
@param parms Consensus constant parameters
|
||||||
@param proposing whether we should count ourselves
|
@param proposing whether we should count ourselves
|
||||||
@param j journal for logging
|
@param j journal for logging
|
||||||
@@ -1712,15 +1712,29 @@ Consensus<Adaptor>::haveConsensus(
|
|||||||
<< ", disagree=" << disagree;
|
<< ", disagree=" << disagree;
|
||||||
|
|
||||||
ConsensusParms const& parms = adaptor_.parms();
|
ConsensusParms const& parms = adaptor_.parms();
|
||||||
// Stalling is BAD
|
// Stalling is BAD. It means that we have a consensus on the close time, so
|
||||||
|
// peers are talking, but we have disputed transactions that peers are
|
||||||
|
// unable or unwilling to come to agreement on one way or the other.
|
||||||
bool const stalled = haveCloseTimeConsensus_ &&
|
bool const stalled = haveCloseTimeConsensus_ &&
|
||||||
|
!result_->disputes.empty() &&
|
||||||
std::ranges::all_of(result_->disputes,
|
std::ranges::all_of(result_->disputes,
|
||||||
[this, &parms](auto const& dispute) {
|
[this, &parms, &clog](auto const& dispute) {
|
||||||
return dispute.second.stalled(
|
return dispute.second.stalled(
|
||||||
parms,
|
parms,
|
||||||
mode_.get() == ConsensusMode::proposing,
|
mode_.get() == ConsensusMode::proposing,
|
||||||
peerUnchangedCounter_);
|
peerUnchangedCounter_,
|
||||||
|
j_,
|
||||||
|
clog);
|
||||||
});
|
});
|
||||||
|
if (stalled)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "Consensus detects as stalled with " << (agree + disagree) << "/"
|
||||||
|
<< prevProposers_ << " proposers, and " << result_->disputes.size()
|
||||||
|
<< " stalled disputed transactions.";
|
||||||
|
JLOG(j_.error()) << ss.str();
|
||||||
|
CLOG(clog) << ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
// Determine if we actually have consensus or not
|
// Determine if we actually have consensus or not
|
||||||
result_->state = checkConsensus(
|
result_->state = checkConsensus(
|
||||||
|
|||||||
@@ -85,7 +85,12 @@ public:
|
|||||||
//! Are we and our peers "stalled" where we probably won't change
|
//! Are we and our peers "stalled" where we probably won't change
|
||||||
//! our vote?
|
//! our vote?
|
||||||
bool
|
bool
|
||||||
stalled(ConsensusParms const& p, bool proposing, int peersUnchanged) const
|
stalled(
|
||||||
|
ConsensusParms const& p,
|
||||||
|
bool proposing,
|
||||||
|
int peersUnchanged,
|
||||||
|
beast::Journal j,
|
||||||
|
std::unique_ptr<std::stringstream> const& clog) const
|
||||||
{
|
{
|
||||||
// at() can throw, but the map is built by hand to ensure all valid
|
// at() can throw, but the map is built by hand to ensure all valid
|
||||||
// values are available.
|
// values are available.
|
||||||
@@ -123,8 +128,24 @@ public:
|
|||||||
int const weight = support / total;
|
int const weight = support / total;
|
||||||
// Returns true if the tx has more than minCONSENSUS_PCT (80) percent
|
// Returns true if the tx has more than minCONSENSUS_PCT (80) percent
|
||||||
// agreement. Either voting for _or_ voting against the tx.
|
// agreement. Either voting for _or_ voting against the tx.
|
||||||
return weight > p.minCONSENSUS_PCT ||
|
bool const stalled =
|
||||||
weight < (100 - p.minCONSENSUS_PCT);
|
weight > p.minCONSENSUS_PCT || weight < (100 - p.minCONSENSUS_PCT);
|
||||||
|
|
||||||
|
if (stalled)
|
||||||
|
{
|
||||||
|
// stalling is an error condition for even a single
|
||||||
|
// transaction.
|
||||||
|
std::stringstream s;
|
||||||
|
s << "Transaction " << ID() << " is stalled. We have been voting "
|
||||||
|
<< (getOurVote() ? "YES" : "NO") << " for " << currentVoteCounter_
|
||||||
|
<< " rounds. Peers have not changed their votes in "
|
||||||
|
<< peersUnchanged << " rounds. The transaction has " << weight
|
||||||
|
<< "% support. ";
|
||||||
|
JLOG(j_.error()) << s.str();
|
||||||
|
CLOG(clog) << s.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return stalled;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! The disputed transaction.
|
//! The disputed transaction.
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ getCountsJson(Application& app, int minObjectCount)
|
|||||||
ret[jss::treenode_cache_size] =
|
ret[jss::treenode_cache_size] =
|
||||||
app.getNodeFamily().getTreeNodeCache()->getCacheSize();
|
app.getNodeFamily().getTreeNodeCache()->getCacheSize();
|
||||||
ret[jss::treenode_track_size] =
|
ret[jss::treenode_track_size] =
|
||||||
app.getNodeFamily().getTreeNodeCache()->getTrackSize();
|
static_cast<int>(app.getNodeFamily().getTreeNodeCache()->size());
|
||||||
|
|
||||||
std::string uptime;
|
std::string uptime;
|
||||||
auto s = UptimeClock::now();
|
auto s = UptimeClock::now();
|
||||||
|
|||||||
Reference in New Issue
Block a user