mirror of
https://github.com/XRPLF/rippled.git
synced 2026-02-17 12:22:34 +00:00
Compare commits
44 Commits
pratik/Ope
...
ximinez/nu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e762b0ce6d | ||
|
|
bbb03e153e | ||
|
|
63b6ec98ea | ||
|
|
7ef9fb4290 | ||
|
|
49dcb6b60b | ||
|
|
26dd1fafe3 | ||
|
|
4b99771021 | ||
|
|
e6664fe4cf | ||
|
|
b5001bc258 | ||
|
|
5dacfa1938 | ||
|
|
394b256f02 | ||
|
|
5bfa38f6c5 | ||
|
|
44e87b8277 | ||
|
|
ddbd78c67a | ||
|
|
822023d8a4 | ||
|
|
5ab62f4422 | ||
|
|
5d9011084c | ||
|
|
060feb4e37 | ||
|
|
295816f21d | ||
|
|
bf8db0555e | ||
|
|
c8c5207675 | ||
|
|
7c6c49bf98 | ||
|
|
6334be1ff0 | ||
|
|
a9c3bb84ba | ||
|
|
ca99e40290 | ||
|
|
7612c1af0c | ||
|
|
67e40be1ab | ||
|
|
0132174a7b | ||
|
|
8773cc4bbf | ||
|
|
dabdadfff5 | ||
|
|
bfe2cd7893 | ||
|
|
0584c20f36 | ||
|
|
3ced0b27b7 | ||
|
|
f83b27f7dd | ||
|
|
cdb41b5376 | ||
|
|
f223c89a9f | ||
|
|
efe07c09f3 | ||
|
|
79cde8b199 | ||
|
|
2078ce01cf | ||
|
|
2770a9cdf3 | ||
|
|
05ef3b1ad8 | ||
|
|
7dd4dbe285 | ||
|
|
b32a5f2c08 | ||
|
|
df76002a44 |
@@ -1,13 +1,14 @@
|
||||
ignorePaths:
|
||||
- build/**
|
||||
- src/libxrpl/crypto
|
||||
- src/test/** # Will be removed in the future
|
||||
- CMakeUserPresets.json
|
||||
- Doxyfile
|
||||
- docs/**/*.puml
|
||||
- cmake/**
|
||||
- LICENSE.md
|
||||
language: en
|
||||
allowCompoundWords: true # TODO (#6334)
|
||||
allowCompoundWords: true
|
||||
ignoreRandomStrings: true
|
||||
minWordLength: 5
|
||||
dictionaries:
|
||||
@@ -15,29 +16,20 @@ dictionaries:
|
||||
- en_US
|
||||
- en_GB
|
||||
ignoreRegExpList:
|
||||
- /\b[rs][1-9A-HJ-NP-Za-km-z]{25,34}/g # addresses and seeds
|
||||
- /\bC[A-Z0-9]{15}/g # CTIDs
|
||||
- /\b(XRPL|BEAST)_[A-Z_0-9]+_H_INCLUDED+/g # include guards
|
||||
- /\b(XRPL|BEAST)_[A-Z_0-9]+_H+/g # include guards
|
||||
- /[rs][1-9A-HJ-NP-Za-km-z]{25,34}/g # addresses and seeds
|
||||
- /(XRPL|BEAST)_[A-Z_0-9]+_H_INCLUDED+/g # include guards
|
||||
- /(XRPL|BEAST)_[A-Z_0-9]+_H+/g # include guards
|
||||
- /::[a-z:_]+/g # things from other namespaces
|
||||
- /\blib[a-z]+/g # libraries
|
||||
- /\b[0-9]{4}-[0-9]{2}-[0-9]{2}[,:][A-Za-zÀ-ÖØ-öø-ÿ.\s]+/g # copyright dates
|
||||
- /\b[0-9]{4}[,:]?\s*[A-Za-zÀ-ÖØ-öø-ÿ.\s]+/g # copyright years
|
||||
- /lib[a-z]+/g # libraries
|
||||
- /[0-9]{4}-[0-9]{2}-[0-9]{2}[,:][A-Za-zÀ-ÖØ-öø-ÿ.\s]+/g # copyright dates
|
||||
- /[0-9]{4}[,:]?\s*[A-Za-zÀ-ÖØ-öø-ÿ.\s]+/g # copyright years
|
||||
- /\[[A-Za-z0-9-]+\]\(https:\/\/github.com\/[A-Za-z0-9-]+\)/g # Github usernames
|
||||
- /-[DWw][a-zA-Z0-9_-]+=/g # compile flags
|
||||
- /[\['"`]-[DWw][a-zA-Z0-9_-]+['"`\]]/g # compile flags
|
||||
- ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
- ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
overrides:
|
||||
- filename: "**/*_test.cpp" # all test files
|
||||
ignoreRegExpList:
|
||||
- /"[^"]*"/g # double-quoted strings
|
||||
- /'[^']*'/g # single-quoted strings
|
||||
- /`[^`]*`/g # backtick strings
|
||||
suggestWords:
|
||||
- xprl->xrpl
|
||||
- xprld->xrpld # cspell: disable-line not sure what this problem is....
|
||||
- unsynched->unsynced # cspell: disable-line not sure what this problem is....
|
||||
- xprld->xrpld
|
||||
- unsynched->unsynced
|
||||
- synched->synced
|
||||
- synch->sync
|
||||
words:
|
||||
@@ -59,7 +51,6 @@ words:
|
||||
- Britto
|
||||
- Btrfs
|
||||
- canonicality
|
||||
- changespq
|
||||
- checkme
|
||||
- choco
|
||||
- chrono
|
||||
@@ -98,6 +89,7 @@ words:
|
||||
- endmacro
|
||||
- exceptioned
|
||||
- Falco
|
||||
- fcontext
|
||||
- finalizers
|
||||
- firewalled
|
||||
- fmtdur
|
||||
@@ -110,19 +102,18 @@ words:
|
||||
- gpgcheck
|
||||
- gpgkey
|
||||
- hotwallet
|
||||
- hwaddress
|
||||
- hwrap
|
||||
- ifndef
|
||||
- inequation
|
||||
- insuf
|
||||
- insuff
|
||||
- invasively
|
||||
- iou
|
||||
- ious
|
||||
- isrdc
|
||||
- itype
|
||||
- jemalloc
|
||||
- jlog
|
||||
- jtnofill
|
||||
- keylet
|
||||
- keylets
|
||||
- keyvadb
|
||||
@@ -149,7 +140,6 @@ words:
|
||||
- Metafuncton
|
||||
- misprediction
|
||||
- mptbalance
|
||||
- MPTDEX
|
||||
- mptflags
|
||||
- mptid
|
||||
- mptissuance
|
||||
@@ -159,7 +149,6 @@ words:
|
||||
- mptokenissuance
|
||||
- mptokens
|
||||
- mpts
|
||||
- mtgox
|
||||
- multisig
|
||||
- multisign
|
||||
- multisigned
|
||||
@@ -187,7 +176,6 @@ words:
|
||||
- perminute
|
||||
- permissioned
|
||||
- pointee
|
||||
- populator
|
||||
- preauth
|
||||
- preauthorization
|
||||
- preauthorize
|
||||
@@ -196,7 +184,6 @@ words:
|
||||
- protobuf
|
||||
- protos
|
||||
- ptrs
|
||||
- pushd
|
||||
- pyenv
|
||||
- qalloc
|
||||
- queuable
|
||||
@@ -232,6 +219,7 @@ words:
|
||||
- soci
|
||||
- socidb
|
||||
- sslws
|
||||
- stackful
|
||||
- statsd
|
||||
- STATSDCOLLECTOR
|
||||
- stissue
|
||||
8
.github/CODEOWNERS
vendored
Normal file
8
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Allow anyone to review any change by default.
|
||||
*
|
||||
|
||||
# Require the rpc-reviewers team to review changes to the rpc code.
|
||||
include/xrpl/protocol/ @xrplf/rpc-reviewers
|
||||
src/libxrpl/protocol/ @xrplf/rpc-reviewers
|
||||
src/xrpld/rpc/ @xrplf/rpc-reviewers
|
||||
src/xrpld/app/misc/ @xrplf/rpc-reviewers
|
||||
@@ -4,11 +4,14 @@ Loop: test.jtx test.toplevel
|
||||
Loop: test.jtx test.unit_test
|
||||
test.unit_test == test.jtx
|
||||
|
||||
Loop: xrpld.app xrpld.core
|
||||
xrpld.app > xrpld.core
|
||||
|
||||
Loop: xrpld.app xrpld.overlay
|
||||
xrpld.overlay > xrpld.app
|
||||
|
||||
Loop: xrpld.app xrpld.peerfinder
|
||||
xrpld.peerfinder == xrpld.app
|
||||
xrpld.peerfinder ~= xrpld.app
|
||||
|
||||
Loop: xrpld.app xrpld.rpc
|
||||
xrpld.rpc > xrpld.app
|
||||
|
||||
@@ -17,15 +17,12 @@ libxrpl.nodestore > xrpl.protocol
|
||||
libxrpl.protocol > xrpl.basics
|
||||
libxrpl.protocol > xrpl.json
|
||||
libxrpl.protocol > xrpl.protocol
|
||||
libxrpl.rdb > xrpl.basics
|
||||
libxrpl.rdb > xrpl.rdb
|
||||
libxrpl.resource > xrpl.basics
|
||||
libxrpl.resource > xrpl.json
|
||||
libxrpl.resource > xrpl.resource
|
||||
libxrpl.server > xrpl.basics
|
||||
libxrpl.server > xrpl.json
|
||||
libxrpl.server > xrpl.protocol
|
||||
libxrpl.server > xrpl.rdb
|
||||
libxrpl.server > xrpl.server
|
||||
libxrpl.shamap > xrpl.basics
|
||||
libxrpl.shamap > xrpl.protocol
|
||||
@@ -44,9 +41,7 @@ test.app > xrpl.json
|
||||
test.app > xrpl.ledger
|
||||
test.app > xrpl.nodestore
|
||||
test.app > xrpl.protocol
|
||||
test.app > xrpl.rdb
|
||||
test.app > xrpl.resource
|
||||
test.app > xrpl.server
|
||||
test.basics > test.jtx
|
||||
test.basics > test.unit_test
|
||||
test.basics > xrpl.basics
|
||||
@@ -72,7 +67,6 @@ test.core > xrpl.basics
|
||||
test.core > xrpl.core
|
||||
test.core > xrpld.core
|
||||
test.core > xrpl.json
|
||||
test.core > xrpl.rdb
|
||||
test.core > xrpl.server
|
||||
test.csf > xrpl.basics
|
||||
test.csf > xrpld.consensus
|
||||
@@ -101,8 +95,8 @@ test.nodestore > test.jtx
|
||||
test.nodestore > test.toplevel
|
||||
test.nodestore > test.unit_test
|
||||
test.nodestore > xrpl.basics
|
||||
test.nodestore > xrpld.core
|
||||
test.nodestore > xrpl.nodestore
|
||||
test.nodestore > xrpl.rdb
|
||||
test.overlay > test.jtx
|
||||
test.overlay > test.toplevel
|
||||
test.overlay > test.unit_test
|
||||
@@ -159,8 +153,6 @@ tests.libxrpl > xrpl.json
|
||||
tests.libxrpl > xrpl.net
|
||||
xrpl.core > xrpl.basics
|
||||
xrpl.core > xrpl.json
|
||||
xrpl.core > xrpl.ledger
|
||||
xrpl.core > xrpl.protocol
|
||||
xrpl.json > xrpl.basics
|
||||
xrpl.ledger > xrpl.basics
|
||||
xrpl.ledger > xrpl.protocol
|
||||
@@ -169,17 +161,12 @@ xrpl.nodestore > xrpl.basics
|
||||
xrpl.nodestore > xrpl.protocol
|
||||
xrpl.protocol > xrpl.basics
|
||||
xrpl.protocol > xrpl.json
|
||||
xrpl.rdb > xrpl.basics
|
||||
xrpl.rdb > xrpl.core
|
||||
xrpl.rdb > xrpl.protocol
|
||||
xrpl.resource > xrpl.basics
|
||||
xrpl.resource > xrpl.json
|
||||
xrpl.resource > xrpl.protocol
|
||||
xrpl.server > xrpl.basics
|
||||
xrpl.server > xrpl.core
|
||||
xrpl.server > xrpl.json
|
||||
xrpl.server > xrpl.protocol
|
||||
xrpl.server > xrpl.rdb
|
||||
xrpl.shamap > xrpl.basics
|
||||
xrpl.shamap > xrpl.nodestore
|
||||
xrpl.shamap > xrpl.protocol
|
||||
@@ -188,15 +175,12 @@ xrpld.app > xrpl.basics
|
||||
xrpld.app > xrpl.core
|
||||
xrpld.app > xrpld.conditions
|
||||
xrpld.app > xrpld.consensus
|
||||
xrpld.app > xrpld.core
|
||||
xrpld.app > xrpl.json
|
||||
xrpld.app > xrpl.ledger
|
||||
xrpld.app > xrpl.net
|
||||
xrpld.app > xrpl.nodestore
|
||||
xrpld.app > xrpl.protocol
|
||||
xrpld.app > xrpl.rdb
|
||||
xrpld.app > xrpl.resource
|
||||
xrpld.app > xrpl.server
|
||||
xrpld.app > xrpl.shamap
|
||||
xrpld.conditions > xrpl.basics
|
||||
xrpld.conditions > xrpl.protocol
|
||||
@@ -208,20 +192,17 @@ xrpld.core > xrpl.core
|
||||
xrpld.core > xrpl.json
|
||||
xrpld.core > xrpl.net
|
||||
xrpld.core > xrpl.protocol
|
||||
xrpld.core > xrpl.rdb
|
||||
xrpld.overlay > xrpl.basics
|
||||
xrpld.overlay > xrpl.core
|
||||
xrpld.overlay > xrpld.core
|
||||
xrpld.overlay > xrpld.peerfinder
|
||||
xrpld.overlay > xrpl.json
|
||||
xrpld.overlay > xrpl.protocol
|
||||
xrpld.overlay > xrpl.rdb
|
||||
xrpld.overlay > xrpl.resource
|
||||
xrpld.overlay > xrpl.server
|
||||
xrpld.peerfinder > xrpl.basics
|
||||
xrpld.peerfinder > xrpld.core
|
||||
xrpld.peerfinder > xrpl.protocol
|
||||
xrpld.peerfinder > xrpl.rdb
|
||||
xrpld.perflog > xrpl.basics
|
||||
xrpld.perflog > xrpl.core
|
||||
xrpld.perflog > xrpld.rpc
|
||||
@@ -234,7 +215,6 @@ xrpld.rpc > xrpl.ledger
|
||||
xrpld.rpc > xrpl.net
|
||||
xrpld.rpc > xrpl.nodestore
|
||||
xrpld.rpc > xrpl.protocol
|
||||
xrpld.rpc > xrpl.rdb
|
||||
xrpld.rpc > xrpl.resource
|
||||
xrpld.rpc > xrpl.server
|
||||
xrpld.shamap > xrpl.shamap
|
||||
|
||||
30
.github/scripts/rename/include.sh
vendored
30
.github/scripts/rename/include.sh
vendored
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Exit the script as soon as an error occurs.
|
||||
set -e
|
||||
|
||||
# This script checks whether there are no new include guards introduced by a new
|
||||
# PR, as header files should use "#pragma once" instead. The script assumes any
|
||||
# include guards will use "XRPL_" as prefix.
|
||||
# Usage: .github/scripts/rename/include.sh <repository directory>
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "Usage: $0 <repository directory>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DIRECTORY=$1
|
||||
echo "Processing directory: ${DIRECTORY}"
|
||||
if [ ! -d "${DIRECTORY}" ]; then
|
||||
echo "Error: Directory '${DIRECTORY}' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find "${DIRECTORY}" -type f \( -name "*.h" -o -name "*.hpp" -o -name "*.ipp" \) | while read -r FILE; do
|
||||
echo "Processing file: ${FILE}"
|
||||
if grep -q "#ifndef XRPL_" "${FILE}"; then
|
||||
echo "Please replace all include guards by #pragma once."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
echo "Checking complete."
|
||||
84
.github/scripts/strategy-matrix/generate.py
vendored
84
.github/scripts/strategy-matrix/generate.py
vendored
@@ -51,20 +51,22 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
# Only generate a subset of configurations in PRs.
|
||||
if not all:
|
||||
# Debian:
|
||||
# - Bookworm using GCC 13: Release on linux/amd64, set the reference
|
||||
# fee to 500.
|
||||
# - Bookworm using GCC 15: Debug on linux/amd64, enable code
|
||||
# coverage (which will be done below).
|
||||
# - Bookworm using Clang 16: Debug on linux/arm64, enable voidstar.
|
||||
# - Bookworm using Clang 17: Release on linux/amd64, set the
|
||||
# reference fee to 1000.
|
||||
# - Bookworm using Clang 20: Debug on linux/amd64.
|
||||
# - Bookworm using GCC 13: Release and Unity on linux/amd64, set
|
||||
# the reference fee to 500.
|
||||
# - Bookworm using GCC 15: Debug and no Unity on linux/amd64, enable
|
||||
# code coverage (which will be done below).
|
||||
# - Bookworm using Clang 16: Debug and no Unity on linux/arm64,
|
||||
# enable voidstar.
|
||||
# - Bookworm using Clang 17: Release and no Unity on linux/amd64,
|
||||
# set the reference fee to 1000.
|
||||
# - Bookworm using Clang 20: Debug and Unity on linux/amd64.
|
||||
if os["distro_name"] == "debian":
|
||||
skip = True
|
||||
if os["distro_version"] == "bookworm":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-13"
|
||||
and build_type == "Release"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=500 {cmake_args}"
|
||||
@@ -72,12 +74,14 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-16"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
):
|
||||
cmake_args = f"-Dvoidstar=ON {cmake_args}"
|
||||
@@ -85,6 +89,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-17"
|
||||
and build_type == "Release"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"-DUNIT_TEST_REFERENCE_FEE=1000 {cmake_args}"
|
||||
@@ -92,6 +97,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
@@ -99,14 +105,15 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
continue
|
||||
|
||||
# RHEL:
|
||||
# - 9 using GCC 12: Debug on linux/amd64.
|
||||
# - 10 using Clang: Release on linux/amd64.
|
||||
# - 9 using GCC 12: Debug and Unity on linux/amd64.
|
||||
# - 10 using Clang: Release and no Unity on linux/amd64.
|
||||
if os["distro_name"] == "rhel":
|
||||
skip = True
|
||||
if os["distro_version"] == "9":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
@@ -114,6 +121,7 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-any"
|
||||
and build_type == "Release"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
@@ -121,16 +129,17 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
continue
|
||||
|
||||
# Ubuntu:
|
||||
# - Jammy using GCC 12: Debug on linux/arm64.
|
||||
# - Noble using GCC 14: Release on linux/amd64.
|
||||
# - Noble using Clang 18: Debug on linux/amd64.
|
||||
# - Noble using Clang 19: Release on linux/arm64.
|
||||
# - Jammy using GCC 12: Debug and no Unity on linux/arm64.
|
||||
# - Noble using GCC 14: Release and Unity on linux/amd64.
|
||||
# - Noble using Clang 18: Debug and no Unity on linux/amd64.
|
||||
# - Noble using Clang 19: Release and Unity on linux/arm64.
|
||||
if os["distro_name"] == "ubuntu":
|
||||
skip = True
|
||||
if os["distro_version"] == "jammy":
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
):
|
||||
skip = False
|
||||
@@ -138,18 +147,21 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-14"
|
||||
and build_type == "Release"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-18"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
skip = False
|
||||
if (
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "clang-19"
|
||||
and build_type == "Release"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "linux/arm64"
|
||||
):
|
||||
skip = False
|
||||
@@ -157,16 +169,20 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
continue
|
||||
|
||||
# MacOS:
|
||||
# - Debug on macos/arm64.
|
||||
# - Debug and no Unity on macos/arm64.
|
||||
if os["distro_name"] == "macos" and not (
|
||||
build_type == "Debug" and architecture["platform"] == "macos/arm64"
|
||||
build_type == "Debug"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "macos/arm64"
|
||||
):
|
||||
continue
|
||||
|
||||
# Windows:
|
||||
# - Release on windows/amd64.
|
||||
# - Release and Unity on windows/amd64.
|
||||
if os["distro_name"] == "windows" and not (
|
||||
build_type == "Release" and architecture["platform"] == "windows/amd64"
|
||||
build_type == "Release"
|
||||
and "-Dunity=ON" in cmake_args
|
||||
and architecture["platform"] == "windows/amd64"
|
||||
):
|
||||
continue
|
||||
|
||||
@@ -193,28 +209,18 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
):
|
||||
continue
|
||||
|
||||
# Enable code coverage for Debian Bookworm using GCC 15 in Debug on
|
||||
# linux/amd64
|
||||
# Enable code coverage for Debian Bookworm using GCC 15 in Debug and no
|
||||
# Unity on linux/amd64
|
||||
if (
|
||||
f"{os['distro_name']}-{os['distro_version']}" == "debian-bookworm"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
|
||||
f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-15"
|
||||
and build_type == "Debug"
|
||||
and "-Dunity=OFF" in cmake_args
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"{cmake_args} -Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0"
|
||||
|
||||
# Enable unity build for Ubuntu Jammy using GCC 12 in Debug on
|
||||
# linux/amd64.
|
||||
if (
|
||||
f"{os['distro_name']}-{os['distro_version']}" == "ubuntu-jammy"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "gcc-12"
|
||||
and build_type == "Debug"
|
||||
and architecture["platform"] == "linux/amd64"
|
||||
):
|
||||
cmake_args = f"{cmake_args} -Dunity=ON"
|
||||
cmake_args = f"-Dcoverage=ON -Dcoverage_format=xml -DCODE_COVERAGE_VERBOSE=ON -DCMAKE_C_FLAGS=-O0 -DCMAKE_CXX_FLAGS=-O0 {cmake_args}"
|
||||
|
||||
# Generate a unique name for the configuration, e.g. macos-arm64-debug
|
||||
# or debian-bookworm-gcc-12-amd64-release.
|
||||
# or debian-bookworm-gcc-12-amd64-release-unity.
|
||||
config_name = os["distro_name"]
|
||||
if (n := os["distro_version"]) != "":
|
||||
config_name += f"-{n}"
|
||||
@@ -236,10 +242,12 @@ def generate_strategy_matrix(all: bool, config: Config) -> list:
|
||||
# names get truncated.
|
||||
# Add Address and Thread (both coupled with UB) sanitizers for specific bookworm distros.
|
||||
# GCC-Asan rippled-embedded tests are failing because of https://github.com/google/sanitizers/issues/856
|
||||
if (
|
||||
os["distro_version"] == "bookworm"
|
||||
and f"{os['compiler_name']}-{os['compiler_version']}" == "clang-20"
|
||||
):
|
||||
if os[
|
||||
"distro_version"
|
||||
] == "bookworm" and f"{os['compiler_name']}-{os['compiler_version']}" in [
|
||||
"clang-20",
|
||||
"gcc-13",
|
||||
]:
|
||||
# Add ASAN + UBSAN configuration.
|
||||
configurations.append(
|
||||
{
|
||||
|
||||
2
.github/scripts/strategy-matrix/linux.json
vendored
2
.github/scripts/strategy-matrix/linux.json
vendored
@@ -208,5 +208,5 @@
|
||||
}
|
||||
],
|
||||
"build_type": ["Debug", "Release"],
|
||||
"cmake_args": [""]
|
||||
"cmake_args": ["-Dunity=OFF", "-Dunity=ON"]
|
||||
}
|
||||
|
||||
5
.github/scripts/strategy-matrix/macos.json
vendored
5
.github/scripts/strategy-matrix/macos.json
vendored
@@ -15,5 +15,8 @@
|
||||
}
|
||||
],
|
||||
"build_type": ["Debug", "Release"],
|
||||
"cmake_args": ["-DCMAKE_POLICY_VERSION_MINIMUM=3.5"]
|
||||
"cmake_args": [
|
||||
"-Dunity=OFF -DCMAKE_POLICY_VERSION_MINIMUM=3.5",
|
||||
"-Dunity=ON -DCMAKE_POLICY_VERSION_MINIMUM=3.5"
|
||||
]
|
||||
}
|
||||
|
||||
2
.github/scripts/strategy-matrix/windows.json
vendored
2
.github/scripts/strategy-matrix/windows.json
vendored
@@ -15,5 +15,5 @@
|
||||
}
|
||||
],
|
||||
"build_type": ["Debug", "Release"],
|
||||
"cmake_args": [""]
|
||||
"cmake_args": ["-Dunity=OFF", "-Dunity=ON"]
|
||||
}
|
||||
|
||||
20
.github/workflows/reusable-build-test-config.yml
vendored
20
.github/workflows/reusable-build-test-config.yml
vendored
@@ -205,14 +205,18 @@ jobs:
|
||||
- name: Set sanitizer options
|
||||
if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }}
|
||||
run: |
|
||||
echo "ASAN_OPTIONS=print_stacktrace=1:detect_container_overflow=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=second_deadlock_stack=1:halt_on_error=0:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
echo "ASAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" >> ${GITHUB_ENV}
|
||||
echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV}
|
||||
echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV}
|
||||
echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Run the separate tests
|
||||
# We continue on error here because we want to try the Embedded tests before
|
||||
# failing. This will give us details on all the failures at once.
|
||||
continue-on-error: true
|
||||
if: ${{ !inputs.build_only }}
|
||||
working-directory: ${{ env.BUILD_DIR }}
|
||||
id: separate_tests
|
||||
# Windows locks some of the build files while running tests, and parallel jobs can collide
|
||||
env:
|
||||
BUILD_TYPE: ${{ inputs.build_type }}
|
||||
@@ -228,8 +232,14 @@ jobs:
|
||||
working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }}
|
||||
env:
|
||||
BUILD_NPROC: ${{ steps.nproc.outputs.nproc }}
|
||||
PARALLELISM: ${{ env.SANITIZERS_ENABLED == 'true' && '1' || steps.nproc.outputs.nproc }}
|
||||
run: |
|
||||
./xrpld --unittest --unittest-jobs "${BUILD_NPROC}"
|
||||
./xrpld --unittest --unittest-jobs "${PARALLELISM}"
|
||||
|
||||
# Pipeline should fail if the separate tests failed.
|
||||
- name: Check results of the SeparateTests
|
||||
if: ${{ !inputs.build_only && steps.separate_tests.outcome == 'failure' }}
|
||||
run: exit 1
|
||||
|
||||
- name: Debug failure (Linux)
|
||||
if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }}
|
||||
|
||||
2
.github/workflows/reusable-check-rename.yml
vendored
2
.github/workflows/reusable-check-rename.yml
vendored
@@ -31,8 +31,6 @@ jobs:
|
||||
run: .github/scripts/rename/namespace.sh .
|
||||
- name: Check config name
|
||||
run: .github/scripts/rename/config.sh .
|
||||
- name: Check include guards
|
||||
run: .github/scripts/rename/include.sh .
|
||||
- name: Check for differences
|
||||
env:
|
||||
MESSAGE: |
|
||||
|
||||
38
BUILD.md
38
BUILD.md
@@ -368,36 +368,6 @@ The workaround for this error is to add two lines to your profile:
|
||||
tools.build:cxxflags=['-DBOOST_ASIO_DISABLE_CONCEPTS']
|
||||
```
|
||||
|
||||
### Set Up Ccache
|
||||
|
||||
To speed up repeated compilations, we recommend that you install
|
||||
[ccache](https://ccache.dev), a tool that wraps your compiler so that it can
|
||||
cache build objects locally.
|
||||
|
||||
#### Linux
|
||||
|
||||
You can install it using the package manager, e.g. `sudo apt install ccache`
|
||||
(Ubuntu) or `sudo dnf install ccache` (RHEL).
|
||||
|
||||
#### macOS
|
||||
|
||||
You can install it using Homebrew, i.e. `brew install ccache`.
|
||||
|
||||
#### Windows
|
||||
|
||||
You can install it using Chocolatey, i.e. `choco install ccache`. If you already
|
||||
have Ccache installed, then `choco upgrade ccache` will update it to the latest
|
||||
version. However, if you see an error such as:
|
||||
|
||||
```
|
||||
terminate called after throwing an instance of 'std::bad_alloc'
|
||||
what(): std::bad_alloc
|
||||
C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Microsoft\VC\v170\Microsoft.CppCommon.targets(617,5): error MSB6006: "cl.exe" exited with code 3.
|
||||
```
|
||||
|
||||
then please install a specific version of Ccache that we know works, via: `choco
|
||||
install ccache --version 4.11.3 --allow-downgrade`.
|
||||
|
||||
### Build and Test
|
||||
|
||||
1. Create a build directory and move into it.
|
||||
@@ -580,10 +550,10 @@ See [Sanitizers docs](./docs/build/sanitizers.md) for more details.
|
||||
| `werr` | OFF | Treat compilation warnings as errors |
|
||||
| `wextra` | OFF | Enable additional compilation warnings |
|
||||
|
||||
[Unity builds][5] may be faster for the first build (at the cost of much more
|
||||
memory) since they concatenate sources into fewer translation units. Non-unity
|
||||
builds may be faster for incremental builds, and can be helpful for detecting
|
||||
`#include` omissions.
|
||||
[Unity builds][5] may be faster for the first build
|
||||
(at the cost of much more memory) since they concatenate sources into fewer
|
||||
translation units. Non-unity builds may be faster for incremental builds,
|
||||
and can be helpful for detecting `#include` omissions.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
# Architecture Analysis
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Design Decisions](./02-design-decisions.md) | [Implementation Strategy](./03-implementation-strategy.md)
|
||||
|
||||
---
|
||||
|
||||
## 1.1 Current rippled Architecture Overview
|
||||
|
||||
The rippled node software consists of several interconnected components that need instrumentation for distributed tracing:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph rippled["rippled Node"]
|
||||
subgraph services["Core Services"]
|
||||
RPC["RPC Server<br/>(HTTP/WS/gRPC)"]
|
||||
Overlay["Overlay<br/>(P2P Network)"]
|
||||
Consensus["Consensus<br/>(RCLConsensus)"]
|
||||
end
|
||||
|
||||
JobQueue["JobQueue<br/>(Thread Pool)"]
|
||||
|
||||
subgraph processing["Processing Layer"]
|
||||
NetworkOPs["NetworkOPs<br/>(Tx Processing)"]
|
||||
LedgerMaster["LedgerMaster<br/>(Ledger Mgmt)"]
|
||||
NodeStore["NodeStore<br/>(Database)"]
|
||||
end
|
||||
|
||||
subgraph observability["Existing Observability"]
|
||||
PerfLog["PerfLog<br/>(JSON)"]
|
||||
Insight["Insight<br/>(StatsD)"]
|
||||
Logging["Logging<br/>(Journal)"]
|
||||
end
|
||||
|
||||
services --> JobQueue
|
||||
JobQueue --> processing
|
||||
end
|
||||
|
||||
style rippled fill:#f5f5f5,stroke:#333
|
||||
style services fill:#e3f2fd,stroke:#1976d2
|
||||
style processing fill:#e8f5e9,stroke:#388e3c
|
||||
style observability fill:#fff3e0,stroke:#f57c00
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.2 Key Components for Instrumentation
|
||||
|
||||
| Component | Location | Purpose | Trace Value |
|
||||
| ----------------- | ------------------------------------------ | ------------------------ | ---------------------------- |
|
||||
| **Overlay** | `src/xrpld/overlay/` | P2P communication | Message propagation timing |
|
||||
| **PeerImp** | `src/xrpld/overlay/detail/PeerImp.cpp` | Individual peer handling | Per-peer latency |
|
||||
| **RCLConsensus** | `src/xrpld/app/consensus/RCLConsensus.cpp` | Consensus algorithm | Round timing, phase analysis |
|
||||
| **NetworkOPs** | `src/xrpld/app/misc/NetworkOPs.cpp` | Transaction processing | Tx lifecycle tracking |
|
||||
| **ServerHandler** | `src/xrpld/rpc/detail/ServerHandler.cpp` | RPC entry point | Request latency |
|
||||
| **RPCHandler** | `src/xrpld/rpc/detail/RPCHandler.cpp` | Command execution | Per-command timing |
|
||||
| **JobQueue** | `src/xrpl/core/JobQueue.h` | Async task execution | Queue wait times |
|
||||
|
||||
---
|
||||
|
||||
## 1.3 Transaction Flow Diagram
|
||||
|
||||
Transaction flow spans multiple nodes in the network. Each node creates linked spans to form a distributed trace:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant PeerA as Peer A (Receive)
|
||||
participant PeerB as Peer B (Relay)
|
||||
participant PeerC as Peer C (Validate)
|
||||
|
||||
Client->>PeerA: 1. Submit TX
|
||||
|
||||
rect rgb(230, 245, 255)
|
||||
Note over PeerA: tx.receive SPAN START
|
||||
PeerA->>PeerA: HashRouter Deduplication
|
||||
PeerA->>PeerA: tx.validate (child span)
|
||||
end
|
||||
|
||||
PeerA->>PeerB: 2. Relay TX (with trace ctx)
|
||||
|
||||
rect rgb(230, 245, 255)
|
||||
Note over PeerB: tx.receive (linked span)
|
||||
end
|
||||
|
||||
PeerB->>PeerC: 3. Relay TX
|
||||
|
||||
rect rgb(230, 245, 255)
|
||||
Note over PeerC: tx.receive (linked span)
|
||||
PeerC->>PeerC: tx.process
|
||||
end
|
||||
|
||||
Note over Client,PeerC: DISTRIBUTED TRACE (same trace_id: abc123)
|
||||
```
|
||||
|
||||
### Trace Structure
|
||||
|
||||
```
|
||||
trace_id: abc123
|
||||
├── span: tx.receive (Peer A)
|
||||
│ ├── span: tx.validate
|
||||
│ └── span: tx.relay
|
||||
├── span: tx.receive (Peer B) [parent: Peer A]
|
||||
│ └── span: tx.relay
|
||||
└── span: tx.receive (Peer C) [parent: Peer B]
|
||||
└── span: tx.process
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.4 Consensus Round Flow
|
||||
|
||||
Consensus rounds are multi-phase operations that benefit significantly from tracing:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph round["consensus.round (root span)"]
|
||||
attrs["Attributes:<br/>xrpl.consensus.ledger.seq = 12345678<br/>xrpl.consensus.mode = proposing<br/>xrpl.consensus.proposers = 35"]
|
||||
|
||||
subgraph open["consensus.phase.open"]
|
||||
open_desc["Duration: ~3s<br/>Waiting for transactions"]
|
||||
end
|
||||
|
||||
subgraph establish["consensus.phase.establish"]
|
||||
est_attrs["proposals_received = 28<br/>disputes_resolved = 3"]
|
||||
est_children["├── consensus.proposal.receive (×28)<br/>├── consensus.proposal.send (×1)<br/>└── consensus.dispute.resolve (×3)"]
|
||||
end
|
||||
|
||||
subgraph accept["consensus.phase.accept"]
|
||||
acc_attrs["transactions_applied = 150<br/>ledger.hash = DEF456..."]
|
||||
acc_children["├── ledger.build<br/>└── ledger.validate"]
|
||||
end
|
||||
|
||||
attrs --> open
|
||||
open --> establish
|
||||
establish --> accept
|
||||
end
|
||||
|
||||
style round fill:#fff8e1,stroke:#ffc107
|
||||
style open fill:#e3f2fd,stroke:#1976d2
|
||||
style establish fill:#e8f5e9,stroke:#388e3c
|
||||
style accept fill:#fce4ec,stroke:#e91e63
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.5 RPC Request Flow
|
||||
|
||||
RPC requests support W3C Trace Context headers for distributed tracing across services:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph request["rpc.request (root span)"]
|
||||
http["HTTP Request<br/>POST /<br/>traceparent: 00-abc123...-def456...-01"]
|
||||
|
||||
attrs["Attributes:<br/>http.method = POST<br/>net.peer.ip = 192.168.1.100<br/>xrpl.rpc.command = submit"]
|
||||
|
||||
subgraph enqueue["jobqueue.enqueue"]
|
||||
job_attr["xrpl.job.type = jtCLIENT_RPC"]
|
||||
end
|
||||
|
||||
subgraph command["rpc.command.submit"]
|
||||
cmd_attrs["xrpl.rpc.version = 2<br/>xrpl.rpc.role = user"]
|
||||
cmd_children["├── tx.deserialize<br/>├── tx.validate_local<br/>└── tx.submit_to_network"]
|
||||
end
|
||||
|
||||
response["Response: 200 OK<br/>Duration: 45ms"]
|
||||
|
||||
http --> attrs
|
||||
attrs --> enqueue
|
||||
enqueue --> command
|
||||
command --> response
|
||||
end
|
||||
|
||||
style request fill:#e8f5e9,stroke:#388e3c
|
||||
style enqueue fill:#e3f2fd,stroke:#1976d2
|
||||
style command fill:#fff3e0,stroke:#ff9800
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.6 Key Trace Points
|
||||
|
||||
The following table identifies priority instrumentation points across the codebase:
|
||||
|
||||
| Category | Span Name | File | Method | Priority |
|
||||
| --------------- | ---------------------- | -------------------- | ---------------------- | -------- |
|
||||
| **Transaction** | `tx.receive` | `PeerImp.cpp` | `handleTransaction()` | High |
|
||||
| **Transaction** | `tx.validate` | `NetworkOPs.cpp` | `processTransaction()` | High |
|
||||
| **Transaction** | `tx.process` | `NetworkOPs.cpp` | `doTransactionSync()` | High |
|
||||
| **Transaction** | `tx.relay` | `OverlayImpl.cpp` | `relay()` | Medium |
|
||||
| **Consensus** | `consensus.round` | `RCLConsensus.cpp` | `startRound()` | High |
|
||||
| **Consensus** | `consensus.phase.*` | `Consensus.h` | `timerEntry()` | High |
|
||||
| **Consensus** | `consensus.proposal.*` | `RCLConsensus.cpp` | `peerProposal()` | Medium |
|
||||
| **RPC** | `rpc.request` | `ServerHandler.cpp` | `onRequest()` | High |
|
||||
| **RPC** | `rpc.command.*` | `RPCHandler.cpp` | `doCommand()` | High |
|
||||
| **Peer** | `peer.connect` | `OverlayImpl.cpp` | `onHandoff()` | Low |
|
||||
| **Peer** | `peer.message.*` | `PeerImp.cpp` | `onMessage()` | Low |
|
||||
| **Ledger** | `ledger.acquire` | `InboundLedgers.cpp` | `acquire()` | Medium |
|
||||
| **Ledger** | `ledger.build` | `RCLConsensus.cpp` | `buildLCL()` | High |
|
||||
|
||||
---
|
||||
|
||||
## 1.7 Instrumentation Priority
|
||||
|
||||
```mermaid
|
||||
quadrantChart
|
||||
title Instrumentation Priority Matrix
|
||||
x-axis Low Complexity --> High Complexity
|
||||
y-axis Low Value --> High Value
|
||||
quadrant-1 Implement First
|
||||
quadrant-2 Plan Carefully
|
||||
quadrant-3 Quick Wins
|
||||
quadrant-4 Consider Later
|
||||
|
||||
RPC Tracing: [0.3, 0.85]
|
||||
Transaction Tracing: [0.6, 0.95]
|
||||
Consensus Tracing: [0.75, 0.9]
|
||||
Peer Message Tracing: [0.4, 0.3]
|
||||
Ledger Acquisition: [0.5, 0.6]
|
||||
JobQueue Tracing: [0.35, 0.5]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1.8 Observable Outcomes
|
||||
|
||||
After implementing OpenTelemetry, operators and developers will gain visibility into the following:
|
||||
|
||||
### 1.8.1 What You Will See: Traces
|
||||
|
||||
| Trace Type | Description | Example Query in Grafana/Tempo |
|
||||
| -------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
|
||||
| **Transaction Lifecycle** | Full journey from RPC submission through validation, relay, consensus, and ledger inclusion | `{service.name="rippled" && xrpl.tx.hash="ABC123..."}` |
|
||||
| **Cross-Node Propagation** | Transaction path across multiple rippled nodes with timing | `{xrpl.tx.relay_count > 0}` |
|
||||
| **Consensus Rounds** | Complete round with all phases (open, establish, accept) | `{span.name=~"consensus.round.*"}` |
|
||||
| **RPC Request Processing** | Individual command execution with timing breakdown | `{xrpl.rpc.command="account_info"}` |
|
||||
| **Ledger Acquisition** | Peer-to-peer ledger data requests and responses | `{span.name="ledger.acquire"}` |
|
||||
|
||||
### 1.8.2 What You Will See: Metrics (Derived from Traces)
|
||||
|
||||
| Metric | Description | Dashboard Panel |
|
||||
| ----------------------------- | -------------------------------------- | --------------------------- |
|
||||
| **RPC Latency (p50/p95/p99)** | Response time distribution per command | Heatmap by command |
|
||||
| **Transaction Throughput** | Transactions processed per second | Time series graph |
|
||||
| **Consensus Round Duration** | Time to complete consensus phases | Histogram |
|
||||
| **Cross-Node Latency** | Time for transaction to reach N nodes | Line chart with percentiles |
|
||||
| **Error Rate** | Failed transactions/RPC calls by type | Stacked bar chart |
|
||||
|
||||
### 1.8.3 Concrete Dashboard Examples
|
||||
|
||||
**Transaction Trace View (Jaeger/Tempo):**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ Trace: abc123... (Transaction Submission) Duration: 847ms │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ├── rpc.request [ServerHandler] ████░░░░░░ 45ms │
|
||||
│ │ └── rpc.command.submit [RPCHandler] ████░░░░░░ 42ms │
|
||||
│ │ └── tx.receive [NetworkOPs] ███░░░░░░░ 35ms │
|
||||
│ │ ├── tx.validate [TxQ] █░░░░░░░░░ 8ms │
|
||||
│ │ └── tx.relay [Overlay] ██░░░░░░░░ 15ms │
|
||||
│ │ ├── tx.receive [Node-B] █████░░░░░ 52ms │
|
||||
│ │ │ └── tx.relay [Node-B] ██░░░░░░░░ 18ms │
|
||||
│ │ └── tx.receive [Node-C] ██████░░░░ 65ms │
|
||||
│ └── consensus.round [RCLConsensus] ████████░░ 720ms │
|
||||
│ ├── consensus.phase.open ██░░░░░░░░ 180ms │
|
||||
│ ├── consensus.phase.establish █████░░░░░ 480ms │
|
||||
│ └── consensus.phase.accept █░░░░░░░░░ 60ms │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**RPC Performance Dashboard Panel:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ RPC Command Latency (Last 1 Hour) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ Command │ p50 │ p95 │ p99 │ Errors │ Rate │
|
||||
│──────────────────┼────────┼────────┼────────┼────────┼──────│
|
||||
│ account_info │ 12ms │ 45ms │ 89ms │ 0.1% │ 150/s│
|
||||
│ submit │ 35ms │ 120ms │ 250ms │ 2.3% │ 45/s│
|
||||
│ ledger │ 8ms │ 25ms │ 55ms │ 0.0% │ 80/s│
|
||||
│ tx │ 15ms │ 50ms │ 100ms │ 0.5% │ 60/s│
|
||||
│ server_info │ 5ms │ 12ms │ 20ms │ 0.0% │ 200/s│
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Consensus Health Dashboard Panel:**
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ Consensus Round Duration (Last 24 Hours) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 5s ┤ * │
|
||||
│ │ * * * │
|
||||
│ 4s ┤ * ** * * │
|
||||
│ │ * * * * ** * │
|
||||
│ 3s ┤ * * * * * * * * │
|
||||
│ │ * * * * * * * * * │
|
||||
│ 2s ┤* ** * * ** * * * * │
|
||||
│ │ ** ** ** │
|
||||
│ 1s ┤────────────────────────────────────────────────── │
|
||||
│ └──────────────────────────────────────────────────── │
|
||||
│ 00:00 04:00 08:00 12:00 16:00 20:00 24:00 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.8.4 Operator Actionable Insights
|
||||
|
||||
| Scenario | What You'll See | Action |
|
||||
| --------------------- | ---------------------------------------------------------------------------- | -------------------------------- |
|
||||
| **Slow RPC** | Span showing which phase is slow (parsing, execution, serialization) | Optimize specific code path |
|
||||
| **Transaction Stuck** | Trace stops at validation; error attribute shows reason | Fix transaction parameters |
|
||||
| **Consensus Delay** | Phase.establish taking too long; proposer attribute shows missing validators | Investigate network connectivity |
|
||||
| **Memory Spike** | Large batch of spans correlating with memory increase | Tune batch_size or sampling |
|
||||
| **Network Partition** | Traces missing cross-node links for specific peer | Check peer connectivity |
|
||||
|
||||
### 1.8.5 Developer Debugging Workflow
|
||||
|
||||
1. **Find Transaction**: Query by `xrpl.tx.hash` to get full trace
|
||||
2. **Identify Bottleneck**: Look at span durations to find slowest component
|
||||
3. **Check Attributes**: Review `xrpl.tx.validity`, `xrpl.rpc.status` for errors
|
||||
4. **Correlate Logs**: Use `trace_id` to find related PerfLog entries
|
||||
5. **Compare Nodes**: Filter by `service.instance.id` to compare behavior across nodes
|
||||
|
||||
---
|
||||
|
||||
*Next: [Design Decisions](./02-design-decisions.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,319 +0,0 @@
|
||||
# Design Decisions
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Architecture Analysis](./01-architecture-analysis.md) | [Code Samples](./04-code-samples.md)
|
||||
|
||||
---
|
||||
|
||||
## 2.1 OpenTelemetry Components
|
||||
|
||||
### 2.1.1 SDK Selection
|
||||
|
||||
**Primary Choice**: OpenTelemetry C++ SDK (`opentelemetry-cpp`)
|
||||
|
||||
| Component | Purpose | Required |
|
||||
| --------------------------------------- | ---------------------- | ----------- |
|
||||
| `opentelemetry-cpp::api` | Tracing API headers | Yes |
|
||||
| `opentelemetry-cpp::sdk` | SDK implementation | Yes |
|
||||
| `opentelemetry-cpp::ext` | Extensions (exporters) | Yes |
|
||||
| `opentelemetry-cpp::otlp_grpc_exporter` | OTLP/gRPC export | Recommended |
|
||||
| `opentelemetry-cpp::otlp_http_exporter` | OTLP/HTTP export | Alternative |
|
||||
|
||||
### 2.1.2 Instrumentation Strategy
|
||||
|
||||
**Manual Instrumentation** (recommended):
|
||||
|
||||
| Approach | Pros | Cons |
|
||||
| ---------- | ----------------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| **Manual** | Precise control, optimized placement, rippled-specific attributes | More development effort |
|
||||
| **Auto** | Less code, automatic coverage | Less control, potential overhead, limited customization |
|
||||
|
||||
---
|
||||
|
||||
## 2.2 Exporter Configuration
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph nodes["rippled Nodes"]
|
||||
node1["rippled<br/>Node 1"]
|
||||
node2["rippled<br/>Node 2"]
|
||||
node3["rippled<br/>Node 3"]
|
||||
end
|
||||
|
||||
collector["OpenTelemetry<br/>Collector<br/>(sidecar or standalone)"]
|
||||
|
||||
subgraph backends["Observability Backends"]
|
||||
jaeger["Jaeger<br/>(Dev)"]
|
||||
tempo["Tempo<br/>(Prod)"]
|
||||
elastic["Elastic<br/>APM"]
|
||||
end
|
||||
|
||||
node1 -->|"OTLP/gRPC<br/>:4317"| collector
|
||||
node2 -->|"OTLP/gRPC<br/>:4317"| collector
|
||||
node3 -->|"OTLP/gRPC<br/>:4317"| collector
|
||||
|
||||
collector --> jaeger
|
||||
collector --> tempo
|
||||
collector --> elastic
|
||||
|
||||
style nodes fill:#e3f2fd,stroke:#1976d2
|
||||
style backends fill:#e8f5e9,stroke:#388e3c
|
||||
style collector fill:#fff3e0,stroke:#ff9800
|
||||
```
|
||||
|
||||
### 2.2.1 OTLP/gRPC (Recommended)
|
||||
|
||||
```cpp
|
||||
// Configuration for OTLP over gRPC
|
||||
namespace otlp = opentelemetry::exporter::otlp;
|
||||
|
||||
otlp::OtlpGrpcExporterOptions opts;
|
||||
opts.endpoint = "localhost:4317";
|
||||
opts.use_ssl_credentials = true;
|
||||
opts.ssl_credentials_cacert_path = "/path/to/ca.crt";
|
||||
```
|
||||
|
||||
### 2.2.2 OTLP/HTTP (Alternative)
|
||||
|
||||
```cpp
|
||||
// Configuration for OTLP over HTTP
|
||||
namespace otlp = opentelemetry::exporter::otlp;
|
||||
|
||||
otlp::OtlpHttpExporterOptions opts;
|
||||
opts.url = "http://localhost:4318/v1/traces";
|
||||
opts.content_type = otlp::HttpRequestContentType::kJson; // or kBinary
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2.3 Span Naming Conventions
|
||||
|
||||
### 2.3.1 Naming Schema
|
||||
|
||||
```
|
||||
<component>.<operation>[.<sub-operation>]
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
- `tx.receive` - Transaction received from peer
|
||||
- `consensus.phase.establish` - Consensus establish phase
|
||||
- `rpc.command.server_info` - server_info RPC command
|
||||
|
||||
### 2.3.2 Complete Span Catalog
|
||||
|
||||
```yaml
|
||||
# Transaction Spans
|
||||
tx:
|
||||
receive: "Transaction received from network"
|
||||
validate: "Transaction signature/format validation"
|
||||
process: "Full transaction processing"
|
||||
relay: "Transaction relay to peers"
|
||||
apply: "Apply transaction to ledger"
|
||||
|
||||
# Consensus Spans
|
||||
consensus:
|
||||
round: "Complete consensus round"
|
||||
phase:
|
||||
open: "Open phase - collecting transactions"
|
||||
establish: "Establish phase - reaching agreement"
|
||||
accept: "Accept phase - applying consensus"
|
||||
proposal:
|
||||
receive: "Receive peer proposal"
|
||||
send: "Send our proposal"
|
||||
validation:
|
||||
receive: "Receive peer validation"
|
||||
send: "Send our validation"
|
||||
|
||||
# RPC Spans
|
||||
rpc:
|
||||
request: "HTTP/WebSocket request handling"
|
||||
command:
|
||||
"*": "Specific RPC command (dynamic)"
|
||||
|
||||
# Peer Spans
|
||||
peer:
|
||||
connect: "Peer connection establishment"
|
||||
disconnect: "Peer disconnection"
|
||||
message:
|
||||
send: "Send protocol message"
|
||||
receive: "Receive protocol message"
|
||||
|
||||
# Ledger Spans
|
||||
ledger:
|
||||
acquire: "Ledger acquisition from network"
|
||||
build: "Build new ledger"
|
||||
validate: "Ledger validation"
|
||||
close: "Close ledger"
|
||||
|
||||
# Job Spans
|
||||
job:
|
||||
enqueue: "Job added to queue"
|
||||
execute: "Job execution"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2.4 Attribute Schema
|
||||
|
||||
### 2.4.1 Resource Attributes (Set Once at Startup)
|
||||
|
||||
```cpp
|
||||
// Standard OpenTelemetry semantic conventions
|
||||
resource::SemanticConventions::SERVICE_NAME = "rippled"
|
||||
resource::SemanticConventions::SERVICE_VERSION = BuildInfo::getVersionString()
|
||||
resource::SemanticConventions::SERVICE_INSTANCE_ID = <node_public_key_base58>
|
||||
|
||||
// Custom rippled attributes
|
||||
"xrpl.network.id" = <network_id> // e.g., 0 for mainnet
|
||||
"xrpl.network.type" = "mainnet" | "testnet" | "devnet" | "standalone"
|
||||
"xrpl.node.type" = "validator" | "stock" | "reporting"
|
||||
"xrpl.node.cluster" = <cluster_name> // If clustered
|
||||
```
|
||||
|
||||
### 2.4.2 Span Attributes by Category
|
||||
|
||||
#### Transaction Attributes
|
||||
```cpp
|
||||
"xrpl.tx.hash" = string // Transaction hash (hex)
|
||||
"xrpl.tx.type" = string // "Payment", "OfferCreate", etc.
|
||||
"xrpl.tx.account" = string // Source account (redacted in prod)
|
||||
"xrpl.tx.sequence" = int64 // Account sequence number
|
||||
"xrpl.tx.fee" = int64 // Fee in drops
|
||||
"xrpl.tx.result" = string // "tesSUCCESS", "tecPATH_DRY", etc.
|
||||
"xrpl.tx.ledger_index" = int64 // Ledger containing transaction
|
||||
```
|
||||
|
||||
#### Consensus Attributes
|
||||
```cpp
|
||||
"xrpl.consensus.round" = int64 // Round number
|
||||
"xrpl.consensus.phase" = string // "open", "establish", "accept"
|
||||
"xrpl.consensus.mode" = string // "proposing", "observing", etc.
|
||||
"xrpl.consensus.proposers" = int64 // Number of proposers
|
||||
"xrpl.consensus.ledger.prev" = string // Previous ledger hash
|
||||
"xrpl.consensus.ledger.seq" = int64 // Ledger sequence
|
||||
"xrpl.consensus.tx_count" = int64 // Transactions in consensus set
|
||||
"xrpl.consensus.duration_ms" = float64 // Round duration
|
||||
```
|
||||
|
||||
#### RPC Attributes
|
||||
```cpp
|
||||
"xrpl.rpc.command" = string // Command name
|
||||
"xrpl.rpc.version" = int64 // API version
|
||||
"xrpl.rpc.role" = string // "admin" or "user"
|
||||
"xrpl.rpc.params" = string // Sanitized parameters (optional)
|
||||
```
|
||||
|
||||
#### Peer & Message Attributes
|
||||
```cpp
|
||||
"xrpl.peer.id" = string // Peer public key (base58)
|
||||
"xrpl.peer.address" = string // IP:port
|
||||
"xrpl.peer.latency_ms" = float64 // Measured latency
|
||||
"xrpl.peer.cluster" = string // Cluster name if clustered
|
||||
"xrpl.message.type" = string // Protocol message type name
|
||||
"xrpl.message.size_bytes" = int64 // Message size
|
||||
"xrpl.message.compressed" = bool // Whether compressed
|
||||
```
|
||||
|
||||
#### Ledger & Job Attributes
|
||||
```cpp
|
||||
"xrpl.ledger.hash" = string // Ledger hash
|
||||
"xrpl.ledger.index" = int64 // Ledger sequence/index
|
||||
"xrpl.ledger.close_time" = int64 // Close time (epoch)
|
||||
"xrpl.ledger.tx_count" = int64 // Transaction count
|
||||
"xrpl.job.type" = string // Job type name
|
||||
"xrpl.job.queue_ms" = float64 // Time spent in queue
|
||||
"xrpl.job.worker" = int64 // Worker thread ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2.5 Context Propagation Design
|
||||
|
||||
### 2.5.1 Propagation Boundaries
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph http["HTTP/WebSocket (RPC)"]
|
||||
w3c["W3C Trace Context Headers:<br/>traceparent: 00-{trace_id}-{span_id}-{flags}<br/>tracestate: rippled=<state>"]
|
||||
end
|
||||
|
||||
subgraph protobuf["Protocol Buffers (P2P)"]
|
||||
proto["message TraceContext {<br/> bytes trace_id = 1; // 16 bytes<br/> bytes span_id = 2; // 8 bytes<br/> uint32 trace_flags = 3;<br/> string trace_state = 4;<br/>}"]
|
||||
end
|
||||
|
||||
subgraph jobqueue["JobQueue (Internal Async)"]
|
||||
job["Context captured at job creation,<br/>restored at execution<br/><br/>class Job {<br/> opentelemetry::context::Context traceContext_;<br/>};"]
|
||||
end
|
||||
|
||||
style http fill:#e3f2fd,stroke:#1976d2
|
||||
style protobuf fill:#e8f5e9,stroke:#388e3c
|
||||
style jobqueue fill:#fff3e0,stroke:#ff9800
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2.6 Integration with Existing Observability
|
||||
|
||||
### 2.6.1 Coexistence Strategy
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph rippled["rippled Process"]
|
||||
perflog["PerfLog<br/>(JSON to file)"]
|
||||
insight["Beast Insight<br/>(StatsD)"]
|
||||
otel["OpenTelemetry<br/>(Tracing)"]
|
||||
end
|
||||
|
||||
perflog --> perffile["perf.log"]
|
||||
insight --> statsd["StatsD Server"]
|
||||
otel --> collector["OTLP Collector"]
|
||||
|
||||
perffile --> grafana["Grafana<br/>(Unified UI)"]
|
||||
statsd --> grafana
|
||||
collector --> grafana
|
||||
|
||||
style rippled fill:#f5f5f5,stroke:#333
|
||||
style grafana fill:#ff9800,stroke:#e65100
|
||||
```
|
||||
|
||||
### 2.6.2 Correlation with PerfLog
|
||||
|
||||
Trace IDs can be correlated with existing PerfLog entries for comprehensive debugging:
|
||||
|
||||
```cpp
|
||||
// In RPCHandler.cpp - correlate trace with PerfLog
|
||||
Status doCommand(RPC::JsonContext& context, Json::Value& result)
|
||||
{
|
||||
// Start OpenTelemetry span
|
||||
auto span = context.app.getTelemetry().startSpan(
|
||||
"rpc.command." + context.method);
|
||||
|
||||
// Get trace ID for correlation
|
||||
auto traceId = span->GetContext().trace_id().IsValid()
|
||||
? toHex(span->GetContext().trace_id())
|
||||
: "";
|
||||
|
||||
// Use existing PerfLog with trace correlation
|
||||
auto const curId = context.app.getPerfLog().currentId();
|
||||
context.app.getPerfLog().rpcStart(context.method, curId);
|
||||
|
||||
// Future: Add trace ID to PerfLog entry
|
||||
// context.app.getPerfLog().setTraceId(curId, traceId);
|
||||
|
||||
try {
|
||||
auto ret = handler(context, result);
|
||||
context.app.getPerfLog().rpcFinish(context.method, curId);
|
||||
span->SetStatus(opentelemetry::trace::StatusCode::kOk);
|
||||
return ret;
|
||||
} catch (std::exception const& e) {
|
||||
context.app.getPerfLog().rpcError(context.method, curId);
|
||||
span->RecordException(e);
|
||||
span->SetStatus(opentelemetry::trace::StatusCode::kError, e.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Architecture Analysis](./01-architecture-analysis.md)* | *Next: [Implementation Strategy](./03-implementation-strategy.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,427 +0,0 @@
|
||||
# Implementation Strategy
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Code Samples](./04-code-samples.md) | [Configuration Reference](./05-configuration-reference.md)
|
||||
|
||||
---
|
||||
|
||||
## 3.1 Directory Structure
|
||||
|
||||
The telemetry implementation follows rippled's existing code organization pattern:
|
||||
|
||||
```
|
||||
include/xrpl/
|
||||
├── telemetry/
|
||||
│ ├── Telemetry.h # Main telemetry interface
|
||||
│ ├── TelemetryConfig.h # Configuration structures
|
||||
│ ├── TraceContext.h # Context propagation utilities
|
||||
│ ├── SpanGuard.h # RAII span management
|
||||
│ └── SpanAttributes.h # Attribute helper functions
|
||||
|
||||
src/libxrpl/
|
||||
├── telemetry/
|
||||
│ ├── Telemetry.cpp # Implementation
|
||||
│ ├── TelemetryConfig.cpp # Config parsing
|
||||
│ ├── TraceContext.cpp # Context serialization
|
||||
│ └── NullTelemetry.cpp # No-op implementation
|
||||
|
||||
src/xrpld/
|
||||
├── telemetry/
|
||||
│ ├── TracingInstrumentation.h # Instrumentation macros
|
||||
│ └── TracingInstrumentation.cpp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.2 Implementation Approach
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph phase1["Phase 1: Core"]
|
||||
sdk["SDK Integration"]
|
||||
interface["Telemetry Interface"]
|
||||
config["Configuration"]
|
||||
end
|
||||
|
||||
subgraph phase2["Phase 2: RPC"]
|
||||
http["HTTP Context"]
|
||||
rpc["RPC Handlers"]
|
||||
end
|
||||
|
||||
subgraph phase3["Phase 3: P2P"]
|
||||
proto["Protobuf Context"]
|
||||
tx["Transaction Relay"]
|
||||
end
|
||||
|
||||
subgraph phase4["Phase 4: Consensus"]
|
||||
consensus["Consensus Rounds"]
|
||||
proposals["Proposals"]
|
||||
end
|
||||
|
||||
phase1 --> phase2 --> phase3 --> phase4
|
||||
|
||||
style phase1 fill:#e3f2fd,stroke:#1976d2
|
||||
style phase2 fill:#e8f5e9,stroke:#388e3c
|
||||
style phase3 fill:#fff3e0,stroke:#ff9800
|
||||
style phase4 fill:#fce4ec,stroke:#e91e63
|
||||
```
|
||||
|
||||
### Key Principles
|
||||
|
||||
1. **Minimal Intrusion**: Instrumentation should not alter existing control flow
|
||||
2. **Zero-Cost When Disabled**: Use compile-time flags and no-op implementations
|
||||
3. **Backward Compatibility**: Protocol Buffer extensions use high field numbers
|
||||
4. **Graceful Degradation**: Tracing failures must not affect node operation
|
||||
|
||||
---
|
||||
|
||||
## 3.3 Performance Overhead Summary
|
||||
|
||||
| Metric | Overhead | Notes |
|
||||
| ------------- | ---------- | ----------------------------------- |
|
||||
| CPU | 1-3% | Span creation and attribute setting |
|
||||
| Memory | 2-5 MB | Batch buffer for pending spans |
|
||||
| Network | 10-50 KB/s | Compressed OTLP export to collector |
|
||||
| Latency (p99) | <2% | With proper sampling configuration |
|
||||
|
||||
---
|
||||
|
||||
## 3.4 Detailed CPU Overhead Analysis
|
||||
|
||||
### 3.4.1 Per-Operation Costs
|
||||
|
||||
| Operation | Time (ns) | Frequency | Impact |
|
||||
| --------------------- | --------- | ---------------------- | ---------- |
|
||||
| Span creation | 200-500 | Every traced operation | Low |
|
||||
| Span end | 100-200 | Every traced operation | Low |
|
||||
| SetAttribute (string) | 80-120 | 3-5 per span | Low |
|
||||
| SetAttribute (int) | 40-60 | 2-3 per span | Negligible |
|
||||
| AddEvent | 50-80 | 0-2 per span | Negligible |
|
||||
| Context injection | 150-250 | Per outgoing message | Low |
|
||||
| Context extraction | 100-180 | Per incoming message | Low |
|
||||
| GetCurrent context | 10-20 | Thread-local access | Negligible |
|
||||
|
||||
### 3.4.2 Transaction Processing Overhead
|
||||
|
||||
```mermaid
|
||||
pie title Transaction Tracing Overhead (~2.4μs total)
|
||||
"tx.receive span" : 800
|
||||
"tx.validate span" : 500
|
||||
"tx.relay span" : 500
|
||||
"Context injection (×3)" : 600
|
||||
```
|
||||
|
||||
**Overhead percentage**: 2.4 μs / 200 μs (avg tx processing) = **~1.2%**
|
||||
|
||||
### 3.4.3 Consensus Round Overhead
|
||||
|
||||
| Operation | Count | Cost (ns) | Total |
|
||||
| ---------------------- | ----- | --------- | ---------- |
|
||||
| consensus.round span | 1 | ~1000 | ~1 μs |
|
||||
| consensus.phase spans | 3 | ~700 | ~2.1 μs |
|
||||
| proposal.receive spans | ~20 | ~600 | ~12 μs |
|
||||
| proposal.send spans | ~3 | ~600 | ~1.8 μs |
|
||||
| Context operations | ~30 | ~200 | ~6 μs |
|
||||
| **TOTAL** | | | **~23 μs** |
|
||||
|
||||
**Overhead percentage**: 23 μs / 3s (typical round) = **~0.0008%** (negligible)
|
||||
|
||||
### 3.4.4 RPC Request Overhead
|
||||
|
||||
| Operation | Cost (ns) |
|
||||
| ---------------- | ------------ |
|
||||
| rpc.request span | ~700 |
|
||||
| rpc.command span | ~600 |
|
||||
| Context extract | ~250 |
|
||||
| Context inject | ~200 |
|
||||
| **TOTAL** | **~1.75 μs** |
|
||||
|
||||
- Fast RPC (1ms): 1.75 μs / 1ms = **~0.175%**
|
||||
- Slow RPC (100ms): 1.75 μs / 100ms = **~0.002%**
|
||||
|
||||
---
|
||||
|
||||
## 3.5 Memory Overhead Analysis
|
||||
|
||||
### 3.5.1 Static Memory
|
||||
|
||||
| Component | Size | Allocated |
|
||||
| ------------------------ | ----------- | ---------- |
|
||||
| TracerProvider singleton | ~64 KB | At startup |
|
||||
| BatchSpanProcessor | ~128 KB | At startup |
|
||||
| OTLP exporter | ~256 KB | At startup |
|
||||
| Propagator registry | ~8 KB | At startup |
|
||||
| **Total static** | **~456 KB** | |
|
||||
|
||||
### 3.5.2 Dynamic Memory
|
||||
|
||||
| Component | Size per unit | Max units | Peak |
|
||||
| -------------------- | ------------- | ---------- | ----------- |
|
||||
| Active span | ~200 bytes | 1000 | ~200 KB |
|
||||
| Queued span (export) | ~500 bytes | 2048 | ~1 MB |
|
||||
| Attribute storage | ~50 bytes | 5 per span | Included |
|
||||
| Context storage | ~64 bytes | Per thread | ~6.4 KB |
|
||||
| **Total dynamic** | | | **~1.2 MB** |
|
||||
|
||||
### 3.5.3 Memory Growth Characteristics
|
||||
|
||||
```mermaid
|
||||
xychart-beta
|
||||
title "Memory Usage vs Span Rate"
|
||||
x-axis "Spans/second" [0, 200, 400, 600, 800, 1000]
|
||||
y-axis "Memory (MB)" 0 --> 6
|
||||
line [1, 1.8, 2.6, 3.4, 4.2, 5]
|
||||
```
|
||||
|
||||
**Notes**:
|
||||
- Memory increases linearly with span rate
|
||||
- Batch export prevents unbounded growth
|
||||
- Queue size is configurable (default 2048 spans)
|
||||
- At queue limit, oldest spans are dropped (not blocked)
|
||||
|
||||
---
|
||||
|
||||
## 3.6 Network Overhead Analysis
|
||||
|
||||
### 3.6.1 Export Bandwidth
|
||||
|
||||
| Sampling Rate | Spans/sec | Bandwidth | Notes |
|
||||
| ------------- | --------- | --------- | ---------------- |
|
||||
| 100% | ~500 | ~250 KB/s | Development only |
|
||||
| 10% | ~50 | ~25 KB/s | Staging |
|
||||
| 1% | ~5 | ~2.5 KB/s | Production |
|
||||
| Error-only | ~1 | ~0.5 KB/s | Minimal overhead |
|
||||
|
||||
### 3.6.2 Trace Context Propagation
|
||||
|
||||
| Message Type | Context Size | Messages/sec | Overhead |
|
||||
| ---------------------- | ------------ | ------------ | ----------- |
|
||||
| TMTransaction | 32 bytes | ~100 | ~3.2 KB/s |
|
||||
| TMProposeSet | 32 bytes | ~10 | ~320 B/s |
|
||||
| TMValidation | 32 bytes | ~50 | ~1.6 KB/s |
|
||||
| **Total P2P overhead** | | | **~5 KB/s** |
|
||||
|
||||
---
|
||||
|
||||
## 3.7 Optimization Strategies
|
||||
|
||||
### 3.7.1 Sampling Strategies
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
trace["New Trace"]
|
||||
|
||||
trace --> errors{"Is Error?"}
|
||||
errors -->|Yes| sample["SAMPLE"]
|
||||
errors -->|No| consensus{"Is Consensus?"}
|
||||
|
||||
consensus -->|Yes| sample
|
||||
consensus -->|No| slow{"Is Slow?"}
|
||||
|
||||
slow -->|Yes| sample
|
||||
slow -->|No| prob{"Random < 10%?"}
|
||||
|
||||
prob -->|Yes| sample
|
||||
prob -->|No| drop["DROP"]
|
||||
|
||||
style sample fill:#4caf50,stroke:#388e3c,color:#fff
|
||||
style drop fill:#f44336,stroke:#c62828,color:#fff
|
||||
```
|
||||
|
||||
### 3.7.2 Batch Tuning Recommendations
|
||||
|
||||
| Environment | Batch Size | Batch Delay | Max Queue |
|
||||
| ------------------ | ---------- | ----------- | --------- |
|
||||
| Low-latency | 128 | 1000ms | 512 |
|
||||
| High-throughput | 1024 | 10000ms | 8192 |
|
||||
| Memory-constrained | 256 | 2000ms | 512 |
|
||||
|
||||
### 3.7.3 Conditional Instrumentation
|
||||
|
||||
```cpp
|
||||
// Compile-time feature flag
|
||||
#ifndef XRPL_ENABLE_TELEMETRY
|
||||
// Zero-cost when disabled
|
||||
#define XRPL_TRACE_SPAN(t, n) ((void)0)
|
||||
#endif
|
||||
|
||||
// Runtime component filtering
|
||||
if (telemetry.shouldTracePeer())
|
||||
{
|
||||
XRPL_TRACE_SPAN(telemetry, "peer.message.receive");
|
||||
// ... instrumentation
|
||||
}
|
||||
// No overhead when component tracing disabled
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.8 Links to Detailed Documentation
|
||||
|
||||
- **[Code Samples](./04-code-samples.md)**: Complete implementation code for all components
|
||||
- **[Configuration Reference](./05-configuration-reference.md)**: Configuration options and collector setup
|
||||
- **[Implementation Phases](./06-implementation-phases.md)**: Detailed timeline and milestones
|
||||
|
||||
---
|
||||
|
||||
## 3.9 Code Intrusiveness Assessment
|
||||
|
||||
This section provides a detailed assessment of how intrusive the OpenTelemetry integration is to the existing rippled codebase.
|
||||
|
||||
### 3.9.1 Files Modified Summary
|
||||
|
||||
| Component | Files Modified | Lines Added | Lines Changed | Architectural Impact |
|
||||
| --------------------- | -------------- | ----------- | ------------- | -------------------- |
|
||||
| **Core Telemetry** | 5 new files | ~800 | 0 | None (new module) |
|
||||
| **Application Init** | 2 files | ~30 | ~5 | Minimal |
|
||||
| **RPC Layer** | 3 files | ~80 | ~20 | Minimal |
|
||||
| **Transaction Relay** | 4 files | ~120 | ~40 | Low |
|
||||
| **Consensus** | 3 files | ~100 | ~30 | Low-Medium |
|
||||
| **Protocol Buffers** | 1 file | ~25 | 0 | Low |
|
||||
| **CMake/Build** | 3 files | ~50 | ~10 | Minimal |
|
||||
| **Total** | **~21 files** | **~1,205** | **~105** | **Low** |
|
||||
|
||||
### 3.9.2 Detailed File Impact
|
||||
|
||||
```mermaid
|
||||
pie title Code Changes by Component
|
||||
"New Telemetry Module" : 800
|
||||
"Transaction Relay" : 160
|
||||
"Consensus" : 130
|
||||
"RPC Layer" : 100
|
||||
"Application Init" : 35
|
||||
"Protocol Buffers" : 25
|
||||
"Build System" : 60
|
||||
```
|
||||
|
||||
#### New Files (No Impact on Existing Code)
|
||||
|
||||
| File | Lines | Purpose |
|
||||
| ---------------------------------------------- | ----- | -------------------- |
|
||||
| `include/xrpl/telemetry/Telemetry.h` | ~160 | Main interface |
|
||||
| `include/xrpl/telemetry/SpanGuard.h` | ~120 | RAII wrapper |
|
||||
| `include/xrpl/telemetry/TraceContext.h` | ~80 | Context propagation |
|
||||
| `src/xrpld/telemetry/TracingInstrumentation.h` | ~60 | Macros |
|
||||
| `src/libxrpl/telemetry/Telemetry.cpp` | ~200 | Implementation |
|
||||
| `src/libxrpl/telemetry/TelemetryConfig.cpp` | ~60 | Config parsing |
|
||||
| `src/libxrpl/telemetry/NullTelemetry.cpp` | ~40 | No-op implementation |
|
||||
|
||||
#### Modified Files (Existing Rippled Code)
|
||||
|
||||
| File | Lines Added | Lines Changed | Risk Level |
|
||||
| ------------------------------------------------- | ----------- | ------------- | ---------- |
|
||||
| `src/xrpld/app/main/Application.cpp` | ~15 | ~3 | Low |
|
||||
| `include/xrpl/app/main/Application.h` | ~5 | ~2 | Low |
|
||||
| `src/xrpld/rpc/detail/ServerHandler.cpp` | ~40 | ~10 | Low |
|
||||
| `src/xrpld/rpc/handlers/*.cpp` | ~30 | ~8 | Low |
|
||||
| `src/xrpld/overlay/detail/PeerImp.cpp` | ~60 | ~15 | Medium |
|
||||
| `src/xrpld/overlay/detail/OverlayImpl.cpp` | ~30 | ~10 | Medium |
|
||||
| `src/xrpld/app/consensus/RCLConsensus.cpp` | ~50 | ~15 | Medium |
|
||||
| `src/xrpld/app/consensus/RCLConsensusAdaptor.cpp` | ~40 | ~12 | Medium |
|
||||
| `src/xrpld/core/JobQueue.cpp` | ~20 | ~5 | Low |
|
||||
| `src/xrpld/overlay/detail/ripple.proto` | ~25 | 0 | Low |
|
||||
| `CMakeLists.txt` | ~40 | ~8 | Low |
|
||||
| `cmake/FindOpenTelemetry.cmake` | ~50 | 0 | None (new) |
|
||||
|
||||
### 3.9.3 Risk Assessment by Component
|
||||
|
||||
```mermaid
|
||||
quadrantChart
|
||||
title Code Intrusiveness Risk Matrix
|
||||
x-axis Low Risk --> High Risk
|
||||
y-axis Low Value --> High Value
|
||||
quadrant-1 High Value, Low Risk - Do First
|
||||
quadrant-2 High Value, High Risk - Plan Carefully
|
||||
quadrant-3 Low Value, Low Risk - Optional
|
||||
quadrant-4 Low Value, High Risk - Avoid
|
||||
|
||||
RPC Tracing: [0.2, 0.8]
|
||||
Transaction Relay: [0.5, 0.9]
|
||||
Consensus Tracing: [0.7, 0.95]
|
||||
Peer Message Tracing: [0.8, 0.4]
|
||||
JobQueue Context: [0.4, 0.5]
|
||||
Ledger Acquisition: [0.5, 0.6]
|
||||
```
|
||||
|
||||
#### Risk Level Definitions
|
||||
|
||||
| Risk Level | Definition | Mitigation |
|
||||
| ---------- | ---------------------------------------------------------------- | ---------------------------------- |
|
||||
| **Low** | Additive changes only; no modification to existing logic | Standard code review |
|
||||
| **Medium** | Minor modifications to existing functions; clear boundaries | Comprehensive unit tests |
|
||||
| **High** | Changes to core logic or data structures; potential side effects | Integration tests + staged rollout |
|
||||
|
||||
### 3.9.4 Architectural Impact Assessment
|
||||
|
||||
| Aspect | Impact | Justification |
|
||||
| -------------------- | ------- | --------------------------------------------------------------------- |
|
||||
| **Data Flow** | None | Tracing is purely observational; no business logic changes |
|
||||
| **Threading Model** | Minimal | Context propagation uses thread-local storage (standard OTel pattern) |
|
||||
| **Memory Model** | Low | Bounded queues prevent unbounded growth; RAII ensures cleanup |
|
||||
| **Network Protocol** | Low | Optional fields in protobuf (high field numbers); backward compatible |
|
||||
| **Configuration** | None | New config section; existing configs unaffected |
|
||||
| **Build System** | Low | Optional CMake flag; builds work without OpenTelemetry |
|
||||
| **Dependencies** | Low | OpenTelemetry SDK is optional; null implementation when disabled |
|
||||
|
||||
### 3.9.5 Backward Compatibility
|
||||
|
||||
| Compatibility | Status | Notes |
|
||||
| --------------- | ------ | ----------------------------------------------------- |
|
||||
| **Config File** | ✅ Full | New `[telemetry]` section is optional |
|
||||
| **Protocol** | ✅ Full | Optional protobuf fields with high field numbers |
|
||||
| **Build** | ✅ Full | `XRPL_ENABLE_TELEMETRY=OFF` produces identical binary |
|
||||
| **Runtime** | ✅ Full | `enabled=0` produces zero overhead |
|
||||
| **API** | ✅ Full | No changes to public RPC or P2P APIs |
|
||||
|
||||
### 3.9.6 Rollback Strategy
|
||||
|
||||
If issues are discovered after deployment:
|
||||
|
||||
1. **Immediate**: Set `enabled=0` in config and restart (zero code change)
|
||||
2. **Quick**: Rebuild with `XRPL_ENABLE_TELEMETRY=OFF`
|
||||
3. **Complete**: Revert telemetry commits (clean separation makes this easy)
|
||||
|
||||
### 3.9.7 Code Change Examples
|
||||
|
||||
**Minimal RPC Instrumentation (Low Intrusiveness):**
|
||||
```cpp
|
||||
// Before
|
||||
void ServerHandler::onRequest(...) {
|
||||
auto result = processRequest(req);
|
||||
send(result);
|
||||
}
|
||||
|
||||
// After (only ~10 lines added)
|
||||
void ServerHandler::onRequest(...) {
|
||||
XRPL_TRACE_RPC(app_.getTelemetry(), "rpc.request"); // +1 line
|
||||
XRPL_TRACE_SET_ATTR("xrpl.rpc.command", command); // +1 line
|
||||
|
||||
auto result = processRequest(req);
|
||||
|
||||
XRPL_TRACE_SET_ATTR("xrpl.rpc.status", status); // +1 line
|
||||
send(result);
|
||||
}
|
||||
```
|
||||
|
||||
**Consensus Instrumentation (Medium Intrusiveness):**
|
||||
```cpp
|
||||
// Before
|
||||
void RCLConsensusAdaptor::startRound(...) {
|
||||
// ... existing logic
|
||||
}
|
||||
|
||||
// After (context storage required)
|
||||
void RCLConsensusAdaptor::startRound(...) {
|
||||
XRPL_TRACE_CONSENSUS(app_.getTelemetry(), "consensus.round");
|
||||
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.seq", seq);
|
||||
|
||||
// Store context for child spans in phase transitions
|
||||
currentRoundContext_ = _xrpl_guard_->context(); // New member variable
|
||||
|
||||
// ... existing logic unchanged
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Design Decisions](./02-design-decisions.md)* | *Next: [Code Samples](./04-code-samples.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,967 +0,0 @@
|
||||
# Code Samples
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Implementation Strategy](./03-implementation-strategy.md) | [Configuration Reference](./05-configuration-reference.md)
|
||||
|
||||
---
|
||||
|
||||
## 4.1 Core Interfaces
|
||||
|
||||
### 4.1.1 Main Telemetry Interface
|
||||
|
||||
```cpp
|
||||
// include/xrpl/telemetry/Telemetry.h
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/telemetry/TelemetryConfig.h>
|
||||
#include <opentelemetry/trace/tracer.h>
|
||||
#include <opentelemetry/trace/span.h>
|
||||
#include <opentelemetry/context/context.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
|
||||
/**
|
||||
* Main telemetry interface for OpenTelemetry integration.
|
||||
*
|
||||
* This class provides the primary API for distributed tracing in rippled.
|
||||
* It manages the OpenTelemetry SDK lifecycle and provides convenience
|
||||
* methods for creating spans and propagating context.
|
||||
*/
|
||||
class Telemetry
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Configuration for the telemetry system.
|
||||
*/
|
||||
struct Setup
|
||||
{
|
||||
bool enabled = false;
|
||||
std::string serviceName = "rippled";
|
||||
std::string serviceVersion;
|
||||
std::string serviceInstanceId; // Node public key
|
||||
|
||||
// Exporter configuration
|
||||
std::string exporterType = "otlp_grpc"; // "otlp_grpc", "otlp_http", "none"
|
||||
std::string exporterEndpoint = "localhost:4317";
|
||||
bool useTls = false;
|
||||
std::string tlsCertPath;
|
||||
|
||||
// Sampling configuration
|
||||
double samplingRatio = 1.0; // 1.0 = 100% sampling
|
||||
|
||||
// Batch processor settings
|
||||
std::uint32_t batchSize = 512;
|
||||
std::chrono::milliseconds batchDelay{5000};
|
||||
std::uint32_t maxQueueSize = 2048;
|
||||
|
||||
// Network attributes
|
||||
std::uint32_t networkId = 0;
|
||||
std::string networkType = "mainnet";
|
||||
|
||||
// Component filtering
|
||||
bool traceTransactions = true;
|
||||
bool traceConsensus = true;
|
||||
bool traceRpc = true;
|
||||
bool tracePeer = false; // High volume, disabled by default
|
||||
bool traceLedger = true;
|
||||
};
|
||||
|
||||
virtual ~Telemetry() = default;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// LIFECYCLE
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Start the telemetry system (call after configuration) */
|
||||
virtual void start() = 0;
|
||||
|
||||
/** Stop the telemetry system (flushes pending spans) */
|
||||
virtual void stop() = 0;
|
||||
|
||||
/** Check if telemetry is enabled */
|
||||
virtual bool isEnabled() const = 0;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// TRACER ACCESS
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Get the tracer for creating spans */
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Tracer>
|
||||
getTracer(std::string_view name = "rippled") = 0;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// SPAN CREATION (Convenience Methods)
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Start a new span with default options */
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
|
||||
startSpan(
|
||||
std::string_view name,
|
||||
opentelemetry::trace::SpanKind kind =
|
||||
opentelemetry::trace::SpanKind::kInternal) = 0;
|
||||
|
||||
/** Start a span as child of given context */
|
||||
virtual opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span>
|
||||
startSpan(
|
||||
std::string_view name,
|
||||
opentelemetry::context::Context const& parentContext,
|
||||
opentelemetry::trace::SpanKind kind =
|
||||
opentelemetry::trace::SpanKind::kInternal) = 0;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// CONTEXT PROPAGATION
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Serialize context for network transmission */
|
||||
virtual std::string serializeContext(
|
||||
opentelemetry::context::Context const& ctx) = 0;
|
||||
|
||||
/** Deserialize context from network data */
|
||||
virtual opentelemetry::context::Context deserializeContext(
|
||||
std::string const& serialized) = 0;
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
// COMPONENT FILTERING
|
||||
// ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
/** Check if transaction tracing is enabled */
|
||||
virtual bool shouldTraceTransactions() const = 0;
|
||||
|
||||
/** Check if consensus tracing is enabled */
|
||||
virtual bool shouldTraceConsensus() const = 0;
|
||||
|
||||
/** Check if RPC tracing is enabled */
|
||||
virtual bool shouldTraceRpc() const = 0;
|
||||
|
||||
/** Check if peer message tracing is enabled */
|
||||
virtual bool shouldTracePeer() const = 0;
|
||||
};
|
||||
|
||||
// Factory functions
|
||||
std::unique_ptr<Telemetry>
|
||||
make_Telemetry(
|
||||
Telemetry::Setup const& setup,
|
||||
beast::Journal journal);
|
||||
|
||||
Telemetry::Setup
|
||||
setup_Telemetry(
|
||||
Section const& section,
|
||||
std::string const& nodePublicKey,
|
||||
std::string const& version);
|
||||
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.2 RAII Span Guard
|
||||
|
||||
```cpp
|
||||
// include/xrpl/telemetry/SpanGuard.h
|
||||
#pragma once
|
||||
|
||||
#include <opentelemetry/trace/span.h>
|
||||
#include <opentelemetry/trace/scope.h>
|
||||
#include <opentelemetry/trace/status.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <exception>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
|
||||
/**
|
||||
* RAII guard for OpenTelemetry spans.
|
||||
*
|
||||
* Automatically ends the span on destruction and makes it the current
|
||||
* span in the thread-local context.
|
||||
*/
|
||||
class SpanGuard
|
||||
{
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span_;
|
||||
opentelemetry::trace::Scope scope_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Construct guard with span.
|
||||
* The span becomes the current span in thread-local context.
|
||||
*/
|
||||
explicit SpanGuard(
|
||||
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> span)
|
||||
: span_(std::move(span))
|
||||
, scope_(span_)
|
||||
{
|
||||
}
|
||||
|
||||
// Non-copyable, non-movable
|
||||
SpanGuard(SpanGuard const&) = delete;
|
||||
SpanGuard& operator=(SpanGuard const&) = delete;
|
||||
SpanGuard(SpanGuard&&) = delete;
|
||||
SpanGuard& operator=(SpanGuard&&) = delete;
|
||||
|
||||
~SpanGuard()
|
||||
{
|
||||
if (span_)
|
||||
span_->End();
|
||||
}
|
||||
|
||||
/** Access the underlying span */
|
||||
opentelemetry::trace::Span& span() { return *span_; }
|
||||
opentelemetry::trace::Span const& span() const { return *span_; }
|
||||
|
||||
/** Set span status to OK */
|
||||
void setOk()
|
||||
{
|
||||
span_->SetStatus(opentelemetry::trace::StatusCode::kOk);
|
||||
}
|
||||
|
||||
/** Set span status with code and description */
|
||||
void setStatus(
|
||||
opentelemetry::trace::StatusCode code,
|
||||
std::string_view description = "")
|
||||
{
|
||||
span_->SetStatus(code, std::string(description));
|
||||
}
|
||||
|
||||
/** Set an attribute on the span */
|
||||
template<typename T>
|
||||
void setAttribute(std::string_view key, T&& value)
|
||||
{
|
||||
span_->SetAttribute(
|
||||
opentelemetry::nostd::string_view(key.data(), key.size()),
|
||||
std::forward<T>(value));
|
||||
}
|
||||
|
||||
/** Add an event to the span */
|
||||
void addEvent(std::string_view name)
|
||||
{
|
||||
span_->AddEvent(std::string(name));
|
||||
}
|
||||
|
||||
/** Record an exception on the span */
|
||||
void recordException(std::exception const& e)
|
||||
{
|
||||
span_->RecordException(e);
|
||||
span_->SetStatus(
|
||||
opentelemetry::trace::StatusCode::kError,
|
||||
e.what());
|
||||
}
|
||||
|
||||
/** Get the current trace context */
|
||||
opentelemetry::context::Context context() const
|
||||
{
|
||||
return opentelemetry::context::RuntimeContext::GetCurrent();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* No-op span guard for when tracing is disabled.
|
||||
* Provides the same interface but does nothing.
|
||||
*/
|
||||
class NullSpanGuard
|
||||
{
|
||||
public:
|
||||
NullSpanGuard() = default;
|
||||
|
||||
void setOk() {}
|
||||
void setStatus(opentelemetry::trace::StatusCode, std::string_view = "") {}
|
||||
|
||||
template<typename T>
|
||||
void setAttribute(std::string_view, T&&) {}
|
||||
|
||||
void addEvent(std::string_view) {}
|
||||
void recordException(std::exception const&) {}
|
||||
};
|
||||
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.3 Instrumentation Macros
|
||||
|
||||
```cpp
|
||||
// src/xrpld/telemetry/TracingInstrumentation.h
|
||||
#pragma once
|
||||
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
#include <xrpl/telemetry/SpanGuard.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// INSTRUMENTATION MACROS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
#ifdef XRPL_ENABLE_TELEMETRY
|
||||
|
||||
// Start a span that is automatically ended when guard goes out of scope
|
||||
#define XRPL_TRACE_SPAN(telemetry, name) \
|
||||
auto _xrpl_span_ = (telemetry).startSpan(name); \
|
||||
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
|
||||
|
||||
// Start a span with specific kind
|
||||
#define XRPL_TRACE_SPAN_KIND(telemetry, name, kind) \
|
||||
auto _xrpl_span_ = (telemetry).startSpan(name, kind); \
|
||||
::xrpl::telemetry::SpanGuard _xrpl_guard_(_xrpl_span_)
|
||||
|
||||
// Conditional span based on component
|
||||
#define XRPL_TRACE_TX(telemetry, name) \
|
||||
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
|
||||
if ((telemetry).shouldTraceTransactions()) { \
|
||||
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
|
||||
}
|
||||
|
||||
#define XRPL_TRACE_CONSENSUS(telemetry, name) \
|
||||
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
|
||||
if ((telemetry).shouldTraceConsensus()) { \
|
||||
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
|
||||
}
|
||||
|
||||
#define XRPL_TRACE_RPC(telemetry, name) \
|
||||
std::optional<::xrpl::telemetry::SpanGuard> _xrpl_guard_; \
|
||||
if ((telemetry).shouldTraceRpc()) { \
|
||||
_xrpl_guard_.emplace((telemetry).startSpan(name)); \
|
||||
}
|
||||
|
||||
// Set attribute on current span (if exists)
|
||||
#define XRPL_TRACE_SET_ATTR(key, value) \
|
||||
if (_xrpl_guard_.has_value()) { \
|
||||
_xrpl_guard_->setAttribute(key, value); \
|
||||
}
|
||||
|
||||
// Record exception on current span
|
||||
#define XRPL_TRACE_EXCEPTION(e) \
|
||||
if (_xrpl_guard_.has_value()) { \
|
||||
_xrpl_guard_->recordException(e); \
|
||||
}
|
||||
|
||||
#else // XRPL_ENABLE_TELEMETRY not defined
|
||||
|
||||
#define XRPL_TRACE_SPAN(telemetry, name) ((void)0)
|
||||
#define XRPL_TRACE_SPAN_KIND(telemetry, name, kind) ((void)0)
|
||||
#define XRPL_TRACE_TX(telemetry, name) ((void)0)
|
||||
#define XRPL_TRACE_CONSENSUS(telemetry, name) ((void)0)
|
||||
#define XRPL_TRACE_RPC(telemetry, name) ((void)0)
|
||||
#define XRPL_TRACE_SET_ATTR(key, value) ((void)0)
|
||||
#define XRPL_TRACE_EXCEPTION(e) ((void)0)
|
||||
|
||||
#endif // XRPL_ENABLE_TELEMETRY
|
||||
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.4 Protocol Buffer Extensions
|
||||
|
||||
### 4.4.1 TraceContext Message Definition
|
||||
|
||||
Add to `src/xrpld/overlay/detail/ripple.proto`:
|
||||
|
||||
```protobuf
|
||||
// Trace context for distributed tracing across nodes
|
||||
// Uses W3C Trace Context format internally
|
||||
message TraceContext {
|
||||
// 16-byte trace identifier (required for valid context)
|
||||
bytes trace_id = 1;
|
||||
|
||||
// 8-byte span identifier of parent span
|
||||
bytes span_id = 2;
|
||||
|
||||
// Trace flags (bit 0 = sampled)
|
||||
uint32 trace_flags = 3;
|
||||
|
||||
// W3C tracestate header value for vendor-specific data
|
||||
string trace_state = 4;
|
||||
}
|
||||
|
||||
// Extend existing messages with optional trace context
|
||||
// High field numbers (1000+) to avoid conflicts
|
||||
|
||||
message TMTransaction {
|
||||
// ... existing fields ...
|
||||
|
||||
// Optional trace context for distributed tracing
|
||||
optional TraceContext trace_context = 1001;
|
||||
}
|
||||
|
||||
message TMProposeSet {
|
||||
// ... existing fields ...
|
||||
optional TraceContext trace_context = 1001;
|
||||
}
|
||||
|
||||
message TMValidation {
|
||||
// ... existing fields ...
|
||||
optional TraceContext trace_context = 1001;
|
||||
}
|
||||
|
||||
message TMGetLedger {
|
||||
// ... existing fields ...
|
||||
optional TraceContext trace_context = 1001;
|
||||
}
|
||||
|
||||
message TMLedgerData {
|
||||
// ... existing fields ...
|
||||
optional TraceContext trace_context = 1001;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4.2 Context Serialization/Deserialization
|
||||
|
||||
```cpp
|
||||
// include/xrpl/telemetry/TraceContext.h
|
||||
#pragma once
|
||||
|
||||
#include <opentelemetry/context/context.h>
|
||||
#include <opentelemetry/trace/span_context.h>
|
||||
#include <protocol/messages.h> // Generated protobuf
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
|
||||
/**
|
||||
* Utilities for trace context serialization and propagation.
|
||||
*/
|
||||
class TraceContextPropagator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Extract trace context from Protocol Buffer message.
|
||||
* Returns empty context if no trace info present.
|
||||
*/
|
||||
static opentelemetry::context::Context
|
||||
extract(protocol::TraceContext const& proto);
|
||||
|
||||
/**
|
||||
* Inject current trace context into Protocol Buffer message.
|
||||
*/
|
||||
static void
|
||||
inject(
|
||||
opentelemetry::context::Context const& ctx,
|
||||
protocol::TraceContext& proto);
|
||||
|
||||
/**
|
||||
* Extract trace context from HTTP headers (for RPC).
|
||||
* Supports W3C Trace Context (traceparent, tracestate).
|
||||
*/
|
||||
static opentelemetry::context::Context
|
||||
extractFromHeaders(
|
||||
std::function<std::optional<std::string>(std::string_view)> headerGetter);
|
||||
|
||||
/**
|
||||
* Inject trace context into HTTP headers (for RPC responses).
|
||||
*/
|
||||
static void
|
||||
injectToHeaders(
|
||||
opentelemetry::context::Context const& ctx,
|
||||
std::function<void(std::string_view, std::string_view)> headerSetter);
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// IMPLEMENTATION
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
|
||||
inline opentelemetry::context::Context
|
||||
TraceContextPropagator::extract(protocol::TraceContext const& proto)
|
||||
{
|
||||
using namespace opentelemetry::trace;
|
||||
|
||||
if (proto.trace_id().size() != 16 || proto.span_id().size() != 8)
|
||||
return opentelemetry::context::Context{}; // Invalid, return empty
|
||||
|
||||
// Construct TraceId and SpanId from bytes
|
||||
TraceId traceId(reinterpret_cast<uint8_t const*>(proto.trace_id().data()));
|
||||
SpanId spanId(reinterpret_cast<uint8_t const*>(proto.span_id().data()));
|
||||
TraceFlags flags(static_cast<uint8_t>(proto.trace_flags()));
|
||||
|
||||
// Create SpanContext from extracted data
|
||||
SpanContext spanContext(traceId, spanId, flags, /* remote = */ true);
|
||||
|
||||
// Create context with extracted span as parent
|
||||
return opentelemetry::context::Context{}.SetValue(
|
||||
opentelemetry::trace::kSpanKey,
|
||||
opentelemetry::nostd::shared_ptr<Span>(
|
||||
new DefaultSpan(spanContext)));
|
||||
}
|
||||
|
||||
inline void
|
||||
TraceContextPropagator::inject(
|
||||
opentelemetry::context::Context const& ctx,
|
||||
protocol::TraceContext& proto)
|
||||
{
|
||||
using namespace opentelemetry::trace;
|
||||
|
||||
// Get current span from context
|
||||
auto span = GetSpan(ctx);
|
||||
if (!span)
|
||||
return;
|
||||
|
||||
auto const& spanCtx = span->GetContext();
|
||||
if (!spanCtx.IsValid())
|
||||
return;
|
||||
|
||||
// Serialize trace_id (16 bytes)
|
||||
auto const& traceId = spanCtx.trace_id();
|
||||
proto.set_trace_id(traceId.Id().data(), TraceId::kSize);
|
||||
|
||||
// Serialize span_id (8 bytes)
|
||||
auto const& spanId = spanCtx.span_id();
|
||||
proto.set_span_id(spanId.Id().data(), SpanId::kSize);
|
||||
|
||||
// Serialize flags
|
||||
proto.set_trace_flags(spanCtx.trace_flags().flags());
|
||||
|
||||
// Note: tracestate not implemented yet
|
||||
}
|
||||
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.5 Module-Specific Instrumentation
|
||||
|
||||
### 4.5.1 Transaction Relay Instrumentation
|
||||
|
||||
```cpp
|
||||
// src/xrpld/overlay/detail/PeerImp.cpp (modified)
|
||||
|
||||
#include <xrpl/telemetry/TracingInstrumentation.h>
|
||||
|
||||
void
|
||||
PeerImp::handleTransaction(
|
||||
std::shared_ptr<protocol::TMTransaction> const& m)
|
||||
{
|
||||
// Extract trace context from incoming message
|
||||
opentelemetry::context::Context parentCtx;
|
||||
if (m->has_trace_context())
|
||||
{
|
||||
parentCtx = telemetry::TraceContextPropagator::extract(
|
||||
m->trace_context());
|
||||
}
|
||||
|
||||
// Start span as child of remote span (cross-node link)
|
||||
auto span = app_.getTelemetry().startSpan(
|
||||
"tx.receive",
|
||||
parentCtx,
|
||||
opentelemetry::trace::SpanKind::kServer);
|
||||
telemetry::SpanGuard guard(span);
|
||||
|
||||
try
|
||||
{
|
||||
// Parse and validate transaction
|
||||
SerialIter sit(makeSlice(m->rawtransaction()));
|
||||
auto stx = std::make_shared<STTx const>(sit);
|
||||
|
||||
// Add transaction attributes
|
||||
guard.setAttribute("xrpl.tx.hash", to_string(stx->getTransactionID()));
|
||||
guard.setAttribute("xrpl.tx.type", stx->getTxnType());
|
||||
guard.setAttribute("xrpl.peer.id", remote_address_.to_string());
|
||||
|
||||
// Check if we've seen this transaction (HashRouter)
|
||||
auto const [flags, suppressed] =
|
||||
app_.getHashRouter().addSuppressionPeer(
|
||||
stx->getTransactionID(),
|
||||
id_);
|
||||
|
||||
if (suppressed)
|
||||
{
|
||||
guard.setAttribute("xrpl.tx.suppressed", true);
|
||||
guard.addEvent("tx.duplicate");
|
||||
return; // Already processing this transaction
|
||||
}
|
||||
|
||||
// Create child span for validation
|
||||
{
|
||||
auto validateSpan = app_.getTelemetry().startSpan("tx.validate");
|
||||
telemetry::SpanGuard validateGuard(validateSpan);
|
||||
|
||||
auto [validity, reason] = checkTransaction(stx);
|
||||
validateGuard.setAttribute("xrpl.tx.validity",
|
||||
validity == Validity::Valid ? "valid" : "invalid");
|
||||
|
||||
if (validity != Validity::Valid)
|
||||
{
|
||||
validateGuard.setStatus(
|
||||
opentelemetry::trace::StatusCode::kError,
|
||||
reason);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Relay to other peers (capture context for propagation)
|
||||
auto ctx = guard.context();
|
||||
|
||||
// Create child span for relay
|
||||
auto relaySpan = app_.getTelemetry().startSpan(
|
||||
"tx.relay",
|
||||
ctx,
|
||||
opentelemetry::trace::SpanKind::kClient);
|
||||
{
|
||||
telemetry::SpanGuard relayGuard(relaySpan);
|
||||
|
||||
// Inject context into outgoing message
|
||||
protocol::TraceContext protoCtx;
|
||||
telemetry::TraceContextPropagator::inject(
|
||||
relayGuard.context(), protoCtx);
|
||||
|
||||
// Relay to other peers
|
||||
app_.overlay().relay(
|
||||
stx->getTransactionID(),
|
||||
*m,
|
||||
protoCtx, // Pass trace context
|
||||
exclusions);
|
||||
|
||||
relayGuard.setAttribute("xrpl.tx.relay_count",
|
||||
static_cast<int64_t>(relayCount));
|
||||
}
|
||||
|
||||
guard.setOk();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
guard.recordException(e);
|
||||
JLOG(journal_.warn()) << "Transaction handling failed: " << e.what();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.2 Consensus Instrumentation
|
||||
|
||||
```cpp
|
||||
// src/xrpld/app/consensus/RCLConsensus.cpp (modified)
|
||||
|
||||
#include <xrpl/telemetry/TracingInstrumentation.h>
|
||||
|
||||
void
|
||||
RCLConsensusAdaptor::startRound(
|
||||
NetClock::time_point const& now,
|
||||
RCLCxLedger::ID const& prevLedgerHash,
|
||||
RCLCxLedger const& prevLedger,
|
||||
hash_set<NodeID> const& peers,
|
||||
bool proposing)
|
||||
{
|
||||
XRPL_TRACE_CONSENSUS(app_.getTelemetry(), "consensus.round");
|
||||
|
||||
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.prev", to_string(prevLedgerHash));
|
||||
XRPL_TRACE_SET_ATTR("xrpl.consensus.ledger.seq",
|
||||
static_cast<int64_t>(prevLedger.seq() + 1));
|
||||
XRPL_TRACE_SET_ATTR("xrpl.consensus.proposers",
|
||||
static_cast<int64_t>(peers.size()));
|
||||
XRPL_TRACE_SET_ATTR("xrpl.consensus.mode",
|
||||
proposing ? "proposing" : "observing");
|
||||
|
||||
// Store trace context for use in phase transitions
|
||||
currentRoundContext_ = _xrpl_guard_.has_value()
|
||||
? _xrpl_guard_->context()
|
||||
: opentelemetry::context::Context{};
|
||||
|
||||
// ... existing implementation ...
|
||||
}
|
||||
|
||||
ConsensusPhase
|
||||
RCLConsensusAdaptor::phaseTransition(ConsensusPhase newPhase)
|
||||
{
|
||||
// Create span for phase transition
|
||||
auto span = app_.getTelemetry().startSpan(
|
||||
"consensus.phase." + to_string(newPhase),
|
||||
currentRoundContext_);
|
||||
telemetry::SpanGuard guard(span);
|
||||
|
||||
guard.setAttribute("xrpl.consensus.phase", to_string(newPhase));
|
||||
guard.addEvent("phase.enter");
|
||||
|
||||
auto const startTime = std::chrono::steady_clock::now();
|
||||
|
||||
try
|
||||
{
|
||||
auto result = doPhaseTransition(newPhase);
|
||||
|
||||
auto const duration = std::chrono::steady_clock::now() - startTime;
|
||||
guard.setAttribute("xrpl.consensus.phase_duration_ms",
|
||||
std::chrono::duration<double, std::milli>(duration).count());
|
||||
|
||||
guard.setOk();
|
||||
return result;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
guard.recordException(e);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RCLConsensusAdaptor::peerProposal(
|
||||
NetClock::time_point const& now,
|
||||
RCLCxPeerPos const& proposal)
|
||||
{
|
||||
// Extract trace context from proposal message
|
||||
opentelemetry::context::Context parentCtx;
|
||||
if (proposal.hasTraceContext())
|
||||
{
|
||||
parentCtx = telemetry::TraceContextPropagator::extract(
|
||||
proposal.traceContext());
|
||||
}
|
||||
|
||||
auto span = app_.getTelemetry().startSpan(
|
||||
"consensus.proposal.receive",
|
||||
parentCtx,
|
||||
opentelemetry::trace::SpanKind::kServer);
|
||||
telemetry::SpanGuard guard(span);
|
||||
|
||||
guard.setAttribute("xrpl.consensus.proposer",
|
||||
toBase58(TokenType::NodePublic, proposal.nodeId()));
|
||||
guard.setAttribute("xrpl.consensus.round",
|
||||
static_cast<int64_t>(proposal.proposal().proposeSeq()));
|
||||
|
||||
// ... existing implementation ...
|
||||
|
||||
guard.setOk();
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.3 RPC Handler Instrumentation
|
||||
|
||||
```cpp
|
||||
// src/xrpld/rpc/detail/ServerHandler.cpp (modified)
|
||||
|
||||
#include <xrpl/telemetry/TracingInstrumentation.h>
|
||||
|
||||
void
|
||||
ServerHandler::onRequest(
|
||||
http_request_type&& req,
|
||||
std::function<void(http_response_type&&)>&& send)
|
||||
{
|
||||
// Extract trace context from HTTP headers (W3C Trace Context)
|
||||
auto parentCtx = telemetry::TraceContextPropagator::extractFromHeaders(
|
||||
[&req](std::string_view name) -> std::optional<std::string> {
|
||||
auto it = req.find(boost::beast::http::field{
|
||||
std::string(name)});
|
||||
if (it != req.end())
|
||||
return std::string(it->value());
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
// Start request span
|
||||
auto span = app_.getTelemetry().startSpan(
|
||||
"rpc.request",
|
||||
parentCtx,
|
||||
opentelemetry::trace::SpanKind::kServer);
|
||||
telemetry::SpanGuard guard(span);
|
||||
|
||||
// Add HTTP attributes
|
||||
guard.setAttribute("http.method", std::string(req.method_string()));
|
||||
guard.setAttribute("http.target", std::string(req.target()));
|
||||
guard.setAttribute("http.user_agent",
|
||||
std::string(req[boost::beast::http::field::user_agent]));
|
||||
|
||||
auto const startTime = std::chrono::steady_clock::now();
|
||||
|
||||
try
|
||||
{
|
||||
// Parse and process request
|
||||
auto const& body = req.body();
|
||||
Json::Value jv;
|
||||
Json::Reader reader;
|
||||
|
||||
if (!reader.parse(body, jv))
|
||||
{
|
||||
guard.setStatus(
|
||||
opentelemetry::trace::StatusCode::kError,
|
||||
"Invalid JSON");
|
||||
sendError(send, "Invalid JSON");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract command name
|
||||
std::string command = jv.isMember("command")
|
||||
? jv["command"].asString()
|
||||
: jv.isMember("method")
|
||||
? jv["method"].asString()
|
||||
: "unknown";
|
||||
|
||||
guard.setAttribute("xrpl.rpc.command", command);
|
||||
|
||||
// Create child span for command execution
|
||||
auto cmdSpan = app_.getTelemetry().startSpan(
|
||||
"rpc.command." + command);
|
||||
{
|
||||
telemetry::SpanGuard cmdGuard(cmdSpan);
|
||||
|
||||
// Execute RPC command
|
||||
auto result = processRequest(jv);
|
||||
|
||||
// Record result attributes
|
||||
if (result.isMember("status"))
|
||||
{
|
||||
cmdGuard.setAttribute("xrpl.rpc.status",
|
||||
result["status"].asString());
|
||||
}
|
||||
|
||||
if (result["status"].asString() == "error")
|
||||
{
|
||||
cmdGuard.setStatus(
|
||||
opentelemetry::trace::StatusCode::kError,
|
||||
result.isMember("error_message")
|
||||
? result["error_message"].asString()
|
||||
: "RPC error");
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdGuard.setOk();
|
||||
}
|
||||
}
|
||||
|
||||
auto const duration = std::chrono::steady_clock::now() - startTime;
|
||||
guard.setAttribute("http.duration_ms",
|
||||
std::chrono::duration<double, std::milli>(duration).count());
|
||||
|
||||
// Inject trace context into response headers
|
||||
http_response_type resp;
|
||||
telemetry::TraceContextPropagator::injectToHeaders(
|
||||
guard.context(),
|
||||
[&resp](std::string_view name, std::string_view value) {
|
||||
resp.set(std::string(name), std::string(value));
|
||||
});
|
||||
|
||||
guard.setOk();
|
||||
send(std::move(resp));
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
guard.recordException(e);
|
||||
JLOG(journal_.error()) << "RPC request failed: " << e.what();
|
||||
sendError(send, e.what());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.4 JobQueue Context Propagation
|
||||
|
||||
```cpp
|
||||
// src/xrpld/core/JobQueue.h (modified)
|
||||
|
||||
#include <opentelemetry/context/context.h>
|
||||
|
||||
class Job
|
||||
{
|
||||
// ... existing members ...
|
||||
|
||||
// Captured trace context at job creation
|
||||
opentelemetry::context::Context traceContext_;
|
||||
|
||||
public:
|
||||
// Constructor captures current trace context
|
||||
Job(JobType type, std::function<void()> func, ...)
|
||||
: type_(type)
|
||||
, func_(std::move(func))
|
||||
, traceContext_(opentelemetry::context::RuntimeContext::GetCurrent())
|
||||
// ... other initializations ...
|
||||
{
|
||||
}
|
||||
|
||||
// Get trace context for restoration during execution
|
||||
opentelemetry::context::Context const&
|
||||
traceContext() const { return traceContext_; }
|
||||
};
|
||||
|
||||
// src/xrpld/core/JobQueue.cpp (modified)
|
||||
|
||||
void
|
||||
Worker::run()
|
||||
{
|
||||
while (auto job = getJob())
|
||||
{
|
||||
// Restore trace context from job creation
|
||||
auto token = opentelemetry::context::RuntimeContext::Attach(
|
||||
job->traceContext());
|
||||
|
||||
// Start execution span
|
||||
auto span = app_.getTelemetry().startSpan("job.execute");
|
||||
telemetry::SpanGuard guard(span);
|
||||
|
||||
guard.setAttribute("xrpl.job.type", to_string(job->type()));
|
||||
guard.setAttribute("xrpl.job.queue_ms", job->queueTimeMs());
|
||||
guard.setAttribute("xrpl.job.worker", workerId_);
|
||||
|
||||
try
|
||||
{
|
||||
job->execute();
|
||||
guard.setOk();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
guard.recordException(e);
|
||||
JLOG(journal_.error()) << "Job execution failed: " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.6 Span Flow Visualization
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Client["External Client"]
|
||||
submit["Submit TX"]
|
||||
end
|
||||
|
||||
subgraph NodeA["rippled Node A"]
|
||||
rpcA["rpc.request"]
|
||||
cmdA["rpc.command.submit"]
|
||||
txRecvA["tx.receive"]
|
||||
txValA["tx.validate"]
|
||||
txRelayA["tx.relay"]
|
||||
end
|
||||
|
||||
subgraph NodeB["rippled Node B"]
|
||||
txRecvB["tx.receive"]
|
||||
txValB["tx.validate"]
|
||||
txRelayB["tx.relay"]
|
||||
end
|
||||
|
||||
subgraph NodeC["rippled Node C"]
|
||||
txRecvC["tx.receive"]
|
||||
consensusC["consensus.round"]
|
||||
phaseC["consensus.phase.establish"]
|
||||
end
|
||||
|
||||
submit --> rpcA
|
||||
rpcA --> cmdA
|
||||
cmdA --> txRecvA
|
||||
txRecvA --> txValA
|
||||
txValA --> txRelayA
|
||||
txRelayA -.->|"TraceContext"| txRecvB
|
||||
txRecvB --> txValB
|
||||
txValB --> txRelayB
|
||||
txRelayB -.->|"TraceContext"| txRecvC
|
||||
txRecvC --> consensusC
|
||||
consensusC --> phaseC
|
||||
|
||||
style rpcA fill:#e3f2fd,stroke:#1976d2
|
||||
style txRecvA fill:#e8f5e9,stroke:#388e3c
|
||||
style txRecvB fill:#e8f5e9,stroke:#388e3c
|
||||
style txRecvC fill:#e8f5e9,stroke:#388e3c
|
||||
style consensusC fill:#fff3e0,stroke:#ff9800
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Implementation Strategy](./03-implementation-strategy.md)* | *Next: [Configuration Reference](./05-configuration-reference.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,936 +0,0 @@
|
||||
# Configuration Reference
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Code Samples](./04-code-samples.md) | [Implementation Phases](./06-implementation-phases.md)
|
||||
|
||||
---
|
||||
|
||||
## 5.1 rippled Configuration
|
||||
|
||||
### 5.1.1 Configuration File Section
|
||||
|
||||
Add to `cfg/xrpld-example.cfg`:
|
||||
|
||||
```ini
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# TELEMETRY (OpenTelemetry Distributed Tracing)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
#
|
||||
# Enables distributed tracing for transaction flow, consensus, and RPC calls.
|
||||
# Traces are exported to an OpenTelemetry Collector using OTLP protocol.
|
||||
#
|
||||
# [telemetry]
|
||||
#
|
||||
# # Enable/disable telemetry (default: 0 = disabled)
|
||||
# enabled=1
|
||||
#
|
||||
# # Exporter type: "otlp_grpc" (default), "otlp_http", or "none"
|
||||
# exporter=otlp_grpc
|
||||
#
|
||||
# # OTLP endpoint (default: localhost:4317 for gRPC, localhost:4318 for HTTP)
|
||||
# endpoint=localhost:4317
|
||||
#
|
||||
# # Use TLS for exporter connection (default: 0)
|
||||
# use_tls=0
|
||||
#
|
||||
# # Path to CA certificate for TLS (optional)
|
||||
# # tls_ca_cert=/path/to/ca.crt
|
||||
#
|
||||
# # Sampling ratio: 0.0-1.0 (default: 1.0 = 100% sampling)
|
||||
# # Use lower values in production to reduce overhead
|
||||
# sampling_ratio=0.1
|
||||
#
|
||||
# # Batch processor settings
|
||||
# batch_size=512 # Spans per batch (default: 512)
|
||||
# batch_delay_ms=5000 # Max delay before sending batch (default: 5000)
|
||||
# max_queue_size=2048 # Max queued spans (default: 2048)
|
||||
#
|
||||
# # Component-specific tracing (default: all enabled except peer)
|
||||
# trace_transactions=1 # Transaction relay and processing
|
||||
# trace_consensus=1 # Consensus rounds and proposals
|
||||
# trace_rpc=1 # RPC request handling
|
||||
# trace_peer=0 # Peer messages (high volume, disabled by default)
|
||||
# trace_ledger=1 # Ledger acquisition and building
|
||||
#
|
||||
# # Service identification (automatically detected if not specified)
|
||||
# # service_name=rippled
|
||||
# # service_instance_id=<node_public_key>
|
||||
|
||||
[telemetry]
|
||||
enabled=0
|
||||
```
|
||||
|
||||
### 5.1.2 Configuration Options Summary
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
| --------------------- | ------ | ---------------- | ----------------------------------------- |
|
||||
| `enabled` | bool | `false` | Enable/disable telemetry |
|
||||
| `exporter` | string | `"otlp_grpc"` | Exporter type: otlp_grpc, otlp_http, none |
|
||||
| `endpoint` | string | `localhost:4317` | OTLP collector endpoint |
|
||||
| `use_tls` | bool | `false` | Enable TLS for exporter connection |
|
||||
| `tls_ca_cert` | string | `""` | Path to CA certificate file |
|
||||
| `sampling_ratio` | float | `1.0` | Sampling ratio (0.0-1.0) |
|
||||
| `batch_size` | uint | `512` | Spans per export batch |
|
||||
| `batch_delay_ms` | uint | `5000` | Max delay before sending batch (ms) |
|
||||
| `max_queue_size` | uint | `2048` | Maximum queued spans |
|
||||
| `trace_transactions` | bool | `true` | Enable transaction tracing |
|
||||
| `trace_consensus` | bool | `true` | Enable consensus tracing |
|
||||
| `trace_rpc` | bool | `true` | Enable RPC tracing |
|
||||
| `trace_peer` | bool | `false` | Enable peer message tracing (high volume) |
|
||||
| `trace_ledger` | bool | `true` | Enable ledger tracing |
|
||||
| `service_name` | string | `"rippled"` | Service name for traces |
|
||||
| `service_instance_id` | string | `<node_pubkey>` | Instance identifier |
|
||||
|
||||
---
|
||||
|
||||
## 5.2 Configuration Parser
|
||||
|
||||
```cpp
|
||||
// src/libxrpl/telemetry/TelemetryConfig.cpp
|
||||
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
|
||||
namespace xrpl {
|
||||
namespace telemetry {
|
||||
|
||||
Telemetry::Setup
|
||||
setup_Telemetry(
|
||||
Section const& section,
|
||||
std::string const& nodePublicKey,
|
||||
std::string const& version)
|
||||
{
|
||||
Telemetry::Setup setup;
|
||||
|
||||
// Basic settings
|
||||
setup.enabled = section.value_or("enabled", false);
|
||||
setup.serviceName = section.value_or("service_name", "rippled");
|
||||
setup.serviceVersion = version;
|
||||
setup.serviceInstanceId = section.value_or(
|
||||
"service_instance_id", nodePublicKey);
|
||||
|
||||
// Exporter settings
|
||||
setup.exporterType = section.value_or("exporter", "otlp_grpc");
|
||||
|
||||
if (setup.exporterType == "otlp_grpc")
|
||||
setup.exporterEndpoint = section.value_or("endpoint", "localhost:4317");
|
||||
else if (setup.exporterType == "otlp_http")
|
||||
setup.exporterEndpoint = section.value_or("endpoint", "localhost:4318");
|
||||
|
||||
setup.useTls = section.value_or("use_tls", false);
|
||||
setup.tlsCertPath = section.value_or("tls_ca_cert", "");
|
||||
|
||||
// Sampling
|
||||
setup.samplingRatio = section.value_or("sampling_ratio", 1.0);
|
||||
if (setup.samplingRatio < 0.0 || setup.samplingRatio > 1.0)
|
||||
{
|
||||
Throw<std::runtime_error>(
|
||||
"telemetry.sampling_ratio must be between 0.0 and 1.0");
|
||||
}
|
||||
|
||||
// Batch processor
|
||||
setup.batchSize = section.value_or("batch_size", 512u);
|
||||
setup.batchDelay = std::chrono::milliseconds{
|
||||
section.value_or("batch_delay_ms", 5000u)};
|
||||
setup.maxQueueSize = section.value_or("max_queue_size", 2048u);
|
||||
|
||||
// Component filtering
|
||||
setup.traceTransactions = section.value_or("trace_transactions", true);
|
||||
setup.traceConsensus = section.value_or("trace_consensus", true);
|
||||
setup.traceRpc = section.value_or("trace_rpc", true);
|
||||
setup.tracePeer = section.value_or("trace_peer", false);
|
||||
setup.traceLedger = section.value_or("trace_ledger", true);
|
||||
|
||||
return setup;
|
||||
}
|
||||
|
||||
} // namespace telemetry
|
||||
} // namespace xrpl
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.3 Application Integration
|
||||
|
||||
### 5.3.1 ApplicationImp Changes
|
||||
|
||||
```cpp
|
||||
// src/xrpld/app/main/Application.cpp (modified)
|
||||
|
||||
#include <xrpl/telemetry/Telemetry.h>
|
||||
|
||||
class ApplicationImp : public Application
|
||||
{
|
||||
// ... existing members ...
|
||||
|
||||
// Telemetry (must be constructed early, destroyed late)
|
||||
std::unique_ptr<telemetry::Telemetry> telemetry_;
|
||||
|
||||
public:
|
||||
ApplicationImp(...)
|
||||
{
|
||||
// Initialize telemetry early (before other components)
|
||||
auto telemetrySection = config_->section("telemetry");
|
||||
auto telemetrySetup = telemetry::setup_Telemetry(
|
||||
telemetrySection,
|
||||
toBase58(TokenType::NodePublic, nodeIdentity_.publicKey()),
|
||||
BuildInfo::getVersionString());
|
||||
|
||||
// Set network attributes
|
||||
telemetrySetup.networkId = config_->NETWORK_ID;
|
||||
telemetrySetup.networkType = [&]() {
|
||||
if (config_->NETWORK_ID == 0) return "mainnet";
|
||||
if (config_->NETWORK_ID == 1) return "testnet";
|
||||
if (config_->NETWORK_ID == 2) return "devnet";
|
||||
return "custom";
|
||||
}();
|
||||
|
||||
telemetry_ = telemetry::make_Telemetry(
|
||||
telemetrySetup,
|
||||
logs_->journal("Telemetry"));
|
||||
|
||||
// ... rest of initialization ...
|
||||
}
|
||||
|
||||
void start() override
|
||||
{
|
||||
// Start telemetry first
|
||||
if (telemetry_)
|
||||
telemetry_->start();
|
||||
|
||||
// ... existing start code ...
|
||||
}
|
||||
|
||||
void stop() override
|
||||
{
|
||||
// ... existing stop code ...
|
||||
|
||||
// Stop telemetry last (to capture shutdown spans)
|
||||
if (telemetry_)
|
||||
telemetry_->stop();
|
||||
}
|
||||
|
||||
telemetry::Telemetry& getTelemetry() override
|
||||
{
|
||||
assert(telemetry_);
|
||||
return *telemetry_;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3.2 Application Interface Addition
|
||||
|
||||
```cpp
|
||||
// include/xrpl/app/main/Application.h (modified)
|
||||
|
||||
namespace telemetry { class Telemetry; }
|
||||
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
// ... existing virtual methods ...
|
||||
|
||||
/** Get the telemetry system for distributed tracing */
|
||||
virtual telemetry::Telemetry& getTelemetry() = 0;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.4 CMake Integration
|
||||
|
||||
### 5.4.1 Find OpenTelemetry Module
|
||||
|
||||
```cmake
|
||||
# cmake/FindOpenTelemetry.cmake
|
||||
|
||||
# Find OpenTelemetry C++ SDK
|
||||
#
|
||||
# This module defines:
|
||||
# OpenTelemetry_FOUND - System has OpenTelemetry
|
||||
# OpenTelemetry::api - API library target
|
||||
# OpenTelemetry::sdk - SDK library target
|
||||
# OpenTelemetry::otlp_grpc_exporter - OTLP gRPC exporter target
|
||||
# OpenTelemetry::otlp_http_exporter - OTLP HTTP exporter target
|
||||
|
||||
find_package(opentelemetry-cpp CONFIG QUIET)
|
||||
|
||||
if(opentelemetry-cpp_FOUND)
|
||||
set(OpenTelemetry_FOUND TRUE)
|
||||
|
||||
# Create imported targets if not already created by config
|
||||
if(NOT TARGET OpenTelemetry::api)
|
||||
add_library(OpenTelemetry::api ALIAS opentelemetry-cpp::api)
|
||||
endif()
|
||||
if(NOT TARGET OpenTelemetry::sdk)
|
||||
add_library(OpenTelemetry::sdk ALIAS opentelemetry-cpp::sdk)
|
||||
endif()
|
||||
if(NOT TARGET OpenTelemetry::otlp_grpc_exporter)
|
||||
add_library(OpenTelemetry::otlp_grpc_exporter ALIAS
|
||||
opentelemetry-cpp::otlp_grpc_exporter)
|
||||
endif()
|
||||
else()
|
||||
# Try pkg-config fallback
|
||||
find_package(PkgConfig QUIET)
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(OTEL opentelemetry-cpp QUIET)
|
||||
if(OTEL_FOUND)
|
||||
set(OpenTelemetry_FOUND TRUE)
|
||||
# Create imported targets from pkg-config
|
||||
add_library(OpenTelemetry::api INTERFACE IMPORTED)
|
||||
target_include_directories(OpenTelemetry::api INTERFACE
|
||||
${OTEL_INCLUDE_DIRS})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(OpenTelemetry
|
||||
REQUIRED_VARS OpenTelemetry_FOUND)
|
||||
```
|
||||
|
||||
### 5.4.2 CMakeLists.txt Changes
|
||||
|
||||
```cmake
|
||||
# CMakeLists.txt (additions)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# TELEMETRY OPTIONS
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
option(XRPL_ENABLE_TELEMETRY
|
||||
"Enable OpenTelemetry distributed tracing support" OFF)
|
||||
|
||||
if(XRPL_ENABLE_TELEMETRY)
|
||||
find_package(OpenTelemetry REQUIRED)
|
||||
|
||||
# Define compile-time flag
|
||||
add_compile_definitions(XRPL_ENABLE_TELEMETRY)
|
||||
|
||||
message(STATUS "OpenTelemetry tracing: ENABLED")
|
||||
else()
|
||||
message(STATUS "OpenTelemetry tracing: DISABLED")
|
||||
endif()
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# TELEMETRY LIBRARY
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
if(XRPL_ENABLE_TELEMETRY)
|
||||
add_library(xrpl_telemetry
|
||||
src/libxrpl/telemetry/Telemetry.cpp
|
||||
src/libxrpl/telemetry/TelemetryConfig.cpp
|
||||
src/libxrpl/telemetry/TraceContext.cpp
|
||||
)
|
||||
|
||||
target_include_directories(xrpl_telemetry
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_link_libraries(xrpl_telemetry
|
||||
PUBLIC
|
||||
OpenTelemetry::api
|
||||
OpenTelemetry::sdk
|
||||
OpenTelemetry::otlp_grpc_exporter
|
||||
PRIVATE
|
||||
xrpl_basics
|
||||
)
|
||||
|
||||
# Add to main library dependencies
|
||||
target_link_libraries(xrpld PRIVATE xrpl_telemetry)
|
||||
else()
|
||||
# Create null implementation library
|
||||
add_library(xrpl_telemetry
|
||||
src/libxrpl/telemetry/NullTelemetry.cpp
|
||||
)
|
||||
target_include_directories(xrpl_telemetry
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
endif()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.5 OpenTelemetry Collector Configuration
|
||||
|
||||
### 5.5.1 Development Configuration
|
||||
|
||||
```yaml
|
||||
# otel-collector-dev.yaml
|
||||
# Minimal configuration for local development
|
||||
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
|
||||
processors:
|
||||
batch:
|
||||
timeout: 1s
|
||||
send_batch_size: 100
|
||||
|
||||
exporters:
|
||||
# Console output for debugging
|
||||
logging:
|
||||
verbosity: detailed
|
||||
sampling_initial: 5
|
||||
sampling_thereafter: 200
|
||||
|
||||
# Jaeger for trace visualization
|
||||
jaeger:
|
||||
endpoint: jaeger:14250
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [batch]
|
||||
exporters: [logging, jaeger]
|
||||
```
|
||||
|
||||
### 5.5.2 Production Configuration
|
||||
|
||||
```yaml
|
||||
# otel-collector-prod.yaml
|
||||
# Production configuration with filtering, sampling, and multiple backends
|
||||
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
tls:
|
||||
cert_file: /etc/otel/server.crt
|
||||
key_file: /etc/otel/server.key
|
||||
ca_file: /etc/otel/ca.crt
|
||||
|
||||
processors:
|
||||
# Memory limiter to prevent OOM
|
||||
memory_limiter:
|
||||
check_interval: 1s
|
||||
limit_mib: 1000
|
||||
spike_limit_mib: 200
|
||||
|
||||
# Batch processing for efficiency
|
||||
batch:
|
||||
timeout: 5s
|
||||
send_batch_size: 512
|
||||
send_batch_max_size: 1024
|
||||
|
||||
# Tail-based sampling (keep errors and slow traces)
|
||||
tail_sampling:
|
||||
decision_wait: 10s
|
||||
num_traces: 100000
|
||||
expected_new_traces_per_sec: 1000
|
||||
policies:
|
||||
# Always keep error traces
|
||||
- name: errors
|
||||
type: status_code
|
||||
status_code:
|
||||
status_codes: [ERROR]
|
||||
# Keep slow consensus rounds (>5s)
|
||||
- name: slow-consensus
|
||||
type: latency
|
||||
latency:
|
||||
threshold_ms: 5000
|
||||
# Keep slow RPC requests (>1s)
|
||||
- name: slow-rpc
|
||||
type: and
|
||||
and:
|
||||
and_sub_policy:
|
||||
- name: rpc-spans
|
||||
type: string_attribute
|
||||
string_attribute:
|
||||
key: xrpl.rpc.command
|
||||
values: [".*"]
|
||||
enabled_regex_matching: true
|
||||
- name: latency
|
||||
type: latency
|
||||
latency:
|
||||
threshold_ms: 1000
|
||||
# Probabilistic sampling for the rest
|
||||
- name: probabilistic
|
||||
type: probabilistic
|
||||
probabilistic:
|
||||
sampling_percentage: 10
|
||||
|
||||
# Attribute processing
|
||||
attributes:
|
||||
actions:
|
||||
# Hash sensitive data
|
||||
- key: xrpl.tx.account
|
||||
action: hash
|
||||
# Add deployment info
|
||||
- key: deployment.environment
|
||||
value: production
|
||||
action: upsert
|
||||
|
||||
exporters:
|
||||
# Grafana Tempo for long-term storage
|
||||
otlp/tempo:
|
||||
endpoint: tempo.monitoring:4317
|
||||
tls:
|
||||
insecure: false
|
||||
ca_file: /etc/otel/tempo-ca.crt
|
||||
|
||||
# Elastic APM for correlation with logs
|
||||
otlp/elastic:
|
||||
endpoint: apm.elastic:8200
|
||||
headers:
|
||||
Authorization: "Bearer ${ELASTIC_APM_TOKEN}"
|
||||
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 0.0.0.0:13133
|
||||
zpages:
|
||||
endpoint: 0.0.0.0:55679
|
||||
|
||||
service:
|
||||
extensions: [health_check, zpages]
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [memory_limiter, tail_sampling, attributes, batch]
|
||||
exporters: [otlp/tempo, otlp/elastic]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.6 Docker Compose Development Environment
|
||||
|
||||
```yaml
|
||||
# docker-compose-telemetry.yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# OpenTelemetry Collector
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector-contrib:0.92.0
|
||||
container_name: otel-collector
|
||||
command: ["--config=/etc/otel-collector-config.yaml"]
|
||||
volumes:
|
||||
- ./otel-collector-dev.yaml:/etc/otel-collector-config.yaml:ro
|
||||
ports:
|
||||
- "4317:4317" # OTLP gRPC
|
||||
- "4318:4318" # OTLP HTTP
|
||||
- "13133:13133" # Health check
|
||||
depends_on:
|
||||
- jaeger
|
||||
|
||||
# Jaeger for trace visualization
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one:1.53
|
||||
container_name: jaeger
|
||||
environment:
|
||||
- COLLECTOR_OTLP_ENABLED=true
|
||||
ports:
|
||||
- "16686:16686" # UI
|
||||
- "14250:14250" # gRPC
|
||||
|
||||
# Grafana for dashboards
|
||||
grafana:
|
||||
image: grafana/grafana:10.2.3
|
||||
container_name: grafana
|
||||
environment:
|
||||
- GF_AUTH_ANONYMOUS_ENABLED=true
|
||||
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||
volumes:
|
||||
- ./grafana/provisioning:/etc/grafana/provisioning:ro
|
||||
- ./grafana/dashboards:/var/lib/grafana/dashboards:ro
|
||||
ports:
|
||||
- "3000:3000"
|
||||
depends_on:
|
||||
- jaeger
|
||||
|
||||
# Prometheus for metrics (optional, for correlation)
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.48.1
|
||||
container_name: prometheus
|
||||
volumes:
|
||||
- ./prometheus.yaml:/etc/prometheus/prometheus.yml:ro
|
||||
ports:
|
||||
- "9090:9090"
|
||||
|
||||
networks:
|
||||
default:
|
||||
name: rippled-telemetry
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.7 Configuration Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph config["Configuration Sources"]
|
||||
cfgFile["xrpld.cfg<br/>[telemetry] section"]
|
||||
cmake["CMake<br/>XRPL_ENABLE_TELEMETRY"]
|
||||
end
|
||||
|
||||
subgraph init["Initialization"]
|
||||
parse["setup_Telemetry()"]
|
||||
factory["make_Telemetry()"]
|
||||
end
|
||||
|
||||
subgraph runtime["Runtime Components"]
|
||||
tracer["TracerProvider"]
|
||||
exporter["OTLP Exporter"]
|
||||
processor["BatchProcessor"]
|
||||
end
|
||||
|
||||
subgraph collector["Collector Pipeline"]
|
||||
recv["Receivers"]
|
||||
proc["Processors"]
|
||||
exp["Exporters"]
|
||||
end
|
||||
|
||||
cfgFile --> parse
|
||||
cmake -->|"compile flag"| parse
|
||||
parse --> factory
|
||||
factory --> tracer
|
||||
tracer --> processor
|
||||
processor --> exporter
|
||||
exporter -->|"OTLP"| recv
|
||||
recv --> proc
|
||||
proc --> exp
|
||||
|
||||
style config fill:#e3f2fd,stroke:#1976d2
|
||||
style runtime fill:#e8f5e9,stroke:#388e3c
|
||||
style collector fill:#fff3e0,stroke:#ff9800
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5.8 Grafana Integration
|
||||
|
||||
Step-by-step instructions for integrating rippled traces with Grafana.
|
||||
|
||||
### 5.8.1 Data Source Configuration
|
||||
|
||||
#### Tempo (Recommended)
|
||||
|
||||
```yaml
|
||||
# grafana/provisioning/datasources/tempo.yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Tempo
|
||||
type: tempo
|
||||
access: proxy
|
||||
url: http://tempo:3200
|
||||
jsonData:
|
||||
httpMethod: GET
|
||||
tracesToLogs:
|
||||
datasourceUid: loki
|
||||
tags: ['service.name', 'xrpl.tx.hash']
|
||||
mappedTags: [{ key: 'trace_id', value: 'traceID' }]
|
||||
mapTagNamesEnabled: true
|
||||
filterByTraceID: true
|
||||
serviceMap:
|
||||
datasourceUid: prometheus
|
||||
nodeGraph:
|
||||
enabled: true
|
||||
search:
|
||||
hide: false
|
||||
lokiSearch:
|
||||
datasourceUid: loki
|
||||
```
|
||||
|
||||
#### Jaeger
|
||||
|
||||
```yaml
|
||||
# grafana/provisioning/datasources/jaeger.yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Jaeger
|
||||
type: jaeger
|
||||
access: proxy
|
||||
url: http://jaeger:16686
|
||||
jsonData:
|
||||
tracesToLogs:
|
||||
datasourceUid: loki
|
||||
tags: ['service.name']
|
||||
```
|
||||
|
||||
#### Elastic APM
|
||||
|
||||
```yaml
|
||||
# grafana/provisioning/datasources/elastic-apm.yaml
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Elasticsearch-APM
|
||||
type: elasticsearch
|
||||
access: proxy
|
||||
url: http://elasticsearch:9200
|
||||
database: "apm-*"
|
||||
jsonData:
|
||||
esVersion: "8.0.0"
|
||||
timeField: "@timestamp"
|
||||
logMessageField: message
|
||||
logLevelField: log.level
|
||||
```
|
||||
|
||||
### 5.8.2 Dashboard Provisioning
|
||||
|
||||
```yaml
|
||||
# grafana/provisioning/dashboards/dashboards.yaml
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: 'rippled-dashboards'
|
||||
orgId: 1
|
||||
folder: 'rippled'
|
||||
folderUid: 'rippled'
|
||||
type: file
|
||||
disableDeletion: false
|
||||
updateIntervalSeconds: 30
|
||||
options:
|
||||
path: /var/lib/grafana/dashboards/rippled
|
||||
```
|
||||
|
||||
### 5.8.3 Example Dashboard: RPC Performance
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "rippled RPC Performance",
|
||||
"uid": "rippled-rpc-performance",
|
||||
"panels": [
|
||||
{
|
||||
"title": "RPC Latency by Command",
|
||||
"type": "heatmap",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && span.xrpl.rpc.command != \"\"} | histogram_over_time(duration) by (span.xrpl.rpc.command)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "RPC Error Rate",
|
||||
"type": "timeseries",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && status.code=error} | rate() by (span.xrpl.rpc.command)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Top 10 Slowest RPC Commands",
|
||||
"type": "table",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && span.xrpl.rpc.command != \"\"} | avg(duration) by (span.xrpl.rpc.command) | topk(10)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 8 }
|
||||
},
|
||||
{
|
||||
"title": "Recent Traces",
|
||||
"type": "table",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\"}"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 16 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5.8.4 Example Dashboard: Transaction Tracing
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "rippled Transaction Tracing",
|
||||
"uid": "rippled-tx-tracing",
|
||||
"panels": [
|
||||
{
|
||||
"title": "Transaction Throughput",
|
||||
"type": "stat",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"tx.receive\"} | rate()"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Cross-Node Relay Count",
|
||||
"type": "timeseries",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"tx.relay\"} | avg(span.xrpl.tx.relay_count)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 4 }
|
||||
},
|
||||
{
|
||||
"title": "Transaction Validation Errors",
|
||||
"type": "table",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"tx.validate\" && status.code=error}"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 4 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 5.8.5 TraceQL Query Examples
|
||||
|
||||
Common queries for rippled traces:
|
||||
|
||||
```
|
||||
# Find all traces for a specific transaction hash
|
||||
{resource.service.name="rippled" && span.xrpl.tx.hash="ABC123..."}
|
||||
|
||||
# Find slow RPC commands (>100ms)
|
||||
{resource.service.name="rippled" && name=~"rpc.command.*"} | duration > 100ms
|
||||
|
||||
# Find consensus rounds taking >5 seconds
|
||||
{resource.service.name="rippled" && name="consensus.round"} | duration > 5s
|
||||
|
||||
# Find failed transactions with error details
|
||||
{resource.service.name="rippled" && name="tx.validate" && status.code=error}
|
||||
|
||||
# Find transactions relayed to many peers
|
||||
{resource.service.name="rippled" && name="tx.relay"} | span.xrpl.tx.relay_count > 10
|
||||
|
||||
# Compare latency across nodes
|
||||
{resource.service.name="rippled" && name="rpc.command.account_info"} | avg(duration) by (resource.service.instance.id)
|
||||
```
|
||||
|
||||
### 5.8.6 Correlation with PerfLog
|
||||
|
||||
To correlate OpenTelemetry traces with existing PerfLog data:
|
||||
|
||||
**Step 1: Configure Loki to ingest PerfLog**
|
||||
|
||||
```yaml
|
||||
# promtail-config.yaml
|
||||
scrape_configs:
|
||||
- job_name: rippled-perflog
|
||||
static_configs:
|
||||
- targets:
|
||||
- localhost
|
||||
labels:
|
||||
job: rippled
|
||||
__path__: /var/log/rippled/perf*.log
|
||||
pipeline_stages:
|
||||
- json:
|
||||
expressions:
|
||||
trace_id: trace_id
|
||||
ledger_seq: ledger_seq
|
||||
tx_hash: tx_hash
|
||||
- labels:
|
||||
trace_id:
|
||||
ledger_seq:
|
||||
tx_hash:
|
||||
```
|
||||
|
||||
**Step 2: Add trace_id to PerfLog entries**
|
||||
|
||||
Modify PerfLog to include trace_id when available:
|
||||
|
||||
```cpp
|
||||
// In PerfLog output, add trace_id from current span context
|
||||
void logPerf(Json::Value& entry) {
|
||||
auto span = opentelemetry::trace::GetSpan(
|
||||
opentelemetry::context::RuntimeContext::GetCurrent());
|
||||
if (span && span->GetContext().IsValid()) {
|
||||
char traceIdHex[33];
|
||||
span->GetContext().trace_id().ToLowerBase16(traceIdHex);
|
||||
entry["trace_id"] = std::string(traceIdHex, 32);
|
||||
}
|
||||
// ... existing logging
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Configure Grafana trace-to-logs link**
|
||||
|
||||
In Tempo data source configuration, set up the derived field:
|
||||
|
||||
```yaml
|
||||
jsonData:
|
||||
tracesToLogs:
|
||||
datasourceUid: loki
|
||||
tags: ['trace_id', 'xrpl.tx.hash']
|
||||
filterByTraceID: true
|
||||
filterBySpanID: false
|
||||
```
|
||||
|
||||
### 5.8.7 Correlation with Insight/StatsD Metrics
|
||||
|
||||
To correlate traces with existing Beast Insight metrics:
|
||||
|
||||
**Step 1: Export Insight metrics to Prometheus**
|
||||
|
||||
```yaml
|
||||
# prometheus.yaml
|
||||
scrape_configs:
|
||||
- job_name: 'rippled-statsd'
|
||||
static_configs:
|
||||
- targets: ['statsd-exporter:9102']
|
||||
```
|
||||
|
||||
**Step 2: Add exemplars to metrics**
|
||||
|
||||
OpenTelemetry SDK automatically adds exemplars (trace IDs) to metrics when using the Prometheus exporter. This links metrics spikes to specific traces.
|
||||
|
||||
**Step 3: Configure Grafana metric-to-trace link**
|
||||
|
||||
```yaml
|
||||
# In Prometheus data source
|
||||
jsonData:
|
||||
exemplarTraceIdDestinations:
|
||||
- name: trace_id
|
||||
datasourceUid: tempo
|
||||
```
|
||||
|
||||
**Step 4: Dashboard panel with exemplars**
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "RPC Latency with Trace Links",
|
||||
"type": "timeseries",
|
||||
"datasource": "Prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, rate(rippled_rpc_duration_seconds_bucket[5m]))",
|
||||
"exemplar": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
This allows clicking on metric data points to jump directly to the related trace.
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Code Samples](./04-code-samples.md)* | *Next: [Implementation Phases](./06-implementation-phases.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,506 +0,0 @@
|
||||
# Implementation Phases
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Configuration Reference](./05-configuration-reference.md) | [Observability Backends](./07-observability-backends.md)
|
||||
|
||||
---
|
||||
|
||||
## 6.1 Phase Overview
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title OpenTelemetry Implementation Timeline
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat Week %W
|
||||
|
||||
section Phase 1
|
||||
Core Infrastructure :p1, 2024-01-01, 2w
|
||||
SDK Integration :p1a, 2024-01-01, 4d
|
||||
Telemetry Interface :p1b, after p1a, 3d
|
||||
Configuration & CMake :p1c, after p1b, 3d
|
||||
Unit Tests :p1d, after p1c, 2d
|
||||
|
||||
section Phase 2
|
||||
RPC Tracing :p2, after p1, 2w
|
||||
HTTP Context Extraction :p2a, after p1, 2d
|
||||
RPC Handler Instrumentation :p2b, after p2a, 4d
|
||||
WebSocket Support :p2c, after p2b, 2d
|
||||
Integration Tests :p2d, after p2c, 2d
|
||||
|
||||
section Phase 3
|
||||
Transaction Tracing :p3, after p2, 2w
|
||||
Protocol Buffer Extension :p3a, after p2, 2d
|
||||
PeerImp Instrumentation :p3b, after p3a, 3d
|
||||
Relay Context Propagation :p3c, after p3b, 3d
|
||||
Multi-node Tests :p3d, after p3c, 2d
|
||||
|
||||
section Phase 4
|
||||
Consensus Tracing :p4, after p3, 2w
|
||||
Consensus Round Spans :p4a, after p3, 3d
|
||||
Proposal Handling :p4b, after p4a, 3d
|
||||
Validation Tests :p4c, after p4b, 4d
|
||||
|
||||
section Phase 5
|
||||
Documentation & Deploy :p5, after p4, 1w
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.2 Phase 1: Core Infrastructure (Weeks 1-2)
|
||||
|
||||
**Objective**: Establish foundational telemetry infrastructure
|
||||
|
||||
### Tasks
|
||||
|
||||
| Task | Description | Effort | Risk |
|
||||
| ---- | ----------------------------------------------------- | ------ | ------ |
|
||||
| 1.1 | Add OpenTelemetry C++ SDK to Conan/CMake | 2d | Low |
|
||||
| 1.2 | Implement `Telemetry` interface and factory | 2d | Low |
|
||||
| 1.3 | Implement `SpanGuard` RAII wrapper | 1d | Low |
|
||||
| 1.4 | Implement configuration parser | 1d | Low |
|
||||
| 1.5 | Integrate into `ApplicationImp` | 1d | Medium |
|
||||
| 1.6 | Add conditional compilation (`XRPL_ENABLE_TELEMETRY`) | 1d | Low |
|
||||
| 1.7 | Create `NullTelemetry` no-op implementation | 0.5d | Low |
|
||||
| 1.8 | Unit tests for core infrastructure | 1.5d | Low |
|
||||
|
||||
**Total Effort**: 10 days (2 developers)
|
||||
|
||||
### Exit Criteria
|
||||
|
||||
- [ ] OpenTelemetry SDK compiles and links
|
||||
- [ ] Telemetry can be enabled/disabled via config
|
||||
- [ ] Basic span creation works
|
||||
- [ ] No performance regression when disabled
|
||||
- [ ] Unit tests passing
|
||||
|
||||
---
|
||||
|
||||
## 6.3 Phase 2: RPC Tracing (Weeks 3-4)
|
||||
|
||||
**Objective**: Complete tracing for all RPC operations
|
||||
|
||||
### Tasks
|
||||
|
||||
| Task | Description | Effort | Risk |
|
||||
| ---- | -------------------------------------------------- | ------ | ------ |
|
||||
| 2.1 | Implement W3C Trace Context HTTP header extraction | 1d | Low |
|
||||
| 2.2 | Instrument `ServerHandler::onRequest()` | 1d | Low |
|
||||
| 2.3 | Instrument `RPCHandler::doCommand()` | 2d | Medium |
|
||||
| 2.4 | Add RPC-specific attributes | 1d | Low |
|
||||
| 2.5 | Instrument WebSocket handler | 1d | Medium |
|
||||
| 2.6 | Integration tests for RPC tracing | 2d | Low |
|
||||
| 2.7 | Performance benchmarks | 1d | Low |
|
||||
| 2.8 | Documentation | 1d | Low |
|
||||
|
||||
**Total Effort**: 10 days
|
||||
|
||||
### Exit Criteria
|
||||
|
||||
- [ ] All RPC commands traced
|
||||
- [ ] Trace context propagates from HTTP headers
|
||||
- [ ] WebSocket and HTTP both instrumented
|
||||
- [ ] <1ms overhead per RPC call
|
||||
- [ ] Integration tests passing
|
||||
|
||||
---
|
||||
|
||||
## 6.4 Phase 3: Transaction Tracing (Weeks 5-6)
|
||||
|
||||
**Objective**: Trace transaction lifecycle across network
|
||||
|
||||
### Tasks
|
||||
|
||||
| Task | Description | Effort | Risk |
|
||||
| ---- | --------------------------------------------- | ------ | ------ |
|
||||
| 3.1 | Define `TraceContext` Protocol Buffer message | 1d | Low |
|
||||
| 3.2 | Implement protobuf context serialization | 1d | Low |
|
||||
| 3.3 | Instrument `PeerImp::handleTransaction()` | 2d | Medium |
|
||||
| 3.4 | Instrument `NetworkOPs::submitTransaction()` | 1d | Medium |
|
||||
| 3.5 | Instrument HashRouter integration | 1d | Medium |
|
||||
| 3.6 | Implement relay context propagation | 2d | High |
|
||||
| 3.7 | Integration tests (multi-node) | 2d | Medium |
|
||||
| 3.8 | Performance benchmarks | 1d | Low |
|
||||
|
||||
**Total Effort**: 11 days
|
||||
|
||||
### Exit Criteria
|
||||
|
||||
- [ ] Transaction traces span across nodes
|
||||
- [ ] Trace context in Protocol Buffer messages
|
||||
- [ ] HashRouter deduplication visible in traces
|
||||
- [ ] Multi-node integration tests passing
|
||||
- [ ] <5% overhead on transaction throughput
|
||||
|
||||
---
|
||||
|
||||
## 6.5 Phase 4: Consensus Tracing (Weeks 7-8)
|
||||
|
||||
**Objective**: Full observability into consensus rounds
|
||||
|
||||
### Tasks
|
||||
|
||||
| Task | Description | Effort | Risk |
|
||||
| ---- | ---------------------------------------------- | ------ | ------ |
|
||||
| 4.1 | Instrument `RCLConsensusAdaptor::startRound()` | 1d | Medium |
|
||||
| 4.2 | Instrument phase transitions | 2d | Medium |
|
||||
| 4.3 | Instrument proposal handling | 2d | High |
|
||||
| 4.4 | Instrument validation handling | 1d | Medium |
|
||||
| 4.5 | Add consensus-specific attributes | 1d | Low |
|
||||
| 4.6 | Correlate with transaction traces | 1d | Medium |
|
||||
| 4.7 | Multi-validator integration tests | 2d | High |
|
||||
| 4.8 | Performance validation | 1d | Medium |
|
||||
|
||||
**Total Effort**: 11 days
|
||||
|
||||
### Exit Criteria
|
||||
|
||||
- [ ] Complete consensus round traces
|
||||
- [ ] Phase transitions visible
|
||||
- [ ] Proposals and validations traced
|
||||
- [ ] No impact on consensus timing
|
||||
- [ ] Multi-validator test network validated
|
||||
|
||||
---
|
||||
|
||||
## 6.6 Phase 5: Documentation & Deployment (Week 9)
|
||||
|
||||
**Objective**: Production readiness
|
||||
|
||||
### Tasks
|
||||
|
||||
| Task | Description | Effort | Risk |
|
||||
| ---- | ----------------------------- | ------ | ---- |
|
||||
| 5.1 | Operator runbook | 1d | Low |
|
||||
| 5.2 | Grafana dashboards | 1d | Low |
|
||||
| 5.3 | Alert definitions | 0.5d | Low |
|
||||
| 5.4 | Collector deployment examples | 0.5d | Low |
|
||||
| 5.5 | Developer documentation | 1d | Low |
|
||||
| 5.6 | Training materials | 0.5d | Low |
|
||||
| 5.7 | Final integration testing | 0.5d | Low |
|
||||
|
||||
**Total Effort**: 5 days
|
||||
|
||||
---
|
||||
|
||||
## 6.7 Risk Assessment
|
||||
|
||||
```mermaid
|
||||
quadrantChart
|
||||
title Risk Assessment Matrix
|
||||
x-axis Low Impact --> High Impact
|
||||
y-axis Low Likelihood --> High Likelihood
|
||||
quadrant-1 Monitor Closely
|
||||
quadrant-2 Mitigate Immediately
|
||||
quadrant-3 Accept Risk
|
||||
quadrant-4 Plan Mitigation
|
||||
|
||||
SDK Compatibility: [0.25, 0.2]
|
||||
Protocol Changes: [0.75, 0.65]
|
||||
Performance Overhead: [0.65, 0.45]
|
||||
Context Propagation: [0.5, 0.5]
|
||||
Memory Leaks: [0.8, 0.2]
|
||||
```
|
||||
|
||||
### Risk Details
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
| ------------------------------------ | ---------- | ------ | --------------------------------------- |
|
||||
| Protocol changes break compatibility | Medium | High | Use high field numbers, optional fields |
|
||||
| Performance overhead unacceptable | Medium | Medium | Sampling, conditional compilation |
|
||||
| Context propagation complexity | Medium | Medium | Phased rollout, extensive testing |
|
||||
| SDK compatibility issues | Low | Medium | Pin SDK version, fallback to no-op |
|
||||
| Memory leaks in long-running nodes | Low | High | Memory profiling, bounded queues |
|
||||
|
||||
---
|
||||
|
||||
## 6.8 Success Metrics
|
||||
|
||||
| Metric | Target | Measurement |
|
||||
| ------------------------ | ------------------------------ | --------------------- |
|
||||
| Trace coverage | >95% of transactions | Sampling verification |
|
||||
| CPU overhead | <3% | Benchmark tests |
|
||||
| Memory overhead | <5 MB | Memory profiling |
|
||||
| Latency impact (p99) | <2% | Performance tests |
|
||||
| Trace completeness | >99% spans with required attrs | Validation script |
|
||||
| Cross-node trace linkage | >90% of multi-hop transactions | Integration tests |
|
||||
|
||||
---
|
||||
|
||||
## 6.9 Effort Summary
|
||||
|
||||
```mermaid
|
||||
pie title Total Effort Distribution (47 developer-days)
|
||||
"Phase 1: Core Infrastructure" : 10
|
||||
"Phase 2: RPC Tracing" : 10
|
||||
"Phase 3: Transaction Tracing" : 11
|
||||
"Phase 4: Consensus Tracing" : 11
|
||||
"Phase 5: Documentation" : 5
|
||||
```
|
||||
|
||||
### Resource Requirements
|
||||
|
||||
| Phase | Developers | Duration | Total Effort |
|
||||
| --------- | ---------- | ----------- | ------------ |
|
||||
| 1 | 2 | 2 weeks | 10 days |
|
||||
| 2 | 1-2 | 2 weeks | 10 days |
|
||||
| 3 | 2 | 2 weeks | 11 days |
|
||||
| 4 | 2 | 2 weeks | 11 days |
|
||||
| 5 | 1 | 1 week | 5 days |
|
||||
| **Total** | **2** | **9 weeks** | **47 days** |
|
||||
|
||||
---
|
||||
|
||||
## 6.10 Quick Wins and Crawl-Walk-Run Strategy
|
||||
|
||||
This section outlines a prioritized approach to maximize ROI with minimal initial investment.
|
||||
|
||||
### 6.10.1 Crawl-Walk-Run Overview
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph crawl["🐢 CRAWL (Week 1-2)"]
|
||||
c1[Core SDK Setup]
|
||||
c2[RPC Tracing Only]
|
||||
c3[Single Node]
|
||||
end
|
||||
|
||||
subgraph walk["🚶 WALK (Week 3-5)"]
|
||||
w1[Transaction Tracing]
|
||||
w2[Cross-Node Context]
|
||||
w3[Basic Dashboards]
|
||||
end
|
||||
|
||||
subgraph run["🏃 RUN (Week 6-9)"]
|
||||
r1[Consensus Tracing]
|
||||
r2[Full Correlation]
|
||||
r3[Production Deploy]
|
||||
end
|
||||
|
||||
crawl --> walk --> run
|
||||
|
||||
style crawl fill:#e8f5e9,stroke:#388e3c
|
||||
style walk fill:#fff3e0,stroke:#ff9800
|
||||
style run fill:#e3f2fd,stroke:#1976d2
|
||||
```
|
||||
|
||||
### 6.10.2 Quick Wins (Immediate Value)
|
||||
|
||||
| Quick Win | Effort | Value | When to Deploy |
|
||||
| ------------------------------ | -------- | ------ | -------------- |
|
||||
| **RPC Command Tracing** | 2 days | High | Week 2 |
|
||||
| **RPC Latency Histograms** | 0.5 days | High | Week 2 |
|
||||
| **Error Rate Dashboard** | 0.5 days | Medium | Week 2 |
|
||||
| **Transaction Submit Tracing** | 1 day | High | Week 3 |
|
||||
| **Consensus Round Duration** | 1 day | Medium | Week 6 |
|
||||
|
||||
### 6.10.3 CRAWL Phase (Weeks 1-2)
|
||||
|
||||
**Goal**: Get basic tracing working with minimal code changes.
|
||||
|
||||
**What You Get**:
|
||||
- RPC request/response traces for all commands
|
||||
- Latency breakdown per RPC command
|
||||
- Error visibility with stack traces
|
||||
- Basic Grafana dashboard
|
||||
|
||||
**Code Changes**: ~15 lines in `ServerHandler.cpp`, ~40 lines in new telemetry module
|
||||
|
||||
**Why Start Here**:
|
||||
- RPC is the lowest-risk, highest-visibility component
|
||||
- Immediate value for debugging client issues
|
||||
- No cross-node complexity
|
||||
- Single file modification to existing code
|
||||
|
||||
### 6.10.4 WALK Phase (Weeks 3-5)
|
||||
|
||||
**Goal**: Add transaction lifecycle tracing across nodes.
|
||||
|
||||
**What You Get**:
|
||||
- End-to-end transaction traces from submit to relay
|
||||
- Cross-node correlation (see transaction path)
|
||||
- HashRouter deduplication visibility
|
||||
- Relay latency metrics
|
||||
|
||||
**Code Changes**: ~120 lines across 4 files, plus protobuf extension
|
||||
|
||||
**Why Do This Second**:
|
||||
- Builds on RPC tracing (transactions submitted via RPC)
|
||||
- Moderate complexity (requires context propagation)
|
||||
- High value for debugging transaction issues
|
||||
|
||||
### 6.10.5 RUN Phase (Weeks 6-9)
|
||||
|
||||
**Goal**: Full observability including consensus.
|
||||
|
||||
**What You Get**:
|
||||
- Complete consensus round visibility
|
||||
- Phase transition timing
|
||||
- Validator proposal tracking
|
||||
- Full end-to-end traces (client → RPC → TX → consensus → ledger)
|
||||
|
||||
**Code Changes**: ~100 lines across 3 consensus files
|
||||
|
||||
**Why Do This Last**:
|
||||
- Highest complexity (consensus is critical path)
|
||||
- Requires thorough testing
|
||||
- Lower relative value (consensus issues are rarer)
|
||||
|
||||
### 6.10.6 ROI Prioritization Matrix
|
||||
|
||||
```mermaid
|
||||
quadrantChart
|
||||
title Implementation ROI Matrix
|
||||
x-axis Low Effort --> High Effort
|
||||
y-axis Low Value --> High Value
|
||||
quadrant-1 Quick Wins - Do First
|
||||
quadrant-2 Major Projects - Plan Carefully
|
||||
quadrant-3 Nice to Have - Optional
|
||||
quadrant-4 Time Sinks - Avoid
|
||||
|
||||
RPC Tracing: [0.15, 0.9]
|
||||
TX Submit Trace: [0.25, 0.85]
|
||||
TX Relay Trace: [0.5, 0.8]
|
||||
Consensus Trace: [0.7, 0.75]
|
||||
Peer Message Trace: [0.85, 0.3]
|
||||
Ledger Acquire: [0.55, 0.5]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6.11 Definition of Done
|
||||
|
||||
Clear, measurable criteria for each phase.
|
||||
|
||||
### 6.11.1 Phase 1: Core Infrastructure
|
||||
|
||||
| Criterion | Measurement | Target |
|
||||
| --------------- | ---------------------------------------------------------- | ---------------------------- |
|
||||
| SDK Integration | `cmake --build` succeeds with `-DXRPL_ENABLE_TELEMETRY=ON` | ✅ Compiles |
|
||||
| Runtime Toggle | `enabled=0` produces zero overhead | <0.1% CPU difference |
|
||||
| Span Creation | Unit test creates and exports span | Span appears in Jaeger |
|
||||
| Configuration | All config options parsed correctly | Config validation tests pass |
|
||||
| Documentation | Developer guide exists | PR approved |
|
||||
|
||||
**Definition of Done**: All criteria met, PR merged, no regressions in CI.
|
||||
|
||||
### 6.11.2 Phase 2: RPC Tracing
|
||||
|
||||
| Criterion | Measurement | Target |
|
||||
| ------------------ | ---------------------------------- | -------------------------- |
|
||||
| Coverage | All RPC commands instrumented | 100% of commands |
|
||||
| Context Extraction | traceparent header propagates | Integration test passes |
|
||||
| Attributes | Command, status, duration recorded | Validation script confirms |
|
||||
| Performance | RPC latency overhead | <1ms p99 |
|
||||
| Dashboard | Grafana dashboard deployed | Screenshot in docs |
|
||||
|
||||
**Definition of Done**: RPC traces visible in Jaeger/Tempo for all commands, dashboard shows latency distribution.
|
||||
|
||||
### 6.11.3 Phase 3: Transaction Tracing
|
||||
|
||||
| Criterion | Measurement | Target |
|
||||
| ---------------- | ------------------------------- | ---------------------------------- |
|
||||
| Local Trace | Submit → validate → TxQ traced | Single-node test passes |
|
||||
| Cross-Node | Context propagates via protobuf | Multi-node test passes |
|
||||
| Relay Visibility | relay_count attribute correct | Spot check 100 txs |
|
||||
| HashRouter | Deduplication visible in trace | Duplicate txs show suppressed=true |
|
||||
| Performance | TX throughput overhead | <5% degradation |
|
||||
|
||||
**Definition of Done**: Transaction traces span 3+ nodes in test network, performance within bounds.
|
||||
|
||||
### 6.11.4 Phase 4: Consensus Tracing
|
||||
|
||||
| Criterion | Measurement | Target |
|
||||
| -------------------- | ----------------------------- | ------------------------- |
|
||||
| Round Tracing | startRound creates root span | Unit test passes |
|
||||
| Phase Visibility | All phases have child spans | Integration test confirms |
|
||||
| Proposer Attribution | Proposer ID in attributes | Spot check 50 rounds |
|
||||
| Timing Accuracy | Phase durations match PerfLog | <5% variance |
|
||||
| No Consensus Impact | Round timing unchanged | Performance test passes |
|
||||
|
||||
**Definition of Done**: Consensus rounds fully traceable, no impact on consensus timing.
|
||||
|
||||
### 6.11.5 Phase 5: Production Deployment
|
||||
|
||||
| Criterion | Measurement | Target |
|
||||
| ------------ | ---------------------------- | -------------------------- |
|
||||
| Collector HA | Multiple collectors deployed | No single point of failure |
|
||||
| Sampling | Tail sampling configured | 10% base + errors + slow |
|
||||
| Retention | Data retained per policy | 7 days hot, 30 days warm |
|
||||
| Alerting | Alerts configured | Error spike, high latency |
|
||||
| Runbook | Operator documentation | Approved by ops team |
|
||||
| Training | Team trained | Session completed |
|
||||
|
||||
**Definition of Done**: Telemetry running in production, operators trained, alerts active.
|
||||
|
||||
### 6.11.6 Success Metrics Summary
|
||||
|
||||
| Phase | Primary Metric | Secondary Metric | Deadline |
|
||||
| ------- | ---------------------- | --------------------------- | ------------- |
|
||||
| Phase 1 | SDK compiles and runs | Zero overhead when disabled | End of Week 2 |
|
||||
| Phase 2 | 100% RPC coverage | <1ms latency overhead | End of Week 4 |
|
||||
| Phase 3 | Cross-node traces work | <5% throughput impact | End of Week 6 |
|
||||
| Phase 4 | Consensus fully traced | No consensus timing impact | End of Week 8 |
|
||||
| Phase 5 | Production deployment | Operators trained | End of Week 9 |
|
||||
|
||||
---
|
||||
|
||||
## 6.12 Recommended Implementation Order
|
||||
|
||||
Based on ROI analysis, implement in this exact order:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph week1["Week 1"]
|
||||
t1[1. OpenTelemetry SDK<br/>Conan/CMake integration]
|
||||
t2[2. Telemetry interface<br/>SpanGuard, config]
|
||||
end
|
||||
|
||||
subgraph week2["Week 2"]
|
||||
t3[3. RPC ServerHandler<br/>instrumentation]
|
||||
t4[4. Basic Jaeger setup<br/>for testing]
|
||||
end
|
||||
|
||||
subgraph week3["Week 3"]
|
||||
t5[5. Transaction submit<br/>tracing]
|
||||
t6[6. Grafana dashboard<br/>v1]
|
||||
end
|
||||
|
||||
subgraph week4["Week 4"]
|
||||
t7[7. Protobuf context<br/>extension]
|
||||
t8[8. PeerImp tx.relay<br/>instrumentation]
|
||||
end
|
||||
|
||||
subgraph week5["Week 5"]
|
||||
t9[9. Multi-node<br/>integration tests]
|
||||
t10[10. Performance<br/>benchmarks]
|
||||
end
|
||||
|
||||
subgraph week6_8["Weeks 6-8"]
|
||||
t11[11. Consensus<br/>instrumentation]
|
||||
t12[12. Full integration<br/>testing]
|
||||
end
|
||||
|
||||
subgraph week9["Week 9"]
|
||||
t13[13. Production<br/>deployment]
|
||||
t14[14. Documentation<br/>& training]
|
||||
end
|
||||
|
||||
t1 --> t2 --> t3 --> t4
|
||||
t4 --> t5 --> t6
|
||||
t6 --> t7 --> t8
|
||||
t8 --> t9 --> t10
|
||||
t10 --> t11 --> t12
|
||||
t12 --> t13 --> t14
|
||||
|
||||
style week1 fill:#e8f5e9,stroke:#388e3c
|
||||
style week2 fill:#e8f5e9,stroke:#388e3c
|
||||
style week3 fill:#fff3e0,stroke:#ff9800
|
||||
style week4 fill:#fff3e0,stroke:#ff9800
|
||||
style week5 fill:#fff3e0,stroke:#ff9800
|
||||
style week6_8 fill:#e3f2fd,stroke:#1976d2
|
||||
style week9 fill:#f3e5f5,stroke:#7b1fa2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Configuration Reference](./05-configuration-reference.md)* | *Next: [Observability Backends](./07-observability-backends.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,559 +0,0 @@
|
||||
# Observability Backend Recommendations
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Implementation Phases](./06-implementation-phases.md) | [Appendix](./08-appendix.md)
|
||||
|
||||
---
|
||||
|
||||
## 7.1 Development/Testing Backends
|
||||
|
||||
| Backend | Pros | Cons | Use Case |
|
||||
| ---------- | ------------------- | ----------------- | ----------------- |
|
||||
| **Jaeger** | Easy setup, good UI | Limited retention | Local dev, CI |
|
||||
| **Zipkin** | Simple, lightweight | Basic features | Quick prototyping |
|
||||
|
||||
### Quick Start with Jaeger
|
||||
|
||||
```bash
|
||||
# Start Jaeger with OTLP support
|
||||
docker run -d --name jaeger \
|
||||
-e COLLECTOR_OTLP_ENABLED=true \
|
||||
-p 16686:16686 \
|
||||
-p 4317:4317 \
|
||||
-p 4318:4318 \
|
||||
jaegertracing/all-in-one:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7.2 Production Backends
|
||||
|
||||
| Backend | Pros | Cons | Use Case |
|
||||
| ----------------- | ----------------------------------------- | ------------------ | --------------------------- |
|
||||
| **Grafana Tempo** | Cost-effective, Grafana integration | Newer project | Most production deployments |
|
||||
| **Elastic APM** | Full observability stack, log correlation | Resource intensive | Existing Elastic users |
|
||||
| **Honeycomb** | Excellent query, high cardinality | SaaS cost | Deep debugging needs |
|
||||
| **Datadog APM** | Full platform, easy setup | SaaS cost | Enterprise with budget |
|
||||
|
||||
### Backend Selection Flowchart
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
start[Select Backend] --> budget{Budget<br/>Constraints?}
|
||||
|
||||
budget -->|Yes| oss[Open Source]
|
||||
budget -->|No| saas{Prefer<br/>SaaS?}
|
||||
|
||||
oss --> existing{Existing<br/>Stack?}
|
||||
existing -->|Grafana| tempo[Grafana Tempo]
|
||||
existing -->|Elastic| elastic[Elastic APM]
|
||||
existing -->|None| tempo
|
||||
|
||||
saas -->|Yes| enterprise{Enterprise<br/>Support?}
|
||||
saas -->|No| oss
|
||||
|
||||
enterprise -->|Yes| datadog[Datadog APM]
|
||||
enterprise -->|No| honeycomb[Honeycomb]
|
||||
|
||||
tempo --> final[Configure Collector]
|
||||
elastic --> final
|
||||
honeycomb --> final
|
||||
datadog --> final
|
||||
|
||||
style tempo fill:#e8f5e9,stroke:#388e3c
|
||||
style elastic fill:#fff3e0,stroke:#ff9800
|
||||
style honeycomb fill:#e3f2fd,stroke:#1976d2
|
||||
style datadog fill:#f3e5f5,stroke:#7b1fa2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7.3 Recommended Production Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph validators["Validator Nodes"]
|
||||
v1[rippled<br/>Validator 1]
|
||||
v2[rippled<br/>Validator 2]
|
||||
end
|
||||
|
||||
subgraph stock["Stock Nodes"]
|
||||
s1[rippled<br/>Stock 1]
|
||||
s2[rippled<br/>Stock 2]
|
||||
end
|
||||
|
||||
subgraph collector["OTel Collector Cluster"]
|
||||
c1[Collector<br/>DC1]
|
||||
c2[Collector<br/>DC2]
|
||||
end
|
||||
|
||||
subgraph backends["Storage Backends"]
|
||||
tempo[(Grafana<br/>Tempo)]
|
||||
elastic[(Elastic<br/>APM)]
|
||||
archive[(S3/GCS<br/>Archive)]
|
||||
end
|
||||
|
||||
subgraph ui["Visualization"]
|
||||
grafana[Grafana<br/>Dashboards]
|
||||
end
|
||||
|
||||
v1 -->|OTLP| c1
|
||||
v2 -->|OTLP| c1
|
||||
s1 -->|OTLP| c2
|
||||
s2 -->|OTLP| c2
|
||||
|
||||
c1 --> tempo
|
||||
c1 --> elastic
|
||||
c2 --> tempo
|
||||
c2 --> archive
|
||||
|
||||
tempo --> grafana
|
||||
elastic --> grafana
|
||||
|
||||
style validators fill:#ffebee,stroke:#c62828
|
||||
style stock fill:#e3f2fd,stroke:#1976d2
|
||||
style collector fill:#fff3e0,stroke:#ff9800
|
||||
style backends fill:#e8f5e9,stroke:#388e3c
|
||||
style ui fill:#f3e5f5,stroke:#7b1fa2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7.4 Architecture Considerations
|
||||
|
||||
### 7.4.1 Collector Placement
|
||||
|
||||
| Strategy | Description | Pros | Cons |
|
||||
| ------------- | -------------------- | ------------------------ | ----------------------- |
|
||||
| **Sidecar** | Collector per node | Isolation, simple config | Resource overhead |
|
||||
| **DaemonSet** | Collector per host | Shared resources | Complexity |
|
||||
| **Gateway** | Central collector(s) | Centralized processing | Single point of failure |
|
||||
|
||||
**Recommendation**: Use **Gateway** pattern with regional collectors for rippled networks:
|
||||
- One collector cluster per datacenter/region
|
||||
- Tail-based sampling at collector level
|
||||
- Multiple export destinations for redundancy
|
||||
|
||||
### 7.4.2 Sampling Strategy
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph head["Head Sampling (Node)"]
|
||||
hs[10% probabilistic]
|
||||
end
|
||||
|
||||
subgraph tail["Tail Sampling (Collector)"]
|
||||
ts1[Keep all errors]
|
||||
ts2[Keep slow >5s]
|
||||
ts3[Keep 10% rest]
|
||||
end
|
||||
|
||||
head --> tail
|
||||
|
||||
ts1 --> final[Final Traces]
|
||||
ts2 --> final
|
||||
ts3 --> final
|
||||
```
|
||||
|
||||
### 7.4.3 Data Retention
|
||||
|
||||
| Environment | Hot Storage | Warm Storage | Cold Archive |
|
||||
| ----------- | ----------- | ------------ | ------------ |
|
||||
| Development | 24 hours | N/A | N/A |
|
||||
| Staging | 7 days | N/A | N/A |
|
||||
| Production | 7 days | 30 days | 1 year |
|
||||
|
||||
---
|
||||
|
||||
## 7.5 Integration Checklist
|
||||
|
||||
- [ ] Choose primary backend (Tempo recommended for cost/features)
|
||||
- [ ] Deploy collector cluster with high availability
|
||||
- [ ] Configure tail-based sampling for error/latency traces
|
||||
- [ ] Set up Grafana dashboards for trace visualization
|
||||
- [ ] Configure alerts for trace anomalies
|
||||
- [ ] Establish data retention policies
|
||||
- [ ] Test trace correlation with logs and metrics
|
||||
|
||||
---
|
||||
|
||||
## 7.6 Grafana Dashboard Examples
|
||||
|
||||
Pre-built dashboards for rippled observability.
|
||||
|
||||
### 7.6.1 Consensus Health Dashboard
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "rippled Consensus Health",
|
||||
"uid": "rippled-consensus-health",
|
||||
"tags": ["rippled", "consensus", "tracing"],
|
||||
"panels": [
|
||||
{
|
||||
"title": "Consensus Round Duration",
|
||||
"type": "timeseries",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"consensus.round\"} | avg(duration) by (resource.service.instance.id)"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "ms",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 4000 },
|
||||
{ "color": "red", "value": 5000 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Phase Duration Breakdown",
|
||||
"type": "barchart",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=~\"consensus.phase.*\"} | avg(duration) by (name)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Proposers per Round",
|
||||
"type": "stat",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"consensus.round\"} | avg(span.xrpl.consensus.proposers)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 4, "w": 6, "x": 0, "y": 8 }
|
||||
},
|
||||
{
|
||||
"title": "Recent Slow Rounds (>5s)",
|
||||
"type": "table",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"consensus.round\"} | duration > 5s"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 8, "w": 24, "x": 0, "y": 12 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 7.6.2 Node Overview Dashboard
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "rippled Node Overview",
|
||||
"uid": "rippled-node-overview",
|
||||
"panels": [
|
||||
{
|
||||
"title": "Active Nodes",
|
||||
"type": "stat",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\"} | count_over_time() by (resource.service.instance.id) | count()"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 4, "w": 4, "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Total Transactions (1h)",
|
||||
"type": "stat",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"tx.receive\"} | count()"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 4, "w": 4, "x": 4, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Error Rate",
|
||||
"type": "gauge",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && status.code=error} | rate() / {resource.service.name=\"rippled\"} | rate() * 100"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "percent",
|
||||
"max": 10,
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 1 },
|
||||
{ "color": "red", "value": 5 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 8, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Service Map",
|
||||
"type": "nodeGraph",
|
||||
"datasource": "Tempo",
|
||||
"gridPos": { "h": 12, "w": 12, "x": 12, "y": 0 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 7.6.3 Alert Rules
|
||||
|
||||
```yaml
|
||||
# grafana/provisioning/alerting/rippled-alerts.yaml
|
||||
apiVersion: 1
|
||||
|
||||
groups:
|
||||
- name: rippled-tracing-alerts
|
||||
folder: rippled
|
||||
interval: 1m
|
||||
rules:
|
||||
- uid: consensus-slow
|
||||
title: Consensus Round Slow
|
||||
condition: A
|
||||
data:
|
||||
- refId: A
|
||||
datasourceUid: tempo
|
||||
model:
|
||||
queryType: traceql
|
||||
query: '{resource.service.name="rippled" && name="consensus.round"} | avg(duration) > 5s'
|
||||
for: 5m
|
||||
annotations:
|
||||
summary: Consensus rounds taking >5 seconds
|
||||
description: "Consensus duration: {{ $value }}ms"
|
||||
labels:
|
||||
severity: warning
|
||||
|
||||
- uid: rpc-error-spike
|
||||
title: RPC Error Rate Spike
|
||||
condition: B
|
||||
data:
|
||||
- refId: B
|
||||
datasourceUid: tempo
|
||||
model:
|
||||
queryType: traceql
|
||||
query: '{resource.service.name="rippled" && name=~"rpc.command.*" && status.code=error} | rate() > 0.05'
|
||||
for: 2m
|
||||
annotations:
|
||||
summary: RPC error rate >5%
|
||||
labels:
|
||||
severity: critical
|
||||
|
||||
- uid: tx-throughput-drop
|
||||
title: Transaction Throughput Drop
|
||||
condition: C
|
||||
data:
|
||||
- refId: C
|
||||
datasourceUid: tempo
|
||||
model:
|
||||
queryType: traceql
|
||||
query: '{resource.service.name="rippled" && name="tx.receive"} | rate() < 10'
|
||||
for: 10m
|
||||
annotations:
|
||||
summary: Transaction throughput below threshold
|
||||
labels:
|
||||
severity: warning
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7.7 PerfLog and Insight Correlation
|
||||
|
||||
How to correlate OpenTelemetry traces with existing rippled observability.
|
||||
|
||||
### 7.7.1 Correlation Architecture
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph rippled["rippled Node"]
|
||||
otel[OpenTelemetry<br/>Spans]
|
||||
perflog[PerfLog<br/>JSON Logs]
|
||||
insight[Beast Insight<br/>StatsD Metrics]
|
||||
end
|
||||
|
||||
subgraph collectors["Data Collection"]
|
||||
otelc[OTel Collector]
|
||||
promtail[Promtail/Fluentd]
|
||||
statsd[StatsD Exporter]
|
||||
end
|
||||
|
||||
subgraph storage["Storage"]
|
||||
tempo[(Tempo)]
|
||||
loki[(Loki)]
|
||||
prom[(Prometheus)]
|
||||
end
|
||||
|
||||
subgraph grafana["Grafana"]
|
||||
traces[Trace View]
|
||||
logs[Log View]
|
||||
metrics[Metrics View]
|
||||
corr[Correlation<br/>Panel]
|
||||
end
|
||||
|
||||
otel -->|OTLP| otelc --> tempo
|
||||
perflog -->|JSON| promtail --> loki
|
||||
insight -->|StatsD| statsd --> prom
|
||||
|
||||
tempo --> traces
|
||||
loki --> logs
|
||||
prom --> metrics
|
||||
|
||||
traces --> corr
|
||||
logs --> corr
|
||||
metrics --> corr
|
||||
|
||||
style corr fill:#f3e5f5,stroke:#7b1fa2
|
||||
```
|
||||
|
||||
### 7.7.2 Correlation Fields
|
||||
|
||||
| Source | Field | Link To | Purpose |
|
||||
| ----------- | --------------------------- | ------------- | -------------------------- |
|
||||
| **Trace** | `trace_id` | Logs | Find log entries for trace |
|
||||
| **Trace** | `xrpl.tx.hash` | Logs, Metrics | Find TX-related data |
|
||||
| **Trace** | `xrpl.consensus.ledger.seq` | Logs | Find ledger-related logs |
|
||||
| **PerfLog** | `trace_id` (new) | Traces | Jump to trace from log |
|
||||
| **PerfLog** | `ledger_seq` | Traces | Find consensus trace |
|
||||
| **Insight** | `exemplar.trace_id` | Traces | Jump from metric spike |
|
||||
|
||||
### 7.7.3 Example: Debugging a Slow Transaction
|
||||
|
||||
**Step 1: Find the trace**
|
||||
```
|
||||
# In Grafana Explore with Tempo
|
||||
{resource.service.name="rippled" && span.xrpl.tx.hash="ABC123..."}
|
||||
```
|
||||
|
||||
**Step 2: Get the trace_id from the trace view**
|
||||
```
|
||||
Trace ID: 4bf92f3577b34da6a3ce929d0e0e4736
|
||||
```
|
||||
|
||||
**Step 3: Find related PerfLog entries**
|
||||
```
|
||||
# In Grafana Explore with Loki
|
||||
{job="rippled"} |= "4bf92f3577b34da6a3ce929d0e0e4736"
|
||||
```
|
||||
|
||||
**Step 4: Check Insight metrics for the time window**
|
||||
```
|
||||
# In Grafana with Prometheus
|
||||
rate(rippled_tx_applied_total[1m])
|
||||
@ timestamp_from_trace
|
||||
```
|
||||
|
||||
### 7.7.4 Unified Dashboard Example
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "rippled Unified Observability",
|
||||
"uid": "rippled-unified",
|
||||
"panels": [
|
||||
{
|
||||
"title": "Transaction Latency (Traces)",
|
||||
"type": "timeseries",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\" && name=\"tx.receive\"} | histogram_over_time(duration)"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 6, "w": 8, "x": 0, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Transaction Rate (Metrics)",
|
||||
"type": "timeseries",
|
||||
"datasource": "Prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(rippled_tx_received_total[5m])",
|
||||
"legendFormat": "{{ instance }}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"links": [
|
||||
{
|
||||
"title": "View traces",
|
||||
"url": "/explore?left={\"datasource\":\"Tempo\",\"query\":\"{resource.service.name=\\\"rippled\\\" && name=\\\"tx.receive\\\"}\"}"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 6, "w": 8, "x": 8, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Recent Logs",
|
||||
"type": "logs",
|
||||
"datasource": "Loki",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "{job=\"rippled\"} | json"
|
||||
}
|
||||
],
|
||||
"gridPos": { "h": 6, "w": 8, "x": 16, "y": 0 }
|
||||
},
|
||||
{
|
||||
"title": "Trace Search",
|
||||
"type": "table",
|
||||
"datasource": "Tempo",
|
||||
"targets": [
|
||||
{
|
||||
"queryType": "traceql",
|
||||
"query": "{resource.service.name=\"rippled\"}"
|
||||
}
|
||||
],
|
||||
"fieldConfig": {
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": { "id": "byName", "options": "traceID" },
|
||||
"properties": [
|
||||
{
|
||||
"id": "links",
|
||||
"value": [
|
||||
{
|
||||
"title": "View trace",
|
||||
"url": "/explore?left={\"datasource\":\"Tempo\",\"query\":\"${__value.raw}\"}"
|
||||
},
|
||||
{
|
||||
"title": "View logs",
|
||||
"url": "/explore?left={\"datasource\":\"Loki\",\"query\":\"{job=\\\"rippled\\\"} |= \\\"${__value.raw}\\\"\"}"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": { "h": 12, "w": 24, "x": 0, "y": 6 }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Implementation Phases](./06-implementation-phases.md)* | *Next: [Appendix](./08-appendix.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
@@ -1,126 +0,0 @@
|
||||
# Appendix
|
||||
|
||||
> **Parent Document**: [OpenTelemetryPlan.md](./OpenTelemetryPlan.md)
|
||||
> **Related**: [Observability Backends](./07-observability-backends.md)
|
||||
|
||||
---
|
||||
|
||||
## 8.1 Glossary
|
||||
|
||||
| Term | Definition |
|
||||
| -------------- | ---------------------------------------------------------- |
|
||||
| **Span** | A unit of work with start/end time, name, and attributes |
|
||||
| **Trace** | A collection of spans representing a complete request flow |
|
||||
| **Trace ID** | 128-bit unique identifier for a trace |
|
||||
| **Span ID** | 64-bit unique identifier for a span within a trace |
|
||||
| **Context** | Carrier for trace/span IDs across boundaries |
|
||||
| **Propagator** | Component that injects/extracts context |
|
||||
| **Sampler** | Decides which traces to record |
|
||||
| **Exporter** | Sends spans to backend |
|
||||
| **Collector** | Receives, processes, and forwards telemetry |
|
||||
| **OTLP** | OpenTelemetry Protocol (wire format) |
|
||||
| **W3C Trace Context** | Standard HTTP headers for trace propagation |
|
||||
| **Baggage** | Key-value pairs propagated across service boundaries |
|
||||
| **Resource** | Entity producing telemetry (service, host, etc.) |
|
||||
| **Instrumentation** | Code that creates telemetry data |
|
||||
|
||||
### rippled-Specific Terms
|
||||
|
||||
| Term | Definition |
|
||||
| -------------- | ---------------------------------------------------------- |
|
||||
| **Overlay** | P2P network layer managing peer connections |
|
||||
| **Consensus** | XRP Ledger consensus algorithm (RCL) |
|
||||
| **Proposal** | Validator's suggested transaction set for a ledger |
|
||||
| **Validation** | Validator's signature on a closed ledger |
|
||||
| **HashRouter** | Component for transaction deduplication |
|
||||
| **JobQueue** | Thread pool for asynchronous task execution |
|
||||
| **PerfLog** | Existing performance logging system in rippled |
|
||||
| **Beast Insight** | Existing metrics framework in rippled |
|
||||
|
||||
---
|
||||
|
||||
## 8.2 Span Hierarchy Visualization
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph trace["Trace: Transaction Lifecycle"]
|
||||
rpc["rpc.submit<br/>(entry point)"]
|
||||
validate["tx.validate"]
|
||||
relay["tx.relay<br/>(parent span)"]
|
||||
|
||||
subgraph peers["Peer Spans"]
|
||||
p1["peer.send<br/>Peer A"]
|
||||
p2["peer.send<br/>Peer B"]
|
||||
p3["peer.send<br/>Peer C"]
|
||||
end
|
||||
|
||||
consensus["consensus.round"]
|
||||
apply["tx.apply"]
|
||||
end
|
||||
|
||||
rpc --> validate
|
||||
validate --> relay
|
||||
relay --> p1
|
||||
relay --> p2
|
||||
relay --> p3
|
||||
p1 -.->|"context propagation"| consensus
|
||||
consensus --> apply
|
||||
|
||||
style trace fill:#f5f5f5,stroke:#333
|
||||
style peers fill:#e3f2fd,stroke:#1976d2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8.3 References
|
||||
|
||||
### OpenTelemetry Resources
|
||||
|
||||
1. [OpenTelemetry C++ SDK](https://github.com/open-telemetry/opentelemetry-cpp)
|
||||
2. [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/)
|
||||
3. [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/)
|
||||
4. [OTLP Protocol Specification](https://opentelemetry.io/docs/specs/otlp/)
|
||||
|
||||
### Standards
|
||||
|
||||
5. [W3C Trace Context](https://www.w3.org/TR/trace-context/)
|
||||
6. [W3C Baggage](https://www.w3.org/TR/baggage/)
|
||||
7. [Protocol Buffers](https://protobuf.dev/)
|
||||
|
||||
### rippled Resources
|
||||
|
||||
8. [rippled Source Code](https://github.com/XRPLF/rippled)
|
||||
9. [XRP Ledger Documentation](https://xrpl.org/docs/)
|
||||
10. [rippled Overlay README](https://github.com/XRPLF/rippled/blob/develop/src/xrpld/overlay/README.md)
|
||||
11. [rippled RPC README](https://github.com/XRPLF/rippled/blob/develop/src/xrpld/rpc/README.md)
|
||||
12. [rippled Consensus README](https://github.com/XRPLF/rippled/blob/develop/src/xrpld/app/consensus/README.md)
|
||||
|
||||
---
|
||||
|
||||
## 8.4 Version History
|
||||
|
||||
| Version | Date | Author | Changes |
|
||||
| ------- | ---------- | ------ | --------------------------- |
|
||||
| 1.0 | 2026-02-12 | - | Initial implementation plan |
|
||||
| 1.1 | 2026-02-13 | - | Refactored into modular documents |
|
||||
|
||||
---
|
||||
|
||||
## 8.5 Document Index
|
||||
|
||||
| Document | Description |
|
||||
| -------- | ----------- |
|
||||
| [OpenTelemetryPlan.md](./OpenTelemetryPlan.md) | Master overview and executive summary |
|
||||
| [01-architecture-analysis.md](./01-architecture-analysis.md) | rippled architecture and trace points |
|
||||
| [02-design-decisions.md](./02-design-decisions.md) | SDK selection, exporters, span conventions |
|
||||
| [03-implementation-strategy.md](./03-implementation-strategy.md) | Directory structure, performance analysis |
|
||||
| [04-code-samples.md](./04-code-samples.md) | C++ code examples for all components |
|
||||
| [05-configuration-reference.md](./05-configuration-reference.md) | rippled config, CMake, Collector configs |
|
||||
| [06-implementation-phases.md](./06-implementation-phases.md) | Timeline, tasks, risks, success metrics |
|
||||
| [07-observability-backends.md](./07-observability-backends.md) | Backend selection and architecture |
|
||||
| [08-appendix.md](./08-appendix.md) | Glossary, references, version history |
|
||||
|
||||
---
|
||||
|
||||
*Previous: [Observability Backends](./07-observability-backends.md)* | *Back to: [Overview](./OpenTelemetryPlan.md)*
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
# OpenTelemetry Distributed Tracing Implementation Plan for rippled (xrpld)
|
||||
|
||||
## Executive Summary
|
||||
|
||||
This document provides a comprehensive implementation plan for integrating OpenTelemetry distributed tracing into the rippled XRP Ledger node software. The plan addresses the unique challenges of a decentralized peer-to-peer system where trace context must propagate across network boundaries between independent nodes.
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- **End-to-end transaction visibility**: Track transactions from submission through consensus to ledger inclusion
|
||||
- **Consensus round analysis**: Understand timing and behavior of consensus phases across validators
|
||||
- **RPC performance insights**: Identify slow handlers and optimize response times
|
||||
- **Network topology understanding**: Visualize message propagation patterns between peers
|
||||
- **Incident debugging**: Correlate events across distributed nodes during issues
|
||||
|
||||
### Estimated Performance Overhead
|
||||
|
||||
| Metric | Overhead | Notes |
|
||||
| ------------- | ---------- | ----------------------------------- |
|
||||
| CPU | 1-3% | Span creation and attribute setting |
|
||||
| Memory | 2-5 MB | Batch buffer for pending spans |
|
||||
| Network | 10-50 KB/s | Compressed OTLP export to collector |
|
||||
| Latency (p99) | <2% | With proper sampling configuration |
|
||||
|
||||
---
|
||||
|
||||
## Document Structure
|
||||
|
||||
This implementation plan is organized into modular documents for easier navigation:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
overview["📋 OpenTelemetryPlan.md<br/>(This Document)"]
|
||||
|
||||
subgraph analysis["Analysis & Design"]
|
||||
arch["01-architecture-analysis.md"]
|
||||
design["02-design-decisions.md"]
|
||||
end
|
||||
|
||||
subgraph impl["Implementation"]
|
||||
strategy["03-implementation-strategy.md"]
|
||||
code["04-code-samples.md"]
|
||||
config["05-configuration-reference.md"]
|
||||
end
|
||||
|
||||
subgraph deploy["Deployment & Planning"]
|
||||
phases["06-implementation-phases.md"]
|
||||
backends["07-observability-backends.md"]
|
||||
appendix["08-appendix.md"]
|
||||
end
|
||||
|
||||
overview --> analysis
|
||||
overview --> impl
|
||||
overview --> deploy
|
||||
|
||||
arch --> design
|
||||
design --> strategy
|
||||
strategy --> code
|
||||
code --> config
|
||||
config --> phases
|
||||
phases --> backends
|
||||
backends --> appendix
|
||||
|
||||
style overview fill:#e8f5e9,stroke:#388e3c,stroke-width:2px
|
||||
style analysis fill:#e3f2fd,stroke:#1976d2
|
||||
style impl fill:#fff3e0,stroke:#ff9800
|
||||
style deploy fill:#f3e5f5,stroke:#7b1fa2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
| Section | Document | Description |
|
||||
| ------- | -------- | ----------- |
|
||||
| **1** | [Architecture Analysis](./01-architecture-analysis.md) | rippled component analysis, trace points, instrumentation priorities |
|
||||
| **2** | [Design Decisions](./02-design-decisions.md) | SDK selection, exporters, span naming, attributes, context propagation |
|
||||
| **3** | [Implementation Strategy](./03-implementation-strategy.md) | Directory structure, key principles, performance optimization |
|
||||
| **4** | [Code Samples](./04-code-samples.md) | Complete C++ implementation examples for all components |
|
||||
| **5** | [Configuration Reference](./05-configuration-reference.md) | rippled config, CMake integration, Collector configurations |
|
||||
| **6** | [Implementation Phases](./06-implementation-phases.md) | 5-phase timeline, tasks, risks, success metrics |
|
||||
| **7** | [Observability Backends](./07-observability-backends.md) | Backend selection guide and production architecture |
|
||||
| **8** | [Appendix](./08-appendix.md) | Glossary, references, version history |
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture Analysis
|
||||
|
||||
The rippled node consists of several key components that require instrumentation for comprehensive distributed tracing. The main areas include the RPC server (HTTP/WebSocket), Overlay P2P network, Consensus mechanism (RCLConsensus), JobQueue for async task execution, and existing observability infrastructure (PerfLog, Insight/StatsD, Journal logging).
|
||||
|
||||
Key trace points span across transaction submission via RPC, peer-to-peer message propagation, consensus round execution, and ledger building. The implementation prioritizes high-value, low-risk components first: RPC handlers provide immediate value with minimal risk, while consensus tracing requires careful implementation to avoid timing impacts.
|
||||
|
||||
➡️ **[Read full Architecture Analysis](./01-architecture-analysis.md)**
|
||||
|
||||
---
|
||||
|
||||
## 2. Design Decisions
|
||||
|
||||
The OpenTelemetry C++ SDK is selected for its CNCF backing, active development, and native performance characteristics. Traces are exported via OTLP/gRPC (primary) or OTLP/HTTP (fallback) to an OpenTelemetry Collector, which provides flexible routing and sampling.
|
||||
|
||||
Span naming follows a hierarchical `<component>.<operation>` convention (e.g., `rpc.submit`, `tx.relay`, `consensus.round`). Context propagation uses W3C Trace Context headers for HTTP and embedded Protocol Buffer fields for P2P messages. The implementation coexists with existing PerfLog and Insight observability systems through correlation IDs.
|
||||
|
||||
➡️ **[Read full Design Decisions](./02-design-decisions.md)**
|
||||
|
||||
---
|
||||
|
||||
## 3. Implementation Strategy
|
||||
|
||||
The telemetry code is organized under `include/xrpl/telemetry/` for headers and `src/libxrpl/telemetry/` for implementation. Key principles include RAII-based span management via `SpanGuard`, conditional compilation with `XRPL_ENABLE_TELEMETRY`, and minimal runtime overhead through batch processing and efficient sampling.
|
||||
|
||||
Performance optimization strategies include probabilistic head sampling (10% default), tail-based sampling at the collector for errors and slow traces, batch export to reduce network overhead, and conditional instrumentation that compiles to no-ops when disabled.
|
||||
|
||||
➡️ **[Read full Implementation Strategy](./03-implementation-strategy.md)**
|
||||
|
||||
---
|
||||
|
||||
## 4. Code Samples
|
||||
|
||||
Complete C++ implementation examples are provided for all telemetry components:
|
||||
- `Telemetry.h` - Core interface for tracer access and span creation
|
||||
- `SpanGuard.h` - RAII wrapper for automatic span lifecycle management
|
||||
- `TracingInstrumentation.h` - Macros for conditional instrumentation
|
||||
- Protocol Buffer extensions for trace context propagation
|
||||
- Module-specific instrumentation (RPC, Consensus, P2P, JobQueue)
|
||||
|
||||
➡️ **[View all Code Samples](./04-code-samples.md)**
|
||||
|
||||
---
|
||||
|
||||
## 5. Configuration Reference
|
||||
|
||||
Configuration is handled through the `[telemetry]` section in `xrpld.cfg` with options for enabling/disabling, exporter selection, endpoint configuration, sampling ratios, and component-level filtering. CMake integration includes a `XRPL_ENABLE_TELEMETRY` option for compile-time control.
|
||||
|
||||
OpenTelemetry Collector configurations are provided for development (with Jaeger) and production (with tail-based sampling, Tempo, and Elastic APM). Docker Compose examples enable quick local development environment setup.
|
||||
|
||||
➡️ **[View full Configuration Reference](./05-configuration-reference.md)**
|
||||
|
||||
---
|
||||
|
||||
## 6. Implementation Phases
|
||||
|
||||
The implementation spans 9 weeks across 5 phases:
|
||||
|
||||
| Phase | Duration | Focus | Key Deliverables |
|
||||
| ----- | -------- | ----- | ---------------- |
|
||||
| 1 | Weeks 1-2 | Core Infrastructure | SDK integration, Telemetry interface, Configuration |
|
||||
| 2 | Weeks 3-4 | RPC Tracing | HTTP context extraction, Handler instrumentation |
|
||||
| 3 | Weeks 5-6 | Transaction Tracing | Protocol Buffer context, Relay propagation |
|
||||
| 4 | Weeks 7-8 | Consensus Tracing | Round spans, Proposal/validation tracing |
|
||||
| 5 | Week 9 | Documentation | Runbook, Dashboards, Training |
|
||||
|
||||
**Total Effort**: 47 developer-days with 2 developers
|
||||
|
||||
➡️ **[View full Implementation Phases](./06-implementation-phases.md)**
|
||||
|
||||
---
|
||||
|
||||
## 7. Observability Backends
|
||||
|
||||
For development and testing, Jaeger provides easy setup with a good UI. For production deployments, Grafana Tempo is recommended for its cost-effectiveness and Grafana integration, while Elastic APM is ideal for organizations with existing Elastic infrastructure.
|
||||
|
||||
The recommended production architecture uses a gateway collector pattern with regional collectors performing tail-based sampling, routing traces to multiple backends (Tempo for primary storage, Elastic for log correlation, S3/GCS for long-term archive).
|
||||
|
||||
➡️ **[View Observability Backend Recommendations](./07-observability-backends.md)**
|
||||
|
||||
---
|
||||
|
||||
## 8. Appendix
|
||||
|
||||
The appendix contains a glossary of OpenTelemetry and rippled-specific terms, references to external documentation and specifications, version history for this implementation plan, and a complete document index.
|
||||
|
||||
➡️ **[View Appendix](./08-appendix.md)**
|
||||
|
||||
---
|
||||
|
||||
*This document provides a comprehensive implementation plan for integrating OpenTelemetry distributed tracing into the rippled XRP Ledger node software. For detailed information on any section, follow the links to the corresponding sub-documents.*
|
||||
|
||||
@@ -940,7 +940,23 @@
|
||||
#
|
||||
# path Location to store the database
|
||||
#
|
||||
# Optional keys for NuDB and RocksDB:
|
||||
# Optional keys
|
||||
#
|
||||
# cache_size Size of cache for database records. Default is 16384.
|
||||
# Setting this value to 0 will use the default value.
|
||||
#
|
||||
# cache_age Length of time in minutes to keep database records
|
||||
# cached. Default is 5 minutes. Setting this value to
|
||||
# 0 will use the default value.
|
||||
#
|
||||
# Note: if neither cache_size nor cache_age is
|
||||
# specified, the cache for database records will not
|
||||
# be created. If only one of cache_size or cache_age
|
||||
# is specified, the cache will be created using the
|
||||
# default value for the unspecified parameter.
|
||||
#
|
||||
# Note: the cache will not be created if online_delete
|
||||
# is specified.
|
||||
#
|
||||
# fast_load Boolean. If set, load the last persisted ledger
|
||||
# from disk upon process start before syncing to
|
||||
@@ -948,6 +964,8 @@
|
||||
# if sufficient IOPS capacity is available.
|
||||
# Default 0.
|
||||
#
|
||||
# Optional keys for NuDB or RocksDB:
|
||||
#
|
||||
# earliest_seq The default is 32570 to match the XRP ledger
|
||||
# network's earliest allowed sequence. Alternate
|
||||
# networks may set this value. Minimum value of 1.
|
||||
|
||||
@@ -466,6 +466,11 @@ function (add_code_coverage_to_target name scope)
|
||||
target_compile_options(${name} ${scope} $<$<COMPILE_LANGUAGE:CXX>:${COVERAGE_CXX_COMPILER_FLAGS}>
|
||||
$<$<COMPILE_LANGUAGE:C>:${COVERAGE_C_COMPILER_FLAGS}>)
|
||||
|
||||
target_link_libraries(${name} ${scope} $<$<LINK_LANGUAGE:CXX>:${COVERAGE_CXX_LINKER_FLAGS}>
|
||||
$<$<LINK_LANGUAGE:C>:${COVERAGE_C_LINKER_FLAGS}>)
|
||||
target_link_libraries(
|
||||
${name}
|
||||
${scope}
|
||||
$<$<LINK_LANGUAGE:CXX>:${COVERAGE_CXX_LINKER_FLAGS}
|
||||
gcov>
|
||||
$<$<LINK_LANGUAGE:C>:${COVERAGE_C_LINKER_FLAGS}
|
||||
gcov>)
|
||||
endfunction () # add_code_coverage_to_target
|
||||
|
||||
@@ -9,5 +9,8 @@ function (xrpl_add_test name)
|
||||
|
||||
isolate_headers(${target} "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/tests/${name}" PRIVATE)
|
||||
|
||||
# Make sure the test isn't optimized away in unity builds
|
||||
set_target_properties(${target} PROPERTIES UNITY_BUILD_MODE GROUP UNITY_BUILD_BATCH_SIZE 0) # Adjust as needed
|
||||
|
||||
add_test(NAME ${target} COMMAND ${target})
|
||||
endfunction ()
|
||||
|
||||
@@ -84,6 +84,9 @@ add_module(xrpl net)
|
||||
target_link_libraries(xrpl.libxrpl.net PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol
|
||||
xrpl.libxrpl.resource)
|
||||
|
||||
add_module(xrpl server)
|
||||
target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol)
|
||||
|
||||
add_module(xrpl nodestore)
|
||||
target_link_libraries(xrpl.libxrpl.nodestore PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol)
|
||||
|
||||
@@ -91,15 +94,8 @@ add_module(xrpl shamap)
|
||||
target_link_libraries(xrpl.libxrpl.shamap PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.crypto xrpl.libxrpl.protocol
|
||||
xrpl.libxrpl.nodestore)
|
||||
|
||||
add_module(xrpl rdb)
|
||||
target_link_libraries(xrpl.libxrpl.rdb PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.core)
|
||||
|
||||
add_module(xrpl server)
|
||||
target_link_libraries(xrpl.libxrpl.server PUBLIC xrpl.libxrpl.protocol xrpl.libxrpl.core xrpl.libxrpl.rdb)
|
||||
|
||||
add_module(xrpl ledger)
|
||||
target_link_libraries(xrpl.libxrpl.ledger PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol
|
||||
xrpl.libxrpl.rdb)
|
||||
target_link_libraries(xrpl.libxrpl.ledger PUBLIC xrpl.libxrpl.basics xrpl.libxrpl.json xrpl.libxrpl.protocol)
|
||||
|
||||
add_library(xrpl.libxrpl)
|
||||
set_target_properties(xrpl.libxrpl PROPERTIES OUTPUT_NAME xrpl)
|
||||
@@ -117,14 +113,13 @@ target_link_modules(
|
||||
core
|
||||
crypto
|
||||
json
|
||||
ledger
|
||||
net
|
||||
nodestore
|
||||
protocol
|
||||
rdb
|
||||
resource
|
||||
server
|
||||
shamap)
|
||||
nodestore
|
||||
shamap
|
||||
net
|
||||
ledger)
|
||||
|
||||
# All headers in libxrpl are in modules.
|
||||
# Uncomment this stanza if you have not yet moved new headers into a module.
|
||||
@@ -165,4 +160,12 @@ if (xrpld)
|
||||
# antithesis_instrumentation.h, which is not exported as INTERFACE
|
||||
target_include_directories(xrpld PRIVATE ${CMAKE_SOURCE_DIR}/external/antithesis-sdk)
|
||||
endif ()
|
||||
|
||||
# any files that don't play well with unity should be added here
|
||||
if (tests)
|
||||
set_source_files_properties(
|
||||
# these two seem to produce conflicts in beast teardown template methods
|
||||
src/test/rpc/ValidatorRPC_test.cpp src/test/ledger/Invariants_test.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION
|
||||
TRUE)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
@@ -23,7 +23,6 @@ install(TARGETS common
|
||||
xrpl.libxrpl.core
|
||||
xrpl.libxrpl.crypto
|
||||
xrpl.libxrpl.json
|
||||
xrpl.libxrpl.rdb
|
||||
xrpl.libxrpl.ledger
|
||||
xrpl.libxrpl.net
|
||||
xrpl.libxrpl.nodestore
|
||||
|
||||
@@ -32,13 +32,13 @@ target_link_libraries(
|
||||
if (Boost_COMPILER)
|
||||
target_link_libraries(xrpl_boost INTERFACE Boost::disable_autolinking)
|
||||
endif ()
|
||||
if (SANITIZERS_ENABLED AND is_clang)
|
||||
# TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
if (NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
get_target_property(Boost_INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
||||
endif ()
|
||||
message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt "src:${Boost_INCLUDE_DIRS}/*")
|
||||
target_compile_options(opts INTERFACE # ignore boost headers for sanitizing
|
||||
-fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt)
|
||||
endif ()
|
||||
# if (SANITIZERS_ENABLED AND is_clang)
|
||||
# # TODO: gcc does not support -fsanitize-blacklist...can we do something else for gcc ?
|
||||
# if (NOT Boost_INCLUDE_DIRS AND TARGET Boost::headers)
|
||||
# get_target_property(Boost_INCLUDE_DIRS Boost::headers INTERFACE_INCLUDE_DIRECTORIES)
|
||||
# endif ()
|
||||
# message(STATUS "Adding [${Boost_INCLUDE_DIRS}] to sanitizer blacklist")
|
||||
# file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt "src:${Boost_INCLUDE_DIRS}/*")
|
||||
# target_compile_options(opts INTERFACE # ignore boost headers for sanitizing
|
||||
# -fsanitize-blacklist=${CMAKE_CURRENT_BINARY_DIR}/san_bl.txt)
|
||||
# endif ()
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"sqlite3/3.49.1#8631739a4c9b93bd3d6b753bac548a63%1765850149.926",
|
||||
"soci/4.0.3#a9f8d773cd33e356b5879a4b0564f287%1765850149.46",
|
||||
"snappy/1.1.10#968fef506ff261592ec30c574d4a7809%1765850147.878",
|
||||
"secp256k1/0.7.1#3a61e95e220062ef32c48d019e9c81f7%1770306721.686",
|
||||
"secp256k1/0.7.0#9c4ab67bdc3860c16ea5b36aed8f74ea%1765850147.928",
|
||||
"rocksdb/10.5.1#4a197eca381a3e5ae8adf8cffa5aacd0%1765850186.86",
|
||||
"re2/20230301#ca3b241baec15bd31ea9187150e0b333%1765850148.103",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
"openssl/3.5.5#05a4ac5b7323f7a329b2db1391d9941f%1769599205.414",
|
||||
"openssl/3.5.4#1b986e61b38fdfda3b40bebc1b234393%1768312656.257",
|
||||
"nudb/2.0.9#0432758a24204da08fee953ec9ea03cb%1769436073.32",
|
||||
"lz4/1.10.0#59fc63cac7f10fbe8e05c7e62c2f3504%1765850143.914",
|
||||
"libiconv/1.17#1e65319e945f2d31941a9d28cc13c058%1765842973.492",
|
||||
@@ -23,7 +23,7 @@
|
||||
"date/3.0.4#862e11e80030356b53c2c38599ceb32b%1765850143.772",
|
||||
"c-ares/1.34.5#5581c2b62a608b40bb85d965ab3ec7c8%1765850144.336",
|
||||
"bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837",
|
||||
"boost/1.90.0#d5e8defe7355494953be18524a7f135b%1769454080.269",
|
||||
"boost/1.90.0#d5e8defe7355494953be18524a7f135b%1765955095.179",
|
||||
"abseil/20250127.0#99262a368bd01c0ccca8790dfced9719%1766517936.993"
|
||||
],
|
||||
"build_requires": [
|
||||
@@ -31,7 +31,7 @@
|
||||
"strawberryperl/5.32.1.1#707032463aa0620fa17ec0d887f5fe41%1765850165.196",
|
||||
"protobuf/6.32.1#f481fd276fc23a33b85a3ed1e898b693%1765850161.038",
|
||||
"nasm/2.16.01#31e26f2ee3c4346ecd347911bd126904%1765850144.707",
|
||||
"msys2/cci.latest#eea83308ad7e9023f7318c60d5a9e6cb%1770199879.083",
|
||||
"msys2/cci.latest#1996656c3c98e5765b25b60ff5cf77b4%1764840888.758",
|
||||
"m4/1.4.19#70dc8bbb33e981d119d2acc0175cf381%1763158052.846",
|
||||
"cmake/4.2.0#ae0a44f44a1ef9ab68fd4b3e9a1f8671%1765850153.937",
|
||||
"cmake/3.31.10#313d16a1aa16bbdb2ca0792467214b76%1765850153.479",
|
||||
|
||||
11
conanfile.py
11
conanfile.py
@@ -1,4 +1,5 @@
|
||||
import re
|
||||
import os
|
||||
|
||||
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
|
||||
|
||||
@@ -32,8 +33,8 @@ class Xrpl(ConanFile):
|
||||
"grpc/1.72.0",
|
||||
"libarchive/3.8.1",
|
||||
"nudb/2.0.9",
|
||||
"openssl/3.5.5",
|
||||
"secp256k1/0.7.1",
|
||||
"openssl/3.5.4",
|
||||
"secp256k1/0.7.0",
|
||||
"soci/4.0.3",
|
||||
"zlib/1.3.1",
|
||||
]
|
||||
@@ -126,6 +127,12 @@ class Xrpl(ConanFile):
|
||||
if self.settings.compiler in ["clang", "gcc"]:
|
||||
self.options["boost"].without_cobalt = True
|
||||
|
||||
# Check if environment variable exists
|
||||
if "SANITIZERS" in os.environ:
|
||||
sanitizers = os.environ["SANITIZERS"]
|
||||
if "Address" in sanitizers:
|
||||
self.default_options["fPIC"] = False
|
||||
|
||||
def requirements(self):
|
||||
# Conan 2 requires transitive headers to be specified
|
||||
transitive_headers_opt = (
|
||||
|
||||
8
docs/build/sanitizers.md
vendored
8
docs/build/sanitizers.md
vendored
@@ -89,8 +89,8 @@ cmake --build . --parallel 4
|
||||
**IMPORTANT**: ASAN with Boost produces many false positives. Use these options:
|
||||
|
||||
```bash
|
||||
export ASAN_OPTIONS="print_stacktrace=1:detect_container_overflow=0:suppressions=path/to/asan.supp:halt_on_error=0:log_path=asan.log"
|
||||
export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsan.log"
|
||||
export ASAN_OPTIONS="include=sanitizers/suppressions/runtime-asan-options.txt:suppressions=sanitizers/suppressions/asan.supp"
|
||||
export LSAN_OPTIONS="include=sanitizers/suppressions/runtime-lsan-options.txt:suppressions=sanitizers/suppressions/lsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -108,7 +108,7 @@ export LSAN_OPTIONS="suppressions=path/to/lsan.supp:halt_on_error=0:log_path=lsa
|
||||
### ThreadSanitizer (TSan)
|
||||
|
||||
```bash
|
||||
export TSAN_OPTIONS="suppressions=path/to/tsan.supp halt_on_error=0 log_path=tsan.log"
|
||||
export TSAN_OPTIONS="include=sanitizers/suppressions/runtime-tsan-options.txt:suppressions=sanitizers/suppressions/tsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
@@ -129,7 +129,7 @@ More details [here](https://github.com/google/sanitizers/wiki/AddressSanitizerLe
|
||||
### UndefinedBehaviorSanitizer (UBSan)
|
||||
|
||||
```bash
|
||||
export UBSAN_OPTIONS="suppressions=path/to/ubsan.supp:print_stacktrace=1:halt_on_error=0:log_path=ubsan.log"
|
||||
export UBSAN_OPTIONS="include=sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=sanitizers/suppressions/ubsan.supp"
|
||||
|
||||
# Run tests
|
||||
./xrpld --unittest --unittest-jobs=5
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_ARCHIVE_H_INCLUDED
|
||||
#define XRPL_BASICS_ARCHIVE_H_INCLUDED
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
@@ -15,3 +16,5 @@ void
|
||||
extractTarLz4(boost::filesystem::path const& src, boost::filesystem::path const& dst);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_BASICCONFIG_H_INCLUDED
|
||||
#define XRPL_BASICS_BASICCONFIG_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
@@ -368,3 +369,5 @@ get_if_exists<bool>(Section const& section, std::string const& name, bool& v)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_BLOB_H_INCLUDED
|
||||
#define XRPL_BASICS_BLOB_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -10,3 +11,5 @@ namespace xrpl {
|
||||
using Blob = std::vector<unsigned char>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_BUFFER_H_INCLUDED
|
||||
#define XRPL_BASICS_BUFFER_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/Slice.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
@@ -212,3 +213,5 @@ operator!=(Buffer const& lhs, Buffer const& rhs) noexcept
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_BYTEUTILITIES_H_INCLUDED
|
||||
#define XRPL_BASICS_BYTEUTILITIES_H_INCLUDED
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
@@ -19,3 +20,5 @@ megabytes(T value) noexcept
|
||||
static_assert(kilobytes(2) == 2048, "kilobytes(2) == 2048");
|
||||
static_assert(megabytes(3) == 3145728, "megabytes(3) == 3145728");
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_COMPRESSIONALGORITHMS_H_INCLUDED
|
||||
#define XRPL_COMPRESSIONALGORITHMS_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
@@ -132,3 +133,5 @@ lz4Decompress(InputStream& in, std::size_t inSize, std::uint8_t* decompressed, s
|
||||
} // namespace compression_algorithms
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_COMPRESSIONALGORITHMS_H_INCLUDED
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_COUNTEDOBJECT_H_INCLUDED
|
||||
#define XRPL_BASICS_COUNTEDOBJECT_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
@@ -133,3 +134,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_DECAYINGSAMPLE_H_INCLUDED
|
||||
#define XRPL_BASICS_DECAYINGSAMPLE_H_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
@@ -129,3 +130,5 @@ private:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_EXPECTED_H_INCLUDED
|
||||
#define XRPL_BASICS_EXPECTED_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
|
||||
@@ -228,3 +229,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_EXPECTED_H_INCLUDED
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_FILEUTILITIES_H_INCLUDED
|
||||
#define XRPL_BASICS_FILEUTILITIES_H_INCLUDED
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
@@ -17,3 +18,5 @@ void
|
||||
writeFileContents(boost::system::error_code& ec, boost::filesystem::path const& destPath, std::string const& contents);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_INTRUSIVEPOINTER_H_INCLUDED
|
||||
#define XRPL_BASICS_INTRUSIVEPOINTER_H_INCLUDED
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
@@ -484,3 +485,4 @@ dynamic_pointer_cast(TT const& v)
|
||||
}
|
||||
} // namespace intr_ptr
|
||||
} // namespace xrpl
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED
|
||||
#define XRPL_BASICS_INTRUSIVEPOINTER_IPP_INCLUDED
|
||||
|
||||
#include <xrpl/basics/IntrusivePointer.h>
|
||||
#include <xrpl/basics/IntrusiveRefCounts.h>
|
||||
@@ -702,3 +703,4 @@ SharedWeakUnion<T>::unsafeReleaseNoStore()
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_INTRUSIVEREFCOUNTS_H_INCLUDED
|
||||
#define XRPL_BASICS_INTRUSIVEREFCOUNTS_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
@@ -460,3 +461,4 @@ partialDestructorFinished(T** o)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
} // namespace xrpl
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_KEYCACHE_H
|
||||
#define XRPL_BASICS_KEYCACHE_H
|
||||
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
@@ -8,3 +9,5 @@ namespace xrpl {
|
||||
using KeyCache = TaggedCache<uint256, int, true>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_KEYCACHE_H
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/thread/tss.hpp>
|
||||
#ifndef XRPL_BASICS_LOCALVALUE_H_INCLUDED
|
||||
#define XRPL_BASICS_LOCALVALUE_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
@@ -41,21 +40,63 @@ struct LocalValues
|
||||
|
||||
// Keys are the address of a LocalValue.
|
||||
std::unordered_map<void const*, std::unique_ptr<BasicValue>> values;
|
||||
};
|
||||
|
||||
static inline void
|
||||
cleanup(LocalValues* lvs)
|
||||
// Wrapper to ensure proper cleanup when thread exits
|
||||
struct LocalValuesHolder
|
||||
{
|
||||
LocalValues* ptr = nullptr;
|
||||
|
||||
~LocalValuesHolder()
|
||||
{
|
||||
if (lvs && !lvs->onCoro)
|
||||
delete lvs;
|
||||
if (ptr && !ptr->onCoro)
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <class = void>
|
||||
boost::thread_specific_ptr<detail::LocalValues>&
|
||||
getLocalValues()
|
||||
inline LocalValuesHolder&
|
||||
getLocalValuesHolder()
|
||||
{
|
||||
static boost::thread_specific_ptr<detail::LocalValues> tsp(&detail::LocalValues::cleanup);
|
||||
return tsp;
|
||||
thread_local LocalValuesHolder holder;
|
||||
return holder;
|
||||
}
|
||||
|
||||
inline LocalValues*&
|
||||
getLocalValuesPtr()
|
||||
{
|
||||
return getLocalValuesHolder().ptr;
|
||||
}
|
||||
|
||||
inline LocalValues*
|
||||
getOrCreateLocalValues()
|
||||
{
|
||||
auto& ptr = getLocalValuesPtr();
|
||||
if (!ptr)
|
||||
{
|
||||
ptr = new LocalValues();
|
||||
ptr->onCoro = false;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// For coroutine support, we need explicit swap functions
|
||||
inline LocalValues*
|
||||
releaseLocalValues()
|
||||
{
|
||||
auto& ptr = getLocalValuesPtr();
|
||||
auto* result = ptr;
|
||||
ptr = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void
|
||||
resetLocalValues(LocalValues* lvs)
|
||||
{
|
||||
auto& ptr = getLocalValuesPtr();
|
||||
// Clean up old value if it's not a coroutine's LocalValues
|
||||
if (ptr && !ptr->onCoro)
|
||||
delete ptr;
|
||||
ptr = lvs;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
@@ -88,21 +129,14 @@ template <class T>
|
||||
T&
|
||||
LocalValue<T>::operator*()
|
||||
{
|
||||
auto lvs = detail::getLocalValues().get();
|
||||
if (!lvs)
|
||||
{
|
||||
lvs = new detail::LocalValues();
|
||||
lvs->onCoro = false;
|
||||
detail::getLocalValues().reset(lvs);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const iter = lvs->values.find(this);
|
||||
if (iter != lvs->values.end())
|
||||
return *reinterpret_cast<T*>(iter->second->get());
|
||||
}
|
||||
auto lvs = detail::getOrCreateLocalValues();
|
||||
auto const iter = lvs->values.find(this);
|
||||
if (iter != lvs->values.end())
|
||||
return *reinterpret_cast<T*>(iter->second->get());
|
||||
|
||||
return *reinterpret_cast<T*>(
|
||||
lvs->values.emplace(this, std::make_unique<detail::LocalValues::Value<T>>(t_)).first->second->get());
|
||||
}
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_LOG_H_INCLUDED
|
||||
#define XRPL_BASICS_LOG_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/UnorderedContainers.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
@@ -257,3 +258,5 @@ beast::Journal
|
||||
debugLog();
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_MATHUTILITIES_H_INCLUDED
|
||||
#define XRPL_BASICS_MATHUTILITIES_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
@@ -44,3 +45,5 @@ static_assert(calculatePercent(50'000'001, 100'000'000) == 51);
|
||||
static_assert(calculatePercent(99'999'999, 100'000'000) == 100);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
#define XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
@@ -9,6 +10,10 @@
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <boost/multiprecision/cpp_int.hpp>
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
namespace xrpl {
|
||||
|
||||
class Number;
|
||||
@@ -16,18 +21,37 @@ class Number;
|
||||
std::string
|
||||
to_string(Number const& amount);
|
||||
|
||||
/** Returns a rough estimate of log10(value).
|
||||
*
|
||||
* The return value is a pair (log, rem), where log is the estimated log10,
|
||||
* and rem is value divided by 10^log. If rem is 1, then value is an exact
|
||||
* power of ten, and log is the exact log10(value).
|
||||
*
|
||||
* This function only works for positive values.
|
||||
*/
|
||||
template <typename T>
|
||||
constexpr std::pair<int, T>
|
||||
logTenEstimate(T value)
|
||||
{
|
||||
int log = 0;
|
||||
T remainder = value;
|
||||
while (value >= 10)
|
||||
{
|
||||
if (value % 10 == 0)
|
||||
remainder = remainder / 10;
|
||||
value /= 10;
|
||||
++log;
|
||||
}
|
||||
return {log, remainder};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr std::optional<int>
|
||||
logTen(T value)
|
||||
{
|
||||
int log = 0;
|
||||
while (value >= 10 && value % 10 == 0)
|
||||
{
|
||||
value /= 10;
|
||||
++log;
|
||||
}
|
||||
if (value == 1)
|
||||
return log;
|
||||
auto const est = logTenEstimate(value);
|
||||
if (est.second == 1)
|
||||
return est.first;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -41,12 +65,10 @@ isPowerOfTen(T value)
|
||||
/** MantissaRange defines a range for the mantissa of a normalized Number.
|
||||
*
|
||||
* The mantissa is in the range [min, max], where
|
||||
* * min is a power of 10, and
|
||||
* * max = min * 10 - 1.
|
||||
*
|
||||
* The mantissa_scale enum indicates whether the range is "small" or "large".
|
||||
* This intentionally restricts the number of MantissaRanges that can be
|
||||
* instantiated to two: one for each scale.
|
||||
* used to two: one for each scale.
|
||||
*
|
||||
* The "small" scale is based on the behavior of STAmount for IOUs. It has a min
|
||||
* value of 10^15, and a max value of 10^16-1. This was sufficient for
|
||||
@@ -60,8 +82,8 @@ isPowerOfTen(T value)
|
||||
* "large" scale.
|
||||
*
|
||||
* The "large" scale is intended to represent all values that can be represented
|
||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 10^18, and a max
|
||||
* value of 10^19-1.
|
||||
* by an STAmount - IOUs, XRP, and MPTs. It has a min value of 2^63/10+1
|
||||
* (truncated), and a max value of 2^63-1.
|
||||
*
|
||||
* Note that if the mentioned amendments are eventually retired, this class
|
||||
* should be left in place, but the "small" scale option should be removed. This
|
||||
@@ -73,25 +95,50 @@ struct MantissaRange
|
||||
enum mantissa_scale { small, large };
|
||||
|
||||
explicit constexpr MantissaRange(mantissa_scale scale_)
|
||||
: min(getMin(scale_)), max(min * 10 - 1), log(logTen(min).value_or(-1)), scale(scale_)
|
||||
: max(getMax(scale_))
|
||||
, min(computeMin(max))
|
||||
, referenceMin(getReferenceMin(scale_, min))
|
||||
, log(computeLog(min))
|
||||
, scale(scale_)
|
||||
{
|
||||
// Since this is constexpr, if any of these throw, it won't compile
|
||||
if (min * 10 <= max)
|
||||
throw std::out_of_range("min * 10 <= max");
|
||||
if (max / 10 >= min)
|
||||
throw std::out_of_range("max / 10 >= min");
|
||||
if ((min - 1) * 10 > max)
|
||||
throw std::out_of_range("(min - 1) * 10 > max");
|
||||
// This is a little hacky
|
||||
if ((max + 10) / 10 < min)
|
||||
throw std::out_of_range("(max + 10) / 10 < min");
|
||||
}
|
||||
|
||||
rep min;
|
||||
// Explicitly delete copy and move operations
|
||||
MantissaRange(MantissaRange const&) = delete;
|
||||
MantissaRange(MantissaRange&&) = delete;
|
||||
MantissaRange&
|
||||
operator=(MantissaRange const&) = delete;
|
||||
MantissaRange&
|
||||
operator=(MantissaRange&&) = delete;
|
||||
|
||||
rep max;
|
||||
rep min;
|
||||
// This is not a great name. Used to determine if mantissas are in range,
|
||||
// but have fewer digits than max
|
||||
rep referenceMin;
|
||||
int log;
|
||||
mantissa_scale scale;
|
||||
|
||||
private:
|
||||
static constexpr rep
|
||||
getMin(mantissa_scale scale_)
|
||||
getMax(mantissa_scale scale)
|
||||
{
|
||||
switch (scale_)
|
||||
switch (scale)
|
||||
{
|
||||
case small:
|
||||
return 1'000'000'000'000'000ULL;
|
||||
return 9'999'999'999'999'999ULL;
|
||||
case large:
|
||||
return 1'000'000'000'000'000'000ULL;
|
||||
return std::numeric_limits<std::int64_t>::max();
|
||||
default:
|
||||
// Since this can never be called outside a non-constexpr
|
||||
// context, this throw assures that the build fails if an
|
||||
@@ -99,12 +146,53 @@ private:
|
||||
throw std::runtime_error("Unknown mantissa scale");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr rep
|
||||
computeMin(rep max)
|
||||
{
|
||||
return max / 10 + 1;
|
||||
}
|
||||
|
||||
static constexpr rep
|
||||
getReferenceMin(mantissa_scale scale, rep min)
|
||||
{
|
||||
switch (scale)
|
||||
{
|
||||
case large:
|
||||
return 1'000'000'000'000'000'000ULL;
|
||||
default:
|
||||
if (isPowerOfTen(min))
|
||||
return min;
|
||||
throw std::runtime_error("Unknown/bad mantissa scale");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr rep
|
||||
computeLog(rep min)
|
||||
{
|
||||
auto const estimate = logTenEstimate(min);
|
||||
return estimate.first + (estimate.second == 1 ? 0 : 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Like std::integral, but only 64-bit integral types.
|
||||
template <class T>
|
||||
concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::uint64_t>;
|
||||
|
||||
namespace detail {
|
||||
#ifdef _MSC_VER
|
||||
using uint128_t = boost::multiprecision::uint128_t;
|
||||
using int128_t = boost::multiprecision::int128_t;
|
||||
#else // !defined(_MSC_VER)
|
||||
using uint128_t = __uint128_t;
|
||||
using int128_t = __int128_t;
|
||||
#endif // !defined(_MSC_VER)
|
||||
|
||||
template <class T>
|
||||
concept UnsignedMantissa =
|
||||
std::is_unsigned_v<T> || std::is_same_v<T, uint128_t>;
|
||||
} // namespace detail
|
||||
|
||||
/** Number is a floating point type that can represent a wide range of values.
|
||||
*
|
||||
* It can represent all values that can be represented by an STAmount -
|
||||
@@ -132,9 +220,7 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
* 1. Normalization can be disabled by using the "unchecked" ctor tag. This
|
||||
* should only be used at specific conversion points, some constexpr
|
||||
* values, and in unit tests.
|
||||
* 2. The max of the "large" range, 10^19-1, is the largest 10^X-1 value that
|
||||
* fits in an unsigned 64-bit number. (10^19-1 < 2^64-1 and
|
||||
* 10^20-1 > 2^64-1). This avoids under- and overflows.
|
||||
* 2. The max of the "large" range, 2^63-1, TODO: explain the large range.
|
||||
*
|
||||
* ---- External Interface ----
|
||||
*
|
||||
@@ -148,7 +234,7 @@ concept Integral64 = std::is_same_v<T, std::int64_t> || std::is_same_v<T, std::u
|
||||
*
|
||||
* Note:
|
||||
* 1. 2^63-1 is between 10^18 and 10^19-1, which are the limits of the "large"
|
||||
* mantissa range.
|
||||
* mantissa range. TODO: update this explanation.
|
||||
* 2. The functions mantissa() and exponent() return the external view of the
|
||||
* Number value, specifically using a signed 63-bit mantissa. This may
|
||||
* require altering the internal representation to fit into that range
|
||||
@@ -208,8 +294,7 @@ class Number
|
||||
using rep = std::int64_t;
|
||||
using internalrep = MantissaRange::rep;
|
||||
|
||||
bool negative_{false};
|
||||
internalrep mantissa_{0};
|
||||
rep mantissa_{0};
|
||||
int exponent_{std::numeric_limits<int>::lowest()};
|
||||
|
||||
public:
|
||||
@@ -217,9 +302,11 @@ public:
|
||||
constexpr static int minExponent = -32768;
|
||||
constexpr static int maxExponent = 32768;
|
||||
|
||||
#if MAXREP
|
||||
constexpr static internalrep maxRep = std::numeric_limits<rep>::max();
|
||||
static_assert(maxRep == 9'223'372'036'854'775'807);
|
||||
static_assert(-maxRep == std::numeric_limits<rep>::min() + 1);
|
||||
#endif
|
||||
|
||||
// May need to make unchecked private
|
||||
struct unchecked
|
||||
@@ -294,7 +381,7 @@ public:
|
||||
friend constexpr bool
|
||||
operator==(Number const& x, Number const& y) noexcept
|
||||
{
|
||||
return x.negative_ == y.negative_ && x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
||||
return x.mantissa_ == y.mantissa_ && x.exponent_ == y.exponent_;
|
||||
}
|
||||
|
||||
friend constexpr bool
|
||||
@@ -308,8 +395,8 @@ public:
|
||||
{
|
||||
// If the two amounts have different signs (zero is treated as positive)
|
||||
// then the comparison is true iff the left is negative.
|
||||
bool const lneg = x.negative_;
|
||||
bool const rneg = y.negative_;
|
||||
bool const lneg = x.mantissa_ < 0;
|
||||
bool const rneg = y.mantissa_ < 0;
|
||||
|
||||
if (lneg != rneg)
|
||||
return lneg;
|
||||
@@ -337,7 +424,7 @@ public:
|
||||
constexpr int
|
||||
signum() const noexcept
|
||||
{
|
||||
return negative_ ? -1 : (mantissa_ ? 1 : 0);
|
||||
return mantissa_ < 0 ? -1 : (mantissa_ ? 1 : 0);
|
||||
}
|
||||
|
||||
Number
|
||||
@@ -376,6 +463,9 @@ public:
|
||||
friend Number
|
||||
root2(Number f);
|
||||
|
||||
friend Number
|
||||
power(Number const& f, unsigned n, unsigned d);
|
||||
|
||||
// Thread local rounding control. Default is to_nearest
|
||||
enum rounding_mode { to_nearest, towards_zero, downward, upward };
|
||||
static rounding_mode
|
||||
@@ -440,22 +530,48 @@ private:
|
||||
static_assert(isPowerOfTen(smallRange.min));
|
||||
static_assert(smallRange.min == 1'000'000'000'000'000LL);
|
||||
static_assert(smallRange.max == 9'999'999'999'999'999LL);
|
||||
static_assert(smallRange.referenceMin == smallRange.min);
|
||||
static_assert(smallRange.log == 15);
|
||||
#if MAXREP
|
||||
static_assert(smallRange.min < maxRep);
|
||||
static_assert(smallRange.max < maxRep);
|
||||
#endif
|
||||
constexpr static MantissaRange largeRange{MantissaRange::large};
|
||||
static_assert(isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.min == 1'000'000'000'000'000'000ULL);
|
||||
static_assert(largeRange.max == internalrep(9'999'999'999'999'999'999ULL));
|
||||
static_assert(!isPowerOfTen(largeRange.min));
|
||||
static_assert(largeRange.min == 922'337'203'685'477'581ULL);
|
||||
static_assert(largeRange.max == internalrep(9'223'372'036'854'775'807ULL));
|
||||
static_assert(largeRange.max == std::numeric_limits<rep>::max());
|
||||
static_assert(largeRange.referenceMin == 1'000'000'000'000'000'000ULL);
|
||||
static_assert(largeRange.log == 18);
|
||||
// There are 2 values that will not fit in largeRange without some extra
|
||||
// work
|
||||
// * 9223372036854775808
|
||||
// * 9223372036854775809
|
||||
// They both end up < min, but with a leftover. If they round up, everything
|
||||
// will be fine. If they don't, well need to bring them up into range.
|
||||
// Guard::bringIntoRange handles this situation.
|
||||
|
||||
#if MAXREP
|
||||
static_assert(largeRange.min < maxRep);
|
||||
static_assert(largeRange.max > maxRep);
|
||||
#endif
|
||||
|
||||
// The range for the mantissa when normalized.
|
||||
// Use reference_wrapper to avoid making copies, and prevent accidentally
|
||||
// changing the values inside the range.
|
||||
static thread_local std::reference_wrapper<MantissaRange const> range_;
|
||||
|
||||
// And one is needed because it needs to choose between oneSmall and
|
||||
// oneLarge based on the current range
|
||||
static Number
|
||||
one(MantissaRange const& range);
|
||||
|
||||
static Number
|
||||
root(MantissaRange const& range, Number f, unsigned d);
|
||||
|
||||
void
|
||||
normalize(MantissaRange const& range);
|
||||
|
||||
void
|
||||
normalize();
|
||||
|
||||
@@ -478,11 +594,14 @@ private:
|
||||
friend void
|
||||
doNormalize(
|
||||
bool& negative,
|
||||
T& mantissa_,
|
||||
int& exponent_,
|
||||
T& mantissa,
|
||||
int& exponent,
|
||||
MantissaRange::rep const& minMantissa,
|
||||
MantissaRange::rep const& maxMantissa);
|
||||
|
||||
bool
|
||||
isnormal(MantissaRange const& range) const noexcept;
|
||||
|
||||
bool
|
||||
isnormal() const noexcept;
|
||||
|
||||
@@ -499,11 +618,73 @@ private:
|
||||
static internalrep
|
||||
externalToInternal(rep mantissa);
|
||||
|
||||
/** Breaks down the number into components, potentially de-normalizing it.
|
||||
*
|
||||
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||
*
|
||||
*/
|
||||
template <detail::UnsignedMantissa Rep = internalrep>
|
||||
std::tuple<bool, Rep, int>
|
||||
toInternal(MantissaRange const& range) const;
|
||||
|
||||
/** Breaks down the number into components, potentially de-normalizing it.
|
||||
*
|
||||
* Ensures that the mantissa always has range_.log + 1 digits.
|
||||
*
|
||||
*/
|
||||
template <detail::UnsignedMantissa Rep = internalrep>
|
||||
std::tuple<bool, Rep, int>
|
||||
toInternal() const;
|
||||
|
||||
/** Rebuilds the number from components.
|
||||
*
|
||||
* If "normalized" is true, the values are expected to be normalized - all
|
||||
* in their valid ranges.
|
||||
*
|
||||
* If "normalized" is false, the values are expected to be "near
|
||||
* normalized", meaning that the mantissa has to be modified at most once to
|
||||
* bring it back into range.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
bool expectNormal = true,
|
||||
detail::UnsignedMantissa Rep = internalrep>
|
||||
void
|
||||
fromInternal(
|
||||
bool negative,
|
||||
Rep mantissa,
|
||||
int exponent,
|
||||
MantissaRange const* pRange);
|
||||
|
||||
/** Rebuilds the number from components.
|
||||
*
|
||||
* If "normalized" is true, the values are expected to be normalized - all
|
||||
* in their valid ranges.
|
||||
*
|
||||
* If "normalized" is false, the values are expected to be "near
|
||||
* normalized", meaning that the mantissa has to be modified at most once to
|
||||
* bring it back into range.
|
||||
*
|
||||
*/
|
||||
template <
|
||||
bool expectNormal = true,
|
||||
detail::UnsignedMantissa Rep = internalrep>
|
||||
void
|
||||
fromInternal(bool negative, Rep mantissa, int exponent);
|
||||
|
||||
class Guard;
|
||||
|
||||
public:
|
||||
constexpr static internalrep largestMantissa = largeRange.max;
|
||||
};
|
||||
|
||||
inline constexpr Number::Number(bool negative, internalrep mantissa, int exponent, unchecked) noexcept
|
||||
: negative_(negative), mantissa_{mantissa}, exponent_{exponent}
|
||||
inline constexpr Number::Number(
|
||||
bool negative,
|
||||
internalrep mantissa,
|
||||
int exponent,
|
||||
unchecked) noexcept
|
||||
: mantissa_{(negative ? -1 : 1) * static_cast<rep>(mantissa)}
|
||||
, exponent_{exponent}
|
||||
{
|
||||
}
|
||||
|
||||
@@ -514,13 +695,8 @@ inline constexpr Number::Number(internalrep mantissa, int exponent, unchecked) n
|
||||
|
||||
constexpr static Number numZero{};
|
||||
|
||||
inline Number::Number(bool negative, internalrep mantissa, int exponent, normalized)
|
||||
: Number(negative, mantissa, exponent, unchecked{})
|
||||
{
|
||||
normalize();
|
||||
}
|
||||
|
||||
inline Number::Number(internalrep mantissa, int exponent, normalized) : Number(false, mantissa, exponent, normalized{})
|
||||
inline Number::Number(internalrep mantissa, int exponent, normalized)
|
||||
: Number(false, mantissa, exponent, normalized{})
|
||||
{
|
||||
}
|
||||
|
||||
@@ -541,17 +717,7 @@ inline Number::Number(rep mantissa) : Number{mantissa, 0}
|
||||
inline constexpr Number::rep
|
||||
Number::mantissa() const noexcept
|
||||
{
|
||||
auto m = mantissa_;
|
||||
if (m > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || (m % 10 == 0 && m / 10 <= maxRep),
|
||||
"xrpl::Number::mantissa",
|
||||
"large normalized mantissa has no remainder");
|
||||
m /= 10;
|
||||
}
|
||||
auto const sign = negative_ ? -1 : 1;
|
||||
return sign * static_cast<Number::rep>(m);
|
||||
return mantissa_;
|
||||
}
|
||||
|
||||
/** Returns the exponent of the external view of the Number.
|
||||
@@ -562,16 +728,7 @@ Number::mantissa() const noexcept
|
||||
inline constexpr int
|
||||
Number::exponent() const noexcept
|
||||
{
|
||||
auto e = exponent_;
|
||||
if (mantissa_ > maxRep)
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!isnormal() || (mantissa_ % 10 == 0 && mantissa_ / 10 <= maxRep),
|
||||
"xrpl::Number::exponent",
|
||||
"large normalized mantissa has no remainder");
|
||||
++e;
|
||||
}
|
||||
return e;
|
||||
return exponent_;
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
@@ -586,7 +743,7 @@ Number::operator-() const noexcept
|
||||
if (mantissa_ == 0)
|
||||
return Number{};
|
||||
auto x = *this;
|
||||
x.negative_ = !x.negative_;
|
||||
x.mantissa_ = -1 * x.mantissa_;
|
||||
return x;
|
||||
}
|
||||
|
||||
@@ -667,39 +824,61 @@ Number::min() noexcept
|
||||
inline Number
|
||||
Number::max() noexcept
|
||||
{
|
||||
return Number{false, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
return Number{false, range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline Number
|
||||
Number::lowest() noexcept
|
||||
{
|
||||
return Number{true, std::min(range_.get().max, maxRep), maxExponent, unchecked{}};
|
||||
return Number{true, range_.get().max, maxExponent, unchecked{}};
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::isnormal(MantissaRange const& range) const noexcept
|
||||
{
|
||||
auto const abs_m = mantissa_ < 0 ? -mantissa_ : mantissa_;
|
||||
|
||||
return *this == Number{} ||
|
||||
(range.min <= abs_m && abs_m <= range.max && //
|
||||
minExponent <= exponent_ && exponent_ <= maxExponent);
|
||||
}
|
||||
|
||||
inline bool
|
||||
Number::isnormal() const noexcept
|
||||
{
|
||||
MantissaRange const& range = range_;
|
||||
auto const abs_m = mantissa_;
|
||||
return *this == Number{} ||
|
||||
(range.min <= abs_m && abs_m <= range.max && (abs_m <= maxRep || abs_m % 10 == 0) && minExponent <= exponent_ &&
|
||||
exponent_ <= maxExponent);
|
||||
return isnormal(range_);
|
||||
}
|
||||
|
||||
template <Integral64 T>
|
||||
std::pair<T, int>
|
||||
Number::normalizeToRange(T minMantissa, T maxMantissa) const
|
||||
{
|
||||
bool negative = negative_;
|
||||
internalrep mantissa = mantissa_;
|
||||
bool negative = mantissa_ < 0;
|
||||
auto const sign = negative ? -1 : 1;
|
||||
internalrep mantissa = sign * mantissa_;
|
||||
int exponent = exponent_;
|
||||
|
||||
if constexpr (std::is_unsigned_v<T>)
|
||||
XRPL_ASSERT_PARTS(!negative, "xrpl::Number::normalizeToRange", "Number is non-negative for unsigned range.");
|
||||
{
|
||||
XRPL_ASSERT_PARTS(
|
||||
!negative,
|
||||
"xrpl::Number::normalizeToRange",
|
||||
"Number is non-negative for unsigned range.");
|
||||
// To avoid logical errors in release builds, throw if the Number is
|
||||
// negative for an unsigned range.
|
||||
if (negative)
|
||||
throw std::runtime_error(
|
||||
"Number::normalizeToRange: Number is negative for "
|
||||
"unsigned range.");
|
||||
}
|
||||
Number::normalize(negative, mantissa, exponent, minMantissa, maxMantissa);
|
||||
|
||||
auto const sign = negative ? -1 : 1;
|
||||
return std::make_pair(static_cast<T>(sign * mantissa), exponent);
|
||||
// Cast mantissa to signed type first (if T is a signed type) to avoid
|
||||
// unsigned integer overflow when multiplying by negative sign
|
||||
T signedMantissa = static_cast<T>(mantissa);
|
||||
if (negative)
|
||||
signedMantissa = -signedMantissa;
|
||||
return std::make_pair(signedMantissa, exponent);
|
||||
}
|
||||
|
||||
inline constexpr Number
|
||||
@@ -818,3 +997,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_NUMBER_H_INCLUDED
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_RANGESET_H_INCLUDED
|
||||
#define XRPL_BASICS_RANGESET_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/core/LexicalCast.h>
|
||||
|
||||
@@ -172,3 +173,5 @@ prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_RESOLVER_H_INCLUDED
|
||||
#define XRPL_BASICS_RESOLVER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/net/IPEndpoint.h>
|
||||
|
||||
@@ -44,3 +45,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_RESOLVERASIO_H_INCLUDED
|
||||
#define XRPL_BASICS_RESOLVERASIO_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/Resolver.h>
|
||||
#include <xrpl/beast/utility/Journal.h>
|
||||
@@ -17,3 +18,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SHAMAP_HASH_H_INCLUDED
|
||||
#define XRPL_BASICS_SHAMAP_HASH_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
@@ -97,3 +98,5 @@ extract(SHAMapHash const& key)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_SHAMAP_HASH_H_INCLUDED
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SHAREDWEAKCACHEPOINTER_H_INCLUDED
|
||||
#define XRPL_BASICS_SHAREDWEAKCACHEPOINTER_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
@@ -112,3 +113,4 @@ private:
|
||||
std::variant<std::shared_ptr<T>, std::weak_ptr<T>> combo_;
|
||||
};
|
||||
} // namespace xrpl
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SHAREDWEAKCACHEPOINTER_IPP_INCLUDED
|
||||
#define XRPL_BASICS_SHAREDWEAKCACHEPOINTER_IPP_INCLUDED
|
||||
|
||||
#include <xrpl/basics/SharedWeakCachePointer.h>
|
||||
|
||||
@@ -163,3 +164,4 @@ SharedWeakCachePointer<T>::convertToWeak()
|
||||
return false;
|
||||
}
|
||||
} // namespace xrpl
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SLABALLOCATOR_H_INCLUDED
|
||||
#define XRPL_BASICS_SLABALLOCATOR_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/ByteUtilities.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
@@ -385,3 +386,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_SLABALLOCATOR_H_INCLUDED
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SLICE_H_INCLUDED
|
||||
#define XRPL_BASICS_SLICE_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/contract.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
@@ -230,3 +231,5 @@ makeSlice(std::basic_string<char, Traits, Alloc> const& s)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_STRINGUTILITIES_H_INCLUDED
|
||||
#define XRPL_BASICS_STRINGUTILITIES_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/Blob.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
@@ -131,3 +132,5 @@ bool
|
||||
isProperlyFormedTomlDomain(std::string_view domain);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_TAGGEDCACHE_H_INCLUDED
|
||||
#define XRPL_BASICS_TAGGEDCACHE_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/IntrusivePointer.h>
|
||||
#include <xrpl/basics/Log.h>
|
||||
@@ -297,3 +298,5 @@ private:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_TAGGEDCACHE_IPP_INCLUDED
|
||||
#define XRPL_BASICS_TAGGEDCACHE_IPP_INCLUDED
|
||||
|
||||
#include <xrpl/basics/IntrusivePointer.ipp>
|
||||
#include <xrpl/basics/TaggedCache.h>
|
||||
@@ -783,3 +784,5 @@ TaggedCache<Key, T, IsKeyCache, SharedWeakUnionPointer, SharedPointerType, Hash,
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_TOSTRING_H_INCLUDED
|
||||
#define XRPL_BASICS_TOSTRING_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
@@ -43,3 +44,5 @@ to_string(char const* s)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_UNORDEREDCONTAINERS_H_INCLUDED
|
||||
#define XRPL_BASICS_UNORDEREDCONTAINERS_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/hardened_hash.h>
|
||||
#include <xrpl/basics/partitioned_unordered_map.h>
|
||||
@@ -98,3 +99,5 @@ template <
|
||||
using hardened_hash_multiset = std::unordered_multiset<Value, Hash, Pred, Allocator>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_UPTIMETIMER_H_INCLUDED
|
||||
#define XRPL_BASICS_UPTIMETIMER_H_INCLUDED
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
@@ -45,3 +46,5 @@ private:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_ALGORITHM_H_INCLUDED
|
||||
#define XRPL_ALGORITHM_H_INCLUDED
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -89,3 +90,5 @@ remove_if_intersect_or_match(FwdIter1 first1, FwdIter1 last1, InputIter2 first2,
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_BASE64_H_INCLUDED
|
||||
#define XRPL_BASICS_BASE64_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
@@ -52,3 +53,5 @@ std::string
|
||||
base64_decode(std::string_view data);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file license.txt or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_BASE_UINT_H_INCLUDED
|
||||
#define XRPL_BASICS_BASE_UINT_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/Expected.h>
|
||||
#include <xrpl/basics/Slice.h>
|
||||
@@ -357,6 +358,7 @@ public:
|
||||
base_uint&
|
||||
operator&=(base_uint const& b)
|
||||
{
|
||||
XRPL_ASSERT(WIDTH == b.WIDTH, "input size mismatch");
|
||||
for (int i = 0; i < WIDTH; i++)
|
||||
data_[i] &= b.data_[i];
|
||||
|
||||
@@ -643,3 +645,5 @@ struct is_uniquely_represented<xrpl::base_uint<Bits, Tag>> : public std::true_ty
|
||||
};
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_CHRONO_H_INCLUDED
|
||||
#define XRPL_BASICS_CHRONO_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/clock/abstract_clock.h>
|
||||
#include <xrpl/beast/clock/basic_seconds_clock.h>
|
||||
@@ -98,3 +99,5 @@ stopwatch()
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_COMPARATORS_H_INCLUDED
|
||||
#define XRPL_BASICS_COMPARATORS_H_INCLUDED
|
||||
|
||||
#include <functional>
|
||||
|
||||
@@ -52,3 +53,5 @@ using equal_to = std::equal_to<T>;
|
||||
#endif
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_CONTRACT_H_INCLUDED
|
||||
#define XRPL_BASICS_CONTRACT_H_INCLUDED
|
||||
|
||||
#include <xrpl/basics/sanitizers.h>
|
||||
#include <xrpl/beast/type_name.h>
|
||||
|
||||
#include <exception>
|
||||
@@ -24,7 +26,7 @@ LogThrow(std::string const& title);
|
||||
control to the next matching exception handler, if any.
|
||||
Otherwise, std::terminate will be called.
|
||||
*/
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Rethrow()
|
||||
{
|
||||
LogThrow("Re-throwing exception");
|
||||
@@ -32,7 +34,7 @@ Rethrow()
|
||||
}
|
||||
|
||||
template <class E, class... Args>
|
||||
[[noreturn]] inline void
|
||||
[[noreturn]] XRPL_NO_SANITIZE_ADDRESS inline void
|
||||
Throw(Args&&... args)
|
||||
{
|
||||
static_assert(std::is_convertible<E*, std::exception*>::value, "Exception must derive from std::exception.");
|
||||
@@ -47,3 +49,5 @@ Throw(Args&&... args)
|
||||
LogicError(std::string const& how) noexcept;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_HARDENED_HASH_H_INCLUDED
|
||||
#define XRPL_BASICS_HARDENED_HASH_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/hash/hash_append.h>
|
||||
#include <xrpl/beast/hash/xxhasher.h>
|
||||
@@ -92,3 +93,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef JOIN_H_INCLUDED
|
||||
#define JOIN_H_INCLUDED
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -79,3 +80,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_MAKE_SSLCONTEXT_H_INCLUDED
|
||||
#define XRPL_BASICS_MAKE_SSLCONTEXT_H_INCLUDED
|
||||
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
@@ -19,3 +20,5 @@ make_SSLContextAuthed(
|
||||
std::string const& cipherList);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_MULDIV_H_INCLUDED
|
||||
#define XRPL_BASICS_MULDIV_H_INCLUDED
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
@@ -21,3 +22,5 @@ std::optional<std::uint64_t>
|
||||
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_PARTITIONED_UNORDERED_MAP_H
|
||||
#define XRPL_BASICS_PARTITIONED_UNORDERED_MAP_H
|
||||
|
||||
#include <xrpl/beast/hash/uhash.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
@@ -392,3 +393,5 @@ private:
|
||||
};
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_PARTITIONED_UNORDERED_MAP_H
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_RANDOM_H_INCLUDED
|
||||
#define XRPL_BASICS_RANDOM_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
#include <xrpl/beast/xor_shift_engine.h>
|
||||
@@ -173,3 +174,5 @@ rand_bool()
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif // XRPL_BASICS_RANDOM_H_INCLUDED
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_UNITY_ROCKSDB_H_INCLUDED
|
||||
#define XRPL_UNITY_ROCKSDB_H_INCLUDED
|
||||
|
||||
#if XRPL_ROCKSDB_AVAILABLE
|
||||
// #include <rocksdb2/port/port_posix.h>
|
||||
@@ -27,3 +28,5 @@
|
||||
#include <rocksdb/write_batch.h>
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SAFE_CAST_H_INCLUDED
|
||||
#define XRPL_BASICS_SAFE_CAST_H_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@@ -68,3 +69,5 @@ unsafe_cast(Src s) noexcept
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
6
include/xrpl/basics/sanitizers.h
Normal file
6
include/xrpl/basics/sanitizers.h
Normal file
@@ -0,0 +1,6 @@
|
||||
// Helper to disable ASan/HwASan for specific functions
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define XRPL_NO_SANITIZE_ADDRESS __attribute__((no_sanitize("address", "hwaddress")))
|
||||
#else
|
||||
#define XRPL_NO_SANITIZE_ADDRESS
|
||||
#endif
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SCOPE_H_INCLUDED
|
||||
#define XRPL_BASICS_SCOPE_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
@@ -219,3 +220,5 @@ template <class Mutex>
|
||||
scope_unlock(std::unique_lock<Mutex>&) -> scope_unlock<Mutex>;
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2022, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_SPINLOCK_H_INCLUDED
|
||||
#define XRPL_BASICS_SPINLOCK_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
@@ -200,3 +201,5 @@ public:
|
||||
/** @} */
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef XRPL_BASICS_STRHEX_H_INCLUDED
|
||||
#define XRPL_BASICS_STRHEX_H_INCLUDED
|
||||
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <boost/endian/conversion.hpp>
|
||||
@@ -26,3 +27,5 @@ strHex(T const& from)
|
||||
}
|
||||
|
||||
} // namespace xrpl
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2014, Nikolaos D. Bougalis <nikb@bougalis.net>
|
||||
|
||||
#pragma once
|
||||
#ifndef BEAST_UTILITY_TAGGED_INTEGER_H_INCLUDED
|
||||
#define BEAST_UTILITY_TAGGED_INTEGER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/hash/hash_append.h>
|
||||
|
||||
@@ -201,3 +202,4 @@ struct is_contiguously_hashable<xrpl::tagged_integer<Int, Tag>, HashAlgorithm>
|
||||
};
|
||||
|
||||
} // namespace beast
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_ASIO_IO_LATENCY_PROBE_H_INCLUDED
|
||||
#define BEAST_ASIO_IO_LATENCY_PROBE_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
|
||||
@@ -225,3 +226,5 @@ private:
|
||||
};
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CHRONO_ABSTRACT_CLOCK_H_INCLUDED
|
||||
#define BEAST_CHRONO_ABSTRACT_CLOCK_H_INCLUDED
|
||||
|
||||
namespace beast {
|
||||
|
||||
@@ -88,3 +89,5 @@ get_abstract_clock()
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CHRONO_BASIC_SECONDS_CLOCK_H_INCLUDED
|
||||
#define BEAST_CHRONO_BASIC_SECONDS_CLOCK_H_INCLUDED
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -32,3 +33,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CHRONO_MANUAL_CLOCK_H_INCLUDED
|
||||
#define BEAST_CHRONO_MANUAL_CLOCK_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/clock/abstract_clock.h>
|
||||
#include <xrpl/beast/utility/instrumentation.h>
|
||||
@@ -74,3 +75,5 @@ public:
|
||||
};
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_CONTAINER_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_CONTAINER_H_INCLUDED
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
@@ -11,3 +12,5 @@ struct is_aged_container : std::false_type
|
||||
};
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_CONTAINER_UTILITY_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_CONTAINER_UTILITY_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/aged_container.h>
|
||||
|
||||
@@ -23,3 +24,5 @@ expire(AgedContainer& c, std::chrono::duration<Rep, Period> const& age)
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_MAP_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_MAP_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_ordered_container.h>
|
||||
|
||||
@@ -17,3 +18,5 @@ template <
|
||||
using aged_map = detail::aged_ordered_container<false, true, Key, T, Clock, Compare, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_MULTIMAP_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_MULTIMAP_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_ordered_container.h>
|
||||
|
||||
@@ -17,3 +18,5 @@ template <
|
||||
using aged_multimap = detail::aged_ordered_container<true, true, Key, T, Clock, Compare, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_MULTISET_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_MULTISET_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_ordered_container.h>
|
||||
|
||||
@@ -16,3 +17,5 @@ template <
|
||||
using aged_multiset = detail::aged_ordered_container<true, false, Key, void, Clock, Compare, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_SET_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_SET_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_ordered_container.h>
|
||||
|
||||
@@ -16,3 +17,5 @@ template <
|
||||
using aged_set = detail::aged_ordered_container<false, false, Key, void, Clock, Compare, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_UNORDERED_MAP_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_UNORDERED_MAP_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_unordered_container.h>
|
||||
|
||||
@@ -18,3 +19,5 @@ template <
|
||||
using aged_unordered_map = detail::aged_unordered_container<false, true, Key, T, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_UNORDERED_MULTIMAP_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_UNORDERED_MULTIMAP_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_unordered_container.h>
|
||||
|
||||
@@ -18,3 +19,5 @@ template <
|
||||
using aged_unordered_multimap = detail::aged_unordered_container<true, true, Key, T, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_UNORDERED_MULTISET_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_UNORDERED_MULTISET_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_unordered_container.h>
|
||||
|
||||
@@ -18,3 +19,5 @@ using aged_unordered_multiset =
|
||||
detail::aged_unordered_container<true, false, Key, void, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_AGED_UNORDERED_SET_H_INCLUDED
|
||||
#define BEAST_CONTAINER_AGED_UNORDERED_SET_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/container/detail/aged_unordered_container.h>
|
||||
|
||||
@@ -17,3 +18,5 @@ template <
|
||||
using aged_unordered_set = detail::aged_unordered_container<false, false, Key, void, Clock, Hash, KeyEqual, Allocator>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_DETAIL_AGED_ASSOCIATIVE_CONTAINER_H_INCLUDED
|
||||
#define BEAST_CONTAINER_DETAIL_AGED_ASSOCIATIVE_CONTAINER_H_INCLUDED
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
@@ -32,3 +33,5 @@ struct aged_associative_container_extract_t<false>
|
||||
|
||||
} // namespace detail
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_DETAIL_AGED_CONTAINER_ITERATOR_H_INCLUDED
|
||||
#define BEAST_CONTAINER_DETAIL_AGED_CONTAINER_ITERATOR_H_INCLUDED
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
@@ -145,3 +146,5 @@ private:
|
||||
} // namespace detail
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_DETAIL_AGED_ORDERED_CONTAINER_H_INCLUDED
|
||||
#define BEAST_CONTAINER_DETAIL_AGED_ORDERED_CONTAINER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/clock/abstract_clock.h>
|
||||
#include <xrpl/beast/container/aged_container.h>
|
||||
@@ -1686,3 +1687,5 @@ expire(
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_DETAIL_AGED_UNORDERED_CONTAINER_H_INCLUDED
|
||||
#define BEAST_CONTAINER_DETAIL_AGED_UNORDERED_CONTAINER_H_INCLUDED
|
||||
|
||||
#include <xrpl/beast/clock/abstract_clock.h>
|
||||
#include <xrpl/beast/container/aged_container.h>
|
||||
@@ -2225,3 +2226,5 @@ expire(
|
||||
}
|
||||
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
// Official repository: https://github.com/boostorg/beast
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef BEAST_CONTAINER_DETAIL_EMPTY_BASE_OPTIMIZATION_H_INCLUDED
|
||||
#define BEAST_CONTAINER_DETAIL_EMPTY_BASE_OPTIMIZATION_H_INCLUDED
|
||||
|
||||
#include <boost/type_traits/is_final.hpp>
|
||||
|
||||
@@ -88,3 +89,5 @@ public:
|
||||
|
||||
} // namespace detail
|
||||
} // namespace beast
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user