mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 20:05:51 +00:00
Compare commits
3 Commits
revert-256
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec5d1eb65c | ||
|
|
5b417bdc45 | ||
|
|
ce631a1f5a |
@@ -2,14 +2,16 @@
|
||||
|
||||
exec 1>&2
|
||||
|
||||
# format all relevant sources
|
||||
find src unittests -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 clang-format -i
|
||||
# paths to check and re-format
|
||||
sources="src unittests"
|
||||
formatter="clang-format -i"
|
||||
|
||||
# check how many lines differ
|
||||
lines=$(git diff src unittests | wc -l)
|
||||
first=$(git diff $sources)
|
||||
find $sources -type f \( -name '*.cpp' -o -name '*.h' -o -name '*.ipp' \) -print0 | xargs -0 $formatter
|
||||
second=$(git diff $sources)
|
||||
changes=$(diff <(echo "$first") <(echo "$second") | wc -l | sed -e 's/^[[:space:]]*//')
|
||||
|
||||
# check if there is any updated files
|
||||
if [ "$lines" != "0" ]; then
|
||||
if [ "$changes" != "0" ]; then
|
||||
cat <<\EOF
|
||||
|
||||
WARNING
|
||||
|
||||
21
.github/actions/sign/action.yml
vendored
21
.github/actions/sign/action.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: 'Sign packages'
|
||||
runs:
|
||||
using: "composite"
|
||||
|
||||
steps:
|
||||
- name: Sign
|
||||
shell: bash
|
||||
run: |
|
||||
set -ex -o pipefail
|
||||
echo "$GPG_KEY_B64"| base64 -d | gpg --batch --no-tty --allow-secret-key-import --import -
|
||||
unset GPG_KEY_B64
|
||||
export GPG_PASSPHRASE=$(echo $GPG_KEY_PASS_B64 | base64 -di)
|
||||
unset GPG_KEY_PASS_B64
|
||||
export GPG_KEYID=$(gpg --with-colon --list-secret-keys | head -n1 | cut -d : -f 5)
|
||||
for PKG in $(ls *.deb); do
|
||||
dpkg-sig \
|
||||
-g "--no-tty --digest-algo 'sha512' --passphrase '${GPG_PASSPHRASE}' --pinentry-mode=loopback" \
|
||||
-k "${GPG_KEYID}" \
|
||||
--sign builder \
|
||||
$PKG
|
||||
done
|
||||
99
.github/workflows/build.yml
vendored
99
.github/workflows/build.yml
vendored
@@ -1,9 +1,9 @@
|
||||
name: Build Clio
|
||||
on:
|
||||
push:
|
||||
branches: [master, release, develop, develop-next]
|
||||
branches: [master, release/*, develop, develop-next]
|
||||
pull_request:
|
||||
branches: [master, release, develop, develop-next]
|
||||
branches: [master, release/*, develop, develop-next]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -29,6 +29,7 @@ jobs:
|
||||
- suffix: rpm
|
||||
image: rippleci/clio-rpm-builder:2022-09-17
|
||||
script: rpm
|
||||
|
||||
container:
|
||||
image: ${{ matrix.type.image }}
|
||||
|
||||
@@ -67,59 +68,63 @@ jobs:
|
||||
name: clio_tests-${{ matrix.type.suffix }}
|
||||
path: ${{ github.workspace }}/clio_tests
|
||||
|
||||
sign:
|
||||
name: Sign packages
|
||||
needs: build_clio
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release' || github.ref == 'refs/heads/develop'
|
||||
env:
|
||||
GPG_KEY_B64: ${{ secrets.GPG_KEY_B64 }}
|
||||
GPG_KEY_PASS_B64: ${{ secrets.GPG_KEY_PASS_B64 }}
|
||||
build_dev:
|
||||
name: ${{ matrix.os.name }} test
|
||||
needs: lint
|
||||
continue-on-error: ${{ matrix.os.experimental }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
type:
|
||||
- suffix: deb
|
||||
image: ubuntu:20.04
|
||||
script: dpkg
|
||||
# - suffix: rpm
|
||||
# image: centos:7
|
||||
# script: rpm
|
||||
container:
|
||||
image: ${{ matrix.type.image }}
|
||||
os:
|
||||
- name: ubuntu-22.04
|
||||
experimental: true
|
||||
- name: macos-11
|
||||
experimental: true
|
||||
- name: macos-12
|
||||
experimental: false
|
||||
runs-on: ${{ matrix.os.name }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install dpkg-sig
|
||||
run: |
|
||||
apt-get update && apt-get install -y dpkg-sig gnupg
|
||||
- name: Get package artifact
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: clio_${{ matrix.type.suffix }}_packages
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
path: clio
|
||||
|
||||
- name: find packages
|
||||
run: find . -name "*.${{ matrix.type.suffix }}"
|
||||
- name: Check Boost cache
|
||||
id: boost
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: boost
|
||||
key: ${{ runner.os }}-boost
|
||||
|
||||
- name: Sign packages
|
||||
uses: ./.github/actions/sign
|
||||
- name: Build boost
|
||||
if: steps.boost.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
curl -s -OJL "https://boostorg.jfrog.io/artifactory/main/release/1.77.0/source/boost_1_77_0.tar.gz"
|
||||
tar zxf boost_1_77_0.tar.gz
|
||||
mv boost_1_77_0 boost
|
||||
cd boost
|
||||
./bootstrap.sh
|
||||
if [[ ${{ matrix.os.name }} =~ mac ]];then
|
||||
mac_flags='cxxflags="-std=c++14"'
|
||||
fi
|
||||
./b2 ${mac_flags}
|
||||
|
||||
- name: install deps
|
||||
run: |
|
||||
if [[ ${{ matrix.os.name }} =~ mac ]];then
|
||||
brew install pkg-config protobuf openssl ninja cassandra-cpp-driver bison
|
||||
elif [[ ${{matrix.os.name }} =~ ubuntu ]];then
|
||||
sudo apt-get -y install git pkg-config protobuf-compiler libprotobuf-dev libssl-dev wget build-essential doxygen bison flex autoconf clang-format
|
||||
fi
|
||||
|
||||
- name: Verify the signature
|
||||
run: |
|
||||
set -e
|
||||
for PKG in $(ls *.deb); do
|
||||
gpg --verify "${PKG}"
|
||||
done
|
||||
|
||||
- name: Get short SHA
|
||||
id: shortsha
|
||||
run: echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)"
|
||||
|
||||
- name: Artifact signed packages
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: signed-clio-deb-packages-${{ steps.shortsha.outputs.sha8 }}
|
||||
path: ${{ github.workspace }}/*.deb
|
||||
- name: Build clio
|
||||
run: |
|
||||
export BOOST_ROOT=$(pwd)/boost
|
||||
cd clio
|
||||
cmake -B build
|
||||
if ! cmake --build build -j$(nproc); then
|
||||
echo '# 🔥${{ matrix.os.name }}🔥 failed!💥' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
test_clio:
|
||||
name: Test Clio
|
||||
|
||||
11
CMake/deps/SourceLocation.cmake
Normal file
11
CMake/deps/SourceLocation.cmake
Normal file
@@ -0,0 +1,11 @@
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
check_include_file_cxx("source_location" SOURCE_LOCATION_AVAILABLE)
|
||||
if(SOURCE_LOCATION_AVAILABLE)
|
||||
target_compile_definitions(clio PUBLIC "HAS_SOURCE_LOCATION")
|
||||
endif()
|
||||
|
||||
check_include_file_cxx("experimental/source_location" EXPERIMENTAL_SOURCE_LOCATION_AVAILABLE)
|
||||
if(EXPERIMENTAL_SOURCE_LOCATION_AVAILABLE)
|
||||
target_compile_definitions(clio PUBLIC "HAS_EXPERIMENTAL_SOURCE_LOCATION")
|
||||
endif()
|
||||
@@ -10,7 +10,8 @@ if(NOT googletest_POPULATED)
|
||||
add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
target_link_libraries(clio_tests PUBLIC clio gtest_main)
|
||||
target_link_libraries(clio_tests PUBLIC clio gmock_main)
|
||||
target_include_directories(clio_tests PRIVATE unittests)
|
||||
|
||||
enable_testing()
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ ExecStart=@CLIO_INSTALL_DIR@/bin/clio_server @CLIO_INSTALL_DIR@/etc/config.json
|
||||
Restart=on-failure
|
||||
User=clio
|
||||
Group=clio
|
||||
LimitNOFILE=65536
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-narrowing -Wall -Werror -Wno-dangling-else")
|
||||
target_compile_options(clio
|
||||
PUBLIC -Wall
|
||||
-Werror
|
||||
-Wno-narrowing
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-dangling-else)
|
||||
|
||||
@@ -21,11 +21,11 @@ if(NOT GIT_COMMIT_HASH)
|
||||
find_package(Git)
|
||||
if(Git_FOUND)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE gch)
|
||||
if(gch)
|
||||
set(GIT_COMMIT_HASH "${gch}")
|
||||
message(STATUS "Git commit: ${GIT_COMMIT_HASH}")
|
||||
add_definitions(-DCLIO_GIT_COMMIT_HASH="${GIT_COMMIT_HASH}")
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE git-ref)
|
||||
if(git-ref)
|
||||
set(BUILD "${git-ref}")
|
||||
message(STATUS "Build version: ${BUILD}")
|
||||
add_definitions(-DCLIO_BUILD="${BUILD}")
|
||||
endif()
|
||||
endif()
|
||||
endif() #git
|
||||
@@ -33,7 +33,6 @@ if(PACKAGING)
|
||||
add_definitions(-DPKG=1)
|
||||
endif()
|
||||
|
||||
|
||||
add_library(clio)
|
||||
target_compile_features(clio PUBLIC cxx_std_20)
|
||||
target_include_directories(clio PUBLIC src)
|
||||
@@ -45,6 +44,7 @@ include(CMake/ClioVersion.cmake)
|
||||
include(CMake/deps/rippled.cmake)
|
||||
include(CMake/deps/Boost.cmake)
|
||||
include(CMake/deps/cassandra.cmake)
|
||||
include(CMake/deps/SourceLocation.cmake)
|
||||
|
||||
target_sources(clio PRIVATE
|
||||
## Main
|
||||
@@ -99,19 +99,23 @@ target_sources(clio PRIVATE
|
||||
src/rpc/handlers/Subscribe.cpp
|
||||
# Server
|
||||
src/rpc/handlers/ServerInfo.cpp
|
||||
# Utility
|
||||
# Utilities
|
||||
src/rpc/handlers/Random.cpp
|
||||
src/util/Taggable.cpp
|
||||
src/config/Config.cpp)
|
||||
src/config/Config.cpp
|
||||
src/log/Logger.cpp
|
||||
src/util/Taggable.cpp)
|
||||
|
||||
add_executable(clio_server src/main/main.cpp)
|
||||
target_link_libraries(clio_server PUBLIC clio)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_executable(clio_tests
|
||||
unittests/RPCErrors.cpp
|
||||
unittests/config.cpp
|
||||
unittests/main.cpp)
|
||||
unittests/RPCErrors.cpp
|
||||
unittests/Backend.cpp
|
||||
unittests/Logger.cpp
|
||||
unittests/Config.cpp
|
||||
unittests/ProfilerTest.cpp
|
||||
unittests/DOSGuard.cpp)
|
||||
include(CMake/deps/gtest.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
63
README.md
63
README.md
@@ -22,7 +22,7 @@ from which data can be extracted. The rippled node does not need to be running o
|
||||
|
||||
## Building
|
||||
|
||||
Clio is built with CMake. Clio requires at least GCC-11 (C++20), and Boost 1.75.0 or later.
|
||||
Clio is built with CMake. Clio requires at least GCC-11/clang-14.0.0 (C++20), and Boost 1.75.0.
|
||||
|
||||
Use these instructions to build a Clio executable from the source. These instructions were tested on Ubuntu 20.04 LTS.
|
||||
|
||||
@@ -72,6 +72,36 @@ server is running
|
||||
to the IP of your Clio server. This entry can take the form of a comma-separated list if
|
||||
you are running multiple Clio nodes.
|
||||
|
||||
|
||||
In addition, the parameter `start_sequence` can be included and configured within the top level of the config file. This parameter specifies the sequence of first ledger to extract if the database is empty. Note that ETL extracts ledgers in order and that no backfilling functionality currently exists, meaning Clio will not retroactively learn ledgers older than the one you specify. Choosing to specify this or not will yield the following behavior:
|
||||
- If this setting is absent and the database is empty, ETL will start with the next ledger validated by the network.
|
||||
- If this setting is present and the database is not empty, an exception is thrown.
|
||||
|
||||
In addition, the optional parameter `finish_sequence` can be added to the json file as well, specifying where the ledger can stop.
|
||||
|
||||
To add `start_sequence` and/or `finish_sequence` to the config.json file appropriately, they will be on the same top level of precedence as other parameters (such as `database`, `etl_sources`, `read_only`, etc.) and be specified with an integer. Here is an example snippet from the config file:
|
||||
|
||||
```json
|
||||
"start_sequence": 12345,
|
||||
"finish_sequence": 54321
|
||||
```
|
||||
|
||||
The parameters `ssl_cert_file` and `ssl_key_file` can also be added to the top level of precedence of our Clio config. `ssl_cert_file` specifies the filepath for your SSL cert while `ssl_key_file` specifies the filepath for your SSL key. It is up to you how to change ownership of these folders for your designated Clio user. Your options include:
|
||||
- Copying the two files as root somewhere that's accessible by the Clio user, then running `sudo chown` to your user
|
||||
- Changing the permissions directly so it's readable by your Clio user
|
||||
- Running Clio as root (strongly discouraged)
|
||||
|
||||
An example of how to specify `ssl_cert_file` and `ssl_key_file` in the config:
|
||||
|
||||
```json
|
||||
"server":{
|
||||
"ip": "0.0.0.0",
|
||||
"port": 51233
|
||||
},
|
||||
"ssl_cert_file" : "/full/path/to/cert.file",
|
||||
"ssl_key_file" : "/full/path/to/key.file"
|
||||
```
|
||||
|
||||
Once your config files are ready, start rippled and Clio. It doesn't matter which you
|
||||
start first, and it's fine to stop one or the other and restart at any given time.
|
||||
|
||||
@@ -152,9 +182,33 @@ You must:
|
||||
## Logging
|
||||
Clio provides several logging options, all are configurable via the config file and are detailed below.
|
||||
|
||||
`log_level`: The minimum level of severity at which the log message will be outputted.
|
||||
`log_level`: The minimum level of severity at which the log message will be outputted by default.
|
||||
Severity options are `trace`, `debug`, `info`, `warning`, `error`, `fatal`. Defaults to `info`.
|
||||
|
||||
`log_format`: The format of log lines produced by clio. Defaults to `"%TimeStamp% (%SourceLocation%) [%ThreadID%] %Channel%:%Severity% %Message%"`.
|
||||
Each of the variables expands like so
|
||||
- `TimeStamp`: The full date and time of the log entry
|
||||
- `SourceLocation`: A partial path to the c++ file and the line number in said file (`source/file/path:linenumber`)
|
||||
- `ThreadID`: The ID of the thread the log entry is written from
|
||||
- `Channel`: The channel that this log entry was sent to
|
||||
- `Severity`: The severity (aka log level) the entry was sent at
|
||||
- `Message`: The actual log message
|
||||
|
||||
`log_channels`: An array of json objects, each overriding properties for a logging `channel`.
|
||||
At the moment of writing, only `log_level` can be overriden using this mechanism.
|
||||
|
||||
Each object is of this format:
|
||||
```json
|
||||
{
|
||||
"channel": "Backend",
|
||||
"log_level": "fatal"
|
||||
}
|
||||
```
|
||||
If no override is present for a given channel, that channel will log at the severity specified by the global `log_level`.
|
||||
Overridable log channels: `Backend`, `WebServer`, `Subscriptions`, `RPC`, `ETL` and `Performance`.
|
||||
|
||||
> **Note:** See `example-config.json` for more details.
|
||||
|
||||
`log_to_console`: Enable/disable log output to console. Options are `true`/`false`. Defaults to true.
|
||||
|
||||
`log_directory`: Path to the directory where log files are stored. If such directory doesn't exist, Clio will create it. If not specified, logs are not written to a file.
|
||||
@@ -170,6 +224,11 @@ rotate the current log file. Defaults to 12 hours.
|
||||
Note, time-based log rotation occurs dependently on size-based log rotation, where if a
|
||||
size-based log rotation occurs, the timer for the time-based rotation will reset.
|
||||
|
||||
`log_tag_style`: Tag implementation to use. Must be one of:
|
||||
- `uint`: Lock free and threadsafe but outputs just a simple unsigned integer
|
||||
- `uuid`: Threadsafe and outputs a UUID tag
|
||||
- `none`: Don't use tagging at all
|
||||
|
||||
## Cassandra / Scylla Administration
|
||||
|
||||
Since Clio relies on either Cassandra or Scylla for its database backend, here are some important considerations:
|
||||
|
||||
@@ -1,46 +1,87 @@
|
||||
{
|
||||
"database":
|
||||
{
|
||||
"type":"cassandra",
|
||||
"cassandra":
|
||||
{
|
||||
"contact_points":"127.0.0.1",
|
||||
"port":9042,
|
||||
"keyspace":"clio",
|
||||
"replication_factor":1,
|
||||
"table_prefix":"",
|
||||
"max_write_requests_outstanding":25000,
|
||||
"max_read_requests_outstanding":30000,
|
||||
"threads":8
|
||||
"database": {
|
||||
"type": "cassandra",
|
||||
"cassandra": {
|
||||
"contact_points": "127.0.0.1",
|
||||
"port": 9042,
|
||||
"keyspace": "clio",
|
||||
"replication_factor": 1,
|
||||
"table_prefix": "",
|
||||
"max_write_requests_outstanding": 25000,
|
||||
"max_read_requests_outstanding": 30000,
|
||||
"threads": 8
|
||||
}
|
||||
},
|
||||
"etl_sources":
|
||||
[
|
||||
"etl_sources": [
|
||||
{
|
||||
"ip":"127.0.0.1",
|
||||
"ws_port":"6006",
|
||||
"grpc_port":"50051"
|
||||
"ip": "127.0.0.1",
|
||||
"ws_port": "6006",
|
||||
"grpc_port": "50051"
|
||||
}
|
||||
],
|
||||
"dos_guard":
|
||||
{
|
||||
"whitelist":["127.0.0.1"]
|
||||
"whitelist":["127.0.0.1"], // comma-separated list of ips to exclude from rate limiting
|
||||
/* The below values are the default values and are only specified here
|
||||
* for documentation purposes. The rate limiter currently limits
|
||||
* connections and bandwidth per ip. The rate limiter looks at the raw
|
||||
* ip of a client connection, and so requests routed through a load
|
||||
* balancer will all have the same ip and be treated as a single client
|
||||
*/
|
||||
"max_fetches":100000000, // max bytes per ip per sweep interval
|
||||
"max_connections":1, // max connections per ip
|
||||
"sweep_interval": 10 // time in seconds before resetting bytes per ip count
|
||||
},
|
||||
"cache":
|
||||
{
|
||||
"peers": [{"ip":"127.0.0.1","port":51234}]
|
||||
},
|
||||
"server":{
|
||||
"ip":"0.0.0.0",
|
||||
"port":51233
|
||||
"ip": "0.0.0.0",
|
||||
"port": 51233,
|
||||
/* Max number of requests to queue up before rejecting further requests.
|
||||
* Defaults to 0, which disables the limit
|
||||
*/
|
||||
"max_queue_size":500
|
||||
},
|
||||
"log_level":"debug",
|
||||
"log_channels": [
|
||||
{
|
||||
"channel": "Backend",
|
||||
"log_level": "fatal"
|
||||
},
|
||||
{
|
||||
"channel": "WebServer",
|
||||
"log_level": "info"
|
||||
},
|
||||
{
|
||||
"channel": "Subscriptions",
|
||||
"log_level": "info"
|
||||
},
|
||||
{
|
||||
"channel": "RPC",
|
||||
"log_level": "error"
|
||||
},
|
||||
{
|
||||
"channel": "ETL",
|
||||
"log_level": "debug"
|
||||
},
|
||||
{
|
||||
"channel": "Performance",
|
||||
"log_level": "trace"
|
||||
}
|
||||
],
|
||||
"log_level": "info",
|
||||
"log_format": "%TimeStamp% (%SourceLocation%) [%ThreadID%] %Channel%:%Severity% %Message%", // This is the default format
|
||||
"log_to_console": true,
|
||||
"log_directory":"./clio_log",
|
||||
"log_directory": "./clio_log",
|
||||
"log_rotation_size": 2048,
|
||||
"log_directory_max_size": 51200,
|
||||
"log_rotation_hour_interval": 12,
|
||||
"log_tag_style": "uint",
|
||||
"extractor_threads":8,
|
||||
"read_only":false
|
||||
"extractor_threads": 8,
|
||||
"read_only": false,
|
||||
//"start_sequence": [integer] the ledger index to start from,
|
||||
//"finish_sequence": [integer] the ledger index to finish at,
|
||||
//"ssl_cert_file" : "/full/path/to/cert.file",
|
||||
//"ssl_key_file" : "/full/path/to/key.file"
|
||||
}
|
||||
|
||||
@@ -1,16 +1,37 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_BACKENDFACTORY_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_BACKENDFACTORY_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <backend/CassandraBackend.h>
|
||||
#include <config/Config.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace Backend {
|
||||
std::shared_ptr<BackendInterface>
|
||||
make_Backend(boost::asio::io_context& ioc, clio::Config const& config)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << ": Constructing BackendInterface";
|
||||
static clio::Logger log{"Backend"};
|
||||
log.info() << "Constructing BackendInterface";
|
||||
|
||||
auto readOnly = config.valueOr("read_only", false);
|
||||
auto type = config.value<std::string>("database.type");
|
||||
@@ -34,11 +55,8 @@ make_Backend(boost::asio::io_context& ioc, clio::Config const& config)
|
||||
backend->updateRange(rng->maxSequence);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << ": Constructed BackendInterface Successfully";
|
||||
log.info() << "Constructed BackendInterface Successfully";
|
||||
|
||||
return backend;
|
||||
}
|
||||
} // namespace Backend
|
||||
|
||||
#endif // RIPPLE_REPORTING_BACKEND_FACTORY
|
||||
|
||||
@@ -1,6 +1,34 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gLog{"Backend"};
|
||||
} // namespace
|
||||
|
||||
namespace Backend {
|
||||
bool
|
||||
BackendInterface::finishWrites(std::uint32_t const ledgerSequence)
|
||||
@@ -26,7 +54,7 @@ std::optional<LedgerRange>
|
||||
BackendInterface::hardFetchLedgerRangeNoThrow(
|
||||
boost::asio::yield_context& yield) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << "(yield)";
|
||||
gLog.trace() << "called";
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@@ -43,7 +71,7 @@ BackendInterface::hardFetchLedgerRangeNoThrow(
|
||||
std::optional<LedgerRange>
|
||||
BackendInterface::hardFetchLedgerRangeNoThrow() const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << "()";
|
||||
gLog.trace() << "called";
|
||||
return retryOnTimeout([&]() { return hardFetchLedgerRange(); });
|
||||
}
|
||||
|
||||
@@ -57,21 +85,17 @@ BackendInterface::fetchLedgerObject(
|
||||
auto obj = cache_.get(key, sequence);
|
||||
if (obj)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - cache hit - " << ripple::strHex(key);
|
||||
gLog.trace() << "Cache hit - " << ripple::strHex(key);
|
||||
return *obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - cache miss - " << ripple::strHex(key);
|
||||
gLog.trace() << "Cache miss - " << ripple::strHex(key);
|
||||
auto dbObj = doFetchLedgerObject(key, sequence, yield);
|
||||
if (!dbObj)
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - missed cache and missed in db";
|
||||
gLog.trace() << "Missed cache and missed in db";
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - missed cache but found in db";
|
||||
gLog.trace() << "Missed cache but found in db";
|
||||
return dbObj;
|
||||
}
|
||||
}
|
||||
@@ -93,9 +117,8 @@ BackendInterface::fetchLedgerObjects(
|
||||
else
|
||||
misses.push_back(keys[i]);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - cache hits = " << keys.size() - misses.size()
|
||||
<< " - cache misses = " << misses.size();
|
||||
gLog.trace() << "Cache hits = " << keys.size() - misses.size()
|
||||
<< " - cache misses = " << misses.size();
|
||||
|
||||
if (misses.size())
|
||||
{
|
||||
@@ -121,11 +144,9 @@ BackendInterface::fetchSuccessorKey(
|
||||
{
|
||||
auto succ = cache_.getSuccessor(key, ledgerSequence);
|
||||
if (succ)
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - cache hit - " << ripple::strHex(key);
|
||||
gLog.trace() << "Cache hit - " << ripple::strHex(key);
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " - cache miss - " << ripple::strHex(key);
|
||||
gLog.trace() << "Cache miss - " << ripple::strHex(key);
|
||||
return succ ? succ->key : doFetchSuccessorKey(key, ledgerSequence, yield);
|
||||
}
|
||||
|
||||
@@ -179,8 +200,8 @@ BackendInterface::fetchBookOffers(
|
||||
succMillis += getMillis(mid2 - mid1);
|
||||
if (!offerDir || offerDir->key >= bookEnd)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " - offerDir.has_value() "
|
||||
<< offerDir.has_value() << " breaking";
|
||||
gLog.trace() << "offerDir.has_value() " << offerDir.has_value()
|
||||
<< " breaking";
|
||||
break;
|
||||
}
|
||||
uTipIndex = offerDir->key;
|
||||
@@ -196,8 +217,7 @@ BackendInterface::fetchBookOffers(
|
||||
auto next = sle.getFieldU64(ripple::sfIndexNext);
|
||||
if (!next)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " next is empty. breaking";
|
||||
gLog.trace() << "Next is empty. breaking";
|
||||
break;
|
||||
}
|
||||
auto nextKey = ripple::keylet::page(uTipIndex, next);
|
||||
@@ -214,29 +234,27 @@ BackendInterface::fetchBookOffers(
|
||||
auto objs = fetchLedgerObjects(keys, ledgerSequence, yield);
|
||||
for (size_t i = 0; i < keys.size() && i < limit; ++i)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " key = " << ripple::strHex(keys[i])
|
||||
<< " blob = " << ripple::strHex(objs[i])
|
||||
<< " ledgerSequence = " << ledgerSequence;
|
||||
gLog.trace() << "Key = " << ripple::strHex(keys[i])
|
||||
<< " blob = " << ripple::strHex(objs[i])
|
||||
<< " ledgerSequence = " << ledgerSequence;
|
||||
assert(objs[i].size());
|
||||
page.offers.push_back({keys[i], objs[i]});
|
||||
}
|
||||
auto end = std::chrono::system_clock::now();
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " "
|
||||
<< "Fetching " << std::to_string(keys.size()) << " offers took "
|
||||
<< std::to_string(getMillis(mid - begin))
|
||||
<< " milliseconds. Fetching next dir took "
|
||||
<< std::to_string(succMillis) << " milliseonds. Fetched next dir "
|
||||
<< std::to_string(numSucc) << " times"
|
||||
<< " Fetching next page of dir took " << std::to_string(pageMillis)
|
||||
<< " milliseconds"
|
||||
<< ". num pages = " << std::to_string(numPages)
|
||||
<< ". Fetching all objects took "
|
||||
<< std::to_string(getMillis(end - mid))
|
||||
<< " milliseconds. total time = "
|
||||
<< std::to_string(getMillis(end - begin)) << " milliseconds"
|
||||
<< " book = " << ripple::strHex(book);
|
||||
gLog.debug() << "Fetching " << std::to_string(keys.size())
|
||||
<< " offers took " << std::to_string(getMillis(mid - begin))
|
||||
<< " milliseconds. Fetching next dir took "
|
||||
<< std::to_string(succMillis)
|
||||
<< " milliseonds. Fetched next dir " << std::to_string(numSucc)
|
||||
<< " times"
|
||||
<< " Fetching next page of dir took "
|
||||
<< std::to_string(pageMillis) << " milliseconds"
|
||||
<< ". num pages = " << std::to_string(numPages)
|
||||
<< ". Fetching all objects took "
|
||||
<< std::to_string(getMillis(end - mid))
|
||||
<< " milliseconds. total time = "
|
||||
<< std::to_string(getMillis(end - begin)) << " milliseconds"
|
||||
<< " book = " << ripple::strHex(book);
|
||||
|
||||
return page;
|
||||
}
|
||||
@@ -274,16 +292,15 @@ BackendInterface::fetchLedgerPage(
|
||||
page.objects.push_back({std::move(keys[i]), std::move(objects[i])});
|
||||
else if (!outOfOrder)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__
|
||||
<< " deleted or non-existent object in successor table. key = "
|
||||
gLog.error()
|
||||
<< "Deleted or non-existent object in successor table. key = "
|
||||
<< ripple::strHex(keys[i]) << " - seq = " << ledgerSequence;
|
||||
std::stringstream msg;
|
||||
for (size_t j = 0; j < objects.size(); ++j)
|
||||
{
|
||||
msg << " - " << ripple::strHex(keys[j]);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << msg.str();
|
||||
gLog.error() << msg.str();
|
||||
}
|
||||
}
|
||||
if (keys.size() && !reachedEnd)
|
||||
@@ -304,7 +321,7 @@ BackendInterface::fetchFees(
|
||||
|
||||
if (!bytes)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " - could not find fees";
|
||||
gLog.error() << "Could not find fees";
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,33 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_BACKENDINTERFACE_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_BACKENDINTERFACE_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <backend/SimpleCache.h>
|
||||
#include <backend/Types.h>
|
||||
|
||||
#include <config/Config.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
@@ -46,6 +62,8 @@ template <class F>
|
||||
auto
|
||||
retryOnTimeout(F func, size_t waitMs = 500)
|
||||
{
|
||||
static clio::Logger log{"Backend"};
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
@@ -54,9 +72,8 @@ retryOnTimeout(F func, size_t waitMs = 500)
|
||||
}
|
||||
catch (DatabaseTimeout& t)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__
|
||||
<< " Database request timed out. Sleeping and retrying ... ";
|
||||
log.error()
|
||||
<< "Database request timed out. Sleeping and retrying ... ";
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(waitMs));
|
||||
}
|
||||
}
|
||||
@@ -644,4 +661,3 @@ private:
|
||||
|
||||
} // namespace Backend
|
||||
using BackendInterface = Backend::BackendInterface;
|
||||
#endif
|
||||
|
||||
@@ -1,9 +1,33 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||
#include <backend/CassandraBackend.h>
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <log/Logger.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
namespace Backend {
|
||||
|
||||
// Type alias for async completion handlers
|
||||
@@ -16,6 +40,8 @@ template <class T, class F>
|
||||
void
|
||||
processAsyncWriteResponse(T& requestParams, CassFuture* fut, F func)
|
||||
{
|
||||
static clio::Logger log{"Backend"};
|
||||
|
||||
CassandraBackend const& backend = *requestParams.backend;
|
||||
auto rc = cass_future_error_code(fut);
|
||||
if (rc != CASS_OK)
|
||||
@@ -23,11 +49,11 @@ processAsyncWriteResponse(T& requestParams, CassFuture* fut, F func)
|
||||
// exponential backoff with a max wait of 2^10 ms (about 1 second)
|
||||
auto wait = std::chrono::milliseconds(
|
||||
lround(std::pow(2, std::min(10u, requestParams.currentRetries))));
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "ERROR!!! Cassandra write error: " << rc << ", "
|
||||
<< cass_error_desc(rc) << " id= " << requestParams.toString()
|
||||
<< ", current retries " << requestParams.currentRetries
|
||||
<< ", retrying in " << wait.count() << " milliseconds";
|
||||
log.error() << "ERROR!!! Cassandra write error: " << rc << ", "
|
||||
<< cass_error_desc(rc)
|
||||
<< " id= " << requestParams.toString()
|
||||
<< ", current retries " << requestParams.currentRetries
|
||||
<< ", retrying in " << wait.count() << " milliseconds";
|
||||
++requestParams.currentRetries;
|
||||
std::shared_ptr<boost::asio::steady_timer> timer =
|
||||
std::make_shared<boost::asio::steady_timer>(
|
||||
@@ -40,11 +66,11 @@ processAsyncWriteResponse(T& requestParams, CassFuture* fut, F func)
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " Succesfully inserted a record";
|
||||
log.trace() << "Succesfully inserted a record";
|
||||
requestParams.finish();
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void
|
||||
processAsyncWrite(CassFuture* fut, void* cbData)
|
||||
@@ -155,6 +181,7 @@ makeAndExecuteAsyncWrite(
|
||||
auto* cb = new WriteCallbackData<T, B>(b, std::move(d), bind, id);
|
||||
cb->start();
|
||||
}
|
||||
|
||||
template <class T, class B>
|
||||
std::shared_ptr<BulkWriteCallbackData<T, B>>
|
||||
makeAndExecuteBulkAsyncWrite(
|
||||
@@ -170,13 +197,14 @@ makeAndExecuteBulkAsyncWrite(
|
||||
cb->start();
|
||||
return cb;
|
||||
}
|
||||
|
||||
void
|
||||
CassandraBackend::doWriteLedgerObject(
|
||||
std::string&& key,
|
||||
std::uint32_t const seq,
|
||||
std::string&& blob)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Writing ledger object to cassandra";
|
||||
log_.trace() << "Writing ledger object to cassandra";
|
||||
if (range)
|
||||
makeAndExecuteAsyncWrite(
|
||||
this,
|
||||
@@ -204,15 +232,16 @@ CassandraBackend::doWriteLedgerObject(
|
||||
},
|
||||
"ledger_object");
|
||||
}
|
||||
|
||||
void
|
||||
CassandraBackend::writeSuccessor(
|
||||
std::string&& key,
|
||||
std::uint32_t const seq,
|
||||
std::string&& successor)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< "Writing successor. key = " << key
|
||||
<< " seq = " << std::to_string(seq) << " successor = " << successor;
|
||||
log_.trace() << "Writing successor. key = " << key.size() << " bytes. "
|
||||
<< " seq = " << std::to_string(seq)
|
||||
<< " successor = " << successor.size() << " bytes.";
|
||||
assert(key.size() != 0);
|
||||
assert(successor.size() != 0);
|
||||
makeAndExecuteAsyncWrite(
|
||||
@@ -319,7 +348,7 @@ CassandraBackend::writeTransaction(
|
||||
std::string&& transaction,
|
||||
std::string&& metadata)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Writing txn to cassandra";
|
||||
log_.trace() << "Writing txn to cassandra";
|
||||
std::string hashCpy = hash;
|
||||
|
||||
makeAndExecuteAsyncWrite(
|
||||
@@ -393,13 +422,13 @@ CassandraBackend::writeNFTs(std::vector<NFTsData>&& data)
|
||||
std::optional<LedgerRange>
|
||||
CassandraBackend::hardFetchLedgerRange(boost::asio::yield_context& yield) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Fetching from cassandra";
|
||||
log_.trace() << "Fetching from cassandra";
|
||||
CassandraStatement statement{selectLedgerRange_};
|
||||
CassandraResult result = executeAsyncRead(statement, yield);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " - no rows";
|
||||
log_.error() << "No rows";
|
||||
return {};
|
||||
}
|
||||
LedgerRange range;
|
||||
@@ -496,43 +525,39 @@ CassandraBackend::fetchTransactions(
|
||||
std::vector<TransactionAndMetadata> results{numHashes};
|
||||
std::vector<std::shared_ptr<ReadCallbackData<result_type>>> cbs;
|
||||
cbs.reserve(numHashes);
|
||||
auto start = std::chrono::system_clock::now();
|
||||
auto timeDiff = util::timed([&]() {
|
||||
for (std::size_t i = 0; i < hashes.size(); ++i)
|
||||
{
|
||||
CassandraStatement statement{selectTransaction_};
|
||||
statement.bindNextBytes(hashes[i]);
|
||||
|
||||
for (std::size_t i = 0; i < hashes.size(); ++i)
|
||||
{
|
||||
CassandraStatement statement{selectTransaction_};
|
||||
statement.bindNextBytes(hashes[i]);
|
||||
cbs.push_back(std::make_shared<ReadCallbackData<result_type>>(
|
||||
numOutstanding, handler, [i, &results](auto& result) {
|
||||
if (result.hasResult())
|
||||
results[i] = {
|
||||
result.getBytes(),
|
||||
result.getBytes(),
|
||||
result.getUInt32(),
|
||||
result.getUInt32()};
|
||||
}));
|
||||
|
||||
cbs.push_back(std::make_shared<ReadCallbackData<result_type>>(
|
||||
numOutstanding, handler, [i, &results](auto& result) {
|
||||
if (result.hasResult())
|
||||
results[i] = {
|
||||
result.getBytes(),
|
||||
result.getBytes(),
|
||||
result.getUInt32(),
|
||||
result.getUInt32()};
|
||||
}));
|
||||
executeAsyncRead(statement, processAsyncRead, *cbs[i]);
|
||||
}
|
||||
assert(results.size() == cbs.size());
|
||||
|
||||
executeAsyncRead(statement, processAsyncRead, *cbs[i]);
|
||||
}
|
||||
assert(results.size() == cbs.size());
|
||||
|
||||
// suspend the coroutine until completion handler is called.
|
||||
result.get();
|
||||
numReadRequestsOutstanding_ -= hashes.size();
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
// suspend the coroutine until completion handler is called.
|
||||
result.get();
|
||||
numReadRequestsOutstanding_ -= hashes.size();
|
||||
});
|
||||
for (auto const& cb : cbs)
|
||||
{
|
||||
if (cb->errored)
|
||||
throw DatabaseTimeout();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Fetched " << numHashes << " transactions from Cassandra in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
log_.debug() << "Fetched " << numHashes
|
||||
<< " transactions from Cassandra in " << timeDiff
|
||||
<< " milliseconds";
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -550,9 +575,7 @@ CassandraBackend::fetchAllTransactionHashesInLedger(
|
||||
auto end = std::chrono::system_clock::now();
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__
|
||||
<< " - no rows . ledger = " << std::to_string(ledgerSequence);
|
||||
log_.error() << "No rows. Ledger = " << std::to_string(ledgerSequence);
|
||||
return {};
|
||||
}
|
||||
std::vector<ripple::uint256> hashes;
|
||||
@@ -560,12 +583,12 @@ CassandraBackend::fetchAllTransactionHashesInLedger(
|
||||
{
|
||||
hashes.push_back(result.getUInt256());
|
||||
} while (result.nextRow());
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Fetched " << hashes.size()
|
||||
<< " transaction hashes from Cassandra in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
log_.debug() << "Fetched " << hashes.size()
|
||||
<< " transaction hashes from Cassandra in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
return hashes;
|
||||
}
|
||||
|
||||
@@ -613,9 +636,9 @@ CassandraBackend::fetchNFTTransactions(
|
||||
{
|
||||
statement.bindNextIntTuple(
|
||||
cursor->ledgerSequence, cursor->transactionIndex);
|
||||
BOOST_LOG_TRIVIAL(debug) << " token_id = " << ripple::strHex(tokenID)
|
||||
<< " tuple = " << cursor->ledgerSequence
|
||||
<< " : " << cursor->transactionIndex;
|
||||
log_.debug() << "token_id = " << ripple::strHex(tokenID)
|
||||
<< " tuple = " << cursor->ledgerSequence
|
||||
<< cursor->transactionIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -624,9 +647,8 @@ CassandraBackend::fetchNFTTransactions(
|
||||
forward ? 0 : std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
statement.bindNextIntTuple(placeHolder, placeHolder);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< " token_id = " << ripple::strHex(tokenID) << " idx = " << seq
|
||||
<< " tuple = " << placeHolder;
|
||||
log_.debug() << "token_id = " << ripple::strHex(tokenID)
|
||||
<< " idx = " << seq << " tuple = " << placeHolder;
|
||||
}
|
||||
|
||||
statement.bindNextUInt(limit);
|
||||
@@ -635,19 +657,19 @@ CassandraBackend::fetchNFTTransactions(
|
||||
|
||||
if (!result.hasResult())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " - no rows returned";
|
||||
log_.debug() << "No rows returned";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ripple::uint256> hashes = {};
|
||||
auto numRows = result.numRows();
|
||||
BOOST_LOG_TRIVIAL(info) << "num_rows = " << numRows;
|
||||
log_.info() << "num_rows = " << numRows;
|
||||
do
|
||||
{
|
||||
hashes.push_back(result.getUInt256());
|
||||
if (--numRows == 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " setting cursor";
|
||||
log_.debug() << "Setting cursor";
|
||||
auto const [lgrSeq, txnIdx] = result.getInt64Tuple();
|
||||
cursor = {
|
||||
static_cast<std::uint32_t>(lgrSeq),
|
||||
@@ -662,11 +684,11 @@ CassandraBackend::fetchNFTTransactions(
|
||||
} while (result.nextRow());
|
||||
|
||||
auto txns = fetchTransactions(hashes, yield);
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " txns = " << txns.size();
|
||||
log_.debug() << "Txns = " << txns.size();
|
||||
|
||||
if (txns.size() == limit)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " returning cursor";
|
||||
log_.debug() << "Returning cursor";
|
||||
return {txns, cursor};
|
||||
}
|
||||
|
||||
@@ -698,9 +720,9 @@ CassandraBackend::fetchAccountTransactions(
|
||||
{
|
||||
statement.bindNextIntTuple(
|
||||
cursor->ledgerSequence, cursor->transactionIndex);
|
||||
BOOST_LOG_TRIVIAL(debug) << " account = " << ripple::strHex(account)
|
||||
<< " tuple = " << cursor->ledgerSequence
|
||||
<< " : " << cursor->transactionIndex;
|
||||
log_.debug() << "account = " << ripple::strHex(account)
|
||||
<< " tuple = " << cursor->ledgerSequence
|
||||
<< cursor->transactionIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -709,9 +731,8 @@ CassandraBackend::fetchAccountTransactions(
|
||||
forward ? 0 : std::numeric_limits<std::uint32_t>::max();
|
||||
|
||||
statement.bindNextIntTuple(placeHolder, placeHolder);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< " account = " << ripple::strHex(account) << " idx = " << seq
|
||||
<< " tuple = " << placeHolder;
|
||||
log_.debug() << "account = " << ripple::strHex(account)
|
||||
<< " idx = " << seq << " tuple = " << placeHolder;
|
||||
}
|
||||
statement.bindNextUInt(limit);
|
||||
|
||||
@@ -719,19 +740,19 @@ CassandraBackend::fetchAccountTransactions(
|
||||
|
||||
if (!result.hasResult())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " - no rows returned";
|
||||
log_.debug() << "No rows returned";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<ripple::uint256> hashes = {};
|
||||
auto numRows = result.numRows();
|
||||
BOOST_LOG_TRIVIAL(info) << "num_rows = " << std::to_string(numRows);
|
||||
log_.info() << "num_rows = " << std::to_string(numRows);
|
||||
do
|
||||
{
|
||||
hashes.push_back(result.getUInt256());
|
||||
if (--numRows == 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " setting cursor";
|
||||
log_.debug() << "Setting cursor";
|
||||
auto [lgrSeq, txnIdx] = result.getInt64Tuple();
|
||||
cursor = {
|
||||
static_cast<std::uint32_t>(lgrSeq),
|
||||
@@ -746,11 +767,11 @@ CassandraBackend::fetchAccountTransactions(
|
||||
} while (result.nextRow());
|
||||
|
||||
auto txns = fetchTransactions(hashes, yield);
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << "txns = " << txns.size();
|
||||
log_.debug() << "Txns = " << txns.size();
|
||||
|
||||
if (txns.size() == limit)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " returning cursor";
|
||||
log_.debug() << "Returning cursor";
|
||||
return {txns, cursor};
|
||||
}
|
||||
|
||||
@@ -763,7 +784,7 @@ CassandraBackend::doFetchSuccessorKey(
|
||||
std::uint32_t const ledgerSequence,
|
||||
boost::asio::yield_context& yield) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Fetching from cassandra";
|
||||
log_.trace() << "Fetching from cassandra";
|
||||
CassandraStatement statement{selectSuccessor_};
|
||||
statement.bindNextBytes(key);
|
||||
statement.bindNextInt(ledgerSequence);
|
||||
@@ -772,7 +793,7 @@ CassandraBackend::doFetchSuccessorKey(
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " - no rows";
|
||||
log_.debug() << "No rows";
|
||||
return {};
|
||||
}
|
||||
auto next = result.getUInt256();
|
||||
@@ -787,7 +808,7 @@ CassandraBackend::doFetchLedgerObject(
|
||||
std::uint32_t const sequence,
|
||||
boost::asio::yield_context& yield) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Fetching from cassandra";
|
||||
log_.trace() << "Fetching from cassandra";
|
||||
CassandraStatement statement{selectObject_};
|
||||
statement.bindNextBytes(key);
|
||||
statement.bindNextInt(sequence);
|
||||
@@ -796,7 +817,7 @@ CassandraBackend::doFetchLedgerObject(
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " - no rows";
|
||||
log_.debug() << "No rows";
|
||||
return {};
|
||||
}
|
||||
auto res = result.getBytes();
|
||||
@@ -820,8 +841,7 @@ CassandraBackend::doFetchLedgerObjects(
|
||||
result_type result(handler);
|
||||
|
||||
std::size_t const numKeys = keys.size();
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< "Fetching " << numKeys << " records from Cassandra";
|
||||
log_.trace() << "Fetching " << numKeys << " records from Cassandra";
|
||||
std::atomic_int numOutstanding = numKeys;
|
||||
std::vector<Blob> results{numKeys};
|
||||
std::vector<std::shared_ptr<ReadCallbackData<result_type>>> cbs;
|
||||
@@ -850,8 +870,7 @@ CassandraBackend::doFetchLedgerObjects(
|
||||
throw DatabaseTimeout();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< "Fetched " << numKeys << " records from Cassandra";
|
||||
log_.trace() << "Fetched " << numKeys << " records from Cassandra";
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -870,9 +889,7 @@ CassandraBackend::fetchLedgerDiff(
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__
|
||||
<< " - no rows . ledger = " << std::to_string(ledgerSequence);
|
||||
log_.error() << "No rows. Ledger = " << std::to_string(ledgerSequence);
|
||||
return {};
|
||||
}
|
||||
std::vector<ripple::uint256> keys;
|
||||
@@ -880,11 +897,12 @@ CassandraBackend::fetchLedgerDiff(
|
||||
{
|
||||
keys.push_back(result.getUInt256());
|
||||
} while (result.nextRow());
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Fetched " << keys.size() << " diff hashes from Cassandra in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
log_.debug() << "Fetched " << keys.size()
|
||||
<< " diff hashes from Cassandra in "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
auto objs = fetchLedgerObjects(keys, ledgerSequence, yield);
|
||||
std::vector<LedgerObject> results;
|
||||
std::transform(
|
||||
@@ -951,12 +969,12 @@ CassandraBackend::doOnlineDelete(
|
||||
cv));
|
||||
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << "Got the mutex";
|
||||
log_.trace() << "Got the mutex";
|
||||
cv.wait(lck, [&numOutstanding, concurrentLimit]() {
|
||||
return numOutstanding < concurrentLimit;
|
||||
});
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " fetched a page";
|
||||
log_.debug() << "Fetched a page";
|
||||
cursor = curCursor;
|
||||
if (!cursor)
|
||||
break;
|
||||
@@ -982,11 +1000,11 @@ CassandraBackend::open(bool readOnly)
|
||||
if (open_)
|
||||
{
|
||||
assert(false);
|
||||
BOOST_LOG_TRIVIAL(error) << "database is already open";
|
||||
log_.error() << "Database is already open";
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Opening Cassandra Backend";
|
||||
log_.info() << "Opening Cassandra Backend";
|
||||
|
||||
CassCluster* cluster = cass_cluster_new();
|
||||
if (!cluster)
|
||||
@@ -1002,9 +1020,9 @@ CassandraBackend::open(bool readOnly)
|
||||
if (cass_cluster_set_cloud_secure_connection_bundle(
|
||||
cluster, secureConnectBundle.c_str()) != CASS_OK)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Unable to configure cloud using the "
|
||||
"secure connection bundle: "
|
||||
<< secureConnectBundle;
|
||||
log_.error() << "Unable to configure cloud using the "
|
||||
"secure connection bundle: "
|
||||
<< secureConnectBundle;
|
||||
throw std::runtime_error(
|
||||
"nodestore: Failed to connect using secure connection "
|
||||
"bundle");
|
||||
@@ -1057,7 +1075,7 @@ CassandraBackend::open(bool readOnly)
|
||||
auto username = config_.maybeValue<std::string>("username");
|
||||
if (username)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "user = " << *username;
|
||||
log_.debug() << "user = " << *username;
|
||||
auto password = config_.value<std::string>("password");
|
||||
cass_cluster_set_credentials(
|
||||
cluster, username->c_str(), password.c_str());
|
||||
@@ -1080,10 +1098,11 @@ CassandraBackend::open(bool readOnly)
|
||||
"max_read_requests_outstanding", maxReadRequestsOutstanding);
|
||||
syncInterval_ = config_.valueOr<int>("sync_interval", syncInterval_);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " sync interval is " << syncInterval_
|
||||
<< ". max write requests outstanding is " << maxWriteRequestsOutstanding
|
||||
<< ". max read requests outstanding is " << maxReadRequestsOutstanding;
|
||||
log_.info() << "Sync interval is " << syncInterval_
|
||||
<< ". max write requests outstanding is "
|
||||
<< maxWriteRequestsOutstanding
|
||||
<< ". max read requests outstanding is "
|
||||
<< maxReadRequestsOutstanding;
|
||||
|
||||
cass_cluster_set_request_timeout(cluster, 10000);
|
||||
|
||||
@@ -1098,7 +1117,7 @@ CassandraBackend::open(bool readOnly)
|
||||
ss << "nodestore: Error setting Cassandra max core connections per "
|
||||
"host"
|
||||
<< ", result: " << rc << ", " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
|
||||
@@ -1140,8 +1159,7 @@ CassandraBackend::open(bool readOnly)
|
||||
auto keyspace = config_.valueOr<std::string>("keyspace", "");
|
||||
if (keyspace.empty())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "No keyspace specified. Using keyspace clio";
|
||||
log_.warn() << "No keyspace specified. Using keyspace clio";
|
||||
keyspace = "clio";
|
||||
}
|
||||
|
||||
@@ -1149,14 +1167,13 @@ CassandraBackend::open(bool readOnly)
|
||||
auto tablePrefix = config_.valueOr<std::string>("table_prefix", "");
|
||||
if (tablePrefix.empty())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning) << "Table prefix is empty";
|
||||
log_.warn() << "Table prefix is empty";
|
||||
}
|
||||
|
||||
cass_cluster_set_connect_timeout(cluster, 10000);
|
||||
|
||||
auto ttl = ttl_ * 2;
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " setting ttl to " << std::to_string(ttl);
|
||||
log_.info() << "Setting ttl to " << std::to_string(ttl);
|
||||
|
||||
auto executeSimpleStatement = [this](std::string const& query) {
|
||||
CassStatement* statement = makeStatement(query.c_str(), 0);
|
||||
@@ -1169,7 +1186,7 @@ CassandraBackend::open(bool readOnly)
|
||||
std::stringstream ss;
|
||||
ss << "nodestore: Error executing simple statement: " << rc << ", "
|
||||
<< cass_error_desc(rc) << " - " << query;
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log_.error() << ss.str();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1192,7 +1209,7 @@ CassandraBackend::open(bool readOnly)
|
||||
ss << "nodestore: Error connecting Cassandra session keyspace: "
|
||||
<< rc << ", " << cass_error_desc(rc)
|
||||
<< ", trying to create it ourselves";
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log_.error() << ss.str();
|
||||
// if the keyspace doesn't exist, try to create it
|
||||
session_.reset(cass_session_new());
|
||||
fut = cass_session_connect(session_.get(), cluster);
|
||||
@@ -1203,7 +1220,7 @@ CassandraBackend::open(bool readOnly)
|
||||
std::stringstream ss;
|
||||
ss << "nodestore: Error connecting Cassandra session at all: "
|
||||
<< rc << ", " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log_.error() << ss.str();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1626,6 +1643,6 @@ CassandraBackend::open(bool readOnly)
|
||||
|
||||
open_ = true;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Opened CassandraBackend successfully";
|
||||
log_.info() << "Opened CassandraBackend successfully";
|
||||
}
|
||||
} // namespace Backend
|
||||
|
||||
@@ -1,17 +1,38 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_CASSANDRABACKEND_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_CASSANDRABACKEND_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
#include <cassandra.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <cassandra.h>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -25,6 +46,7 @@ namespace Backend {
|
||||
class CassandraPreparedStatement
|
||||
{
|
||||
private:
|
||||
clio::Logger log_{"Backend"};
|
||||
CassPrepared const* prepared_ = nullptr;
|
||||
|
||||
public:
|
||||
@@ -65,7 +87,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "nodestore: Error preparing statement : " << rc << ", "
|
||||
<< cass_error_desc(rc) << ". query : " << query;
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log_.error() << ss.str();
|
||||
}
|
||||
cass_future_free(prepareFuture);
|
||||
return rc == CASS_OK;
|
||||
@@ -73,7 +95,7 @@ public:
|
||||
|
||||
~CassandraPreparedStatement()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__;
|
||||
log_.trace() << "called";
|
||||
if (prepared_)
|
||||
{
|
||||
cass_prepared_free(prepared_);
|
||||
@@ -86,6 +108,7 @@ class CassandraStatement
|
||||
{
|
||||
CassStatement* statement_ = nullptr;
|
||||
size_t curBindingIndex_ = 0;
|
||||
clio::Logger log_{"Backend"};
|
||||
|
||||
public:
|
||||
CassandraStatement(CassandraPreparedStatement const& prepared)
|
||||
@@ -123,7 +146,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding boolean to statement: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
curBindingIndex_++;
|
||||
@@ -179,7 +202,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding bytes to statement: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
curBindingIndex_++;
|
||||
@@ -191,8 +214,8 @@ public:
|
||||
if (!statement_)
|
||||
throw std::runtime_error(
|
||||
"CassandraStatement::bindNextUInt - statement_ is null");
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< std::to_string(curBindingIndex_) << " " << std::to_string(value);
|
||||
log_.trace() << std::to_string(curBindingIndex_) << " "
|
||||
<< std::to_string(value);
|
||||
CassError rc =
|
||||
cass_statement_bind_int32(statement_, curBindingIndex_, value);
|
||||
if (rc != CASS_OK)
|
||||
@@ -200,7 +223,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding uint to statement: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
curBindingIndex_++;
|
||||
@@ -225,7 +248,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding int to statement: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
curBindingIndex_++;
|
||||
@@ -241,7 +264,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding int to tuple: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
rc = cass_tuple_set_int64(tuple, 1, second);
|
||||
@@ -250,7 +273,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding int to tuple: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
rc = cass_statement_bind_tuple(statement_, curBindingIndex_, tuple);
|
||||
@@ -259,7 +282,7 @@ public:
|
||||
std::stringstream ss;
|
||||
ss << "Error binding tuple to statement: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " : " << ss.str();
|
||||
log_.error() << ss.str();
|
||||
throw std::runtime_error(ss.str());
|
||||
}
|
||||
cass_tuple_free(tuple);
|
||||
@@ -275,6 +298,7 @@ public:
|
||||
|
||||
class CassandraResult
|
||||
{
|
||||
clio::Logger log_{"Backend"};
|
||||
CassResult const* result_ = nullptr;
|
||||
CassRow const* row_ = nullptr;
|
||||
CassIterator* iter_ = nullptr;
|
||||
@@ -365,7 +389,7 @@ public:
|
||||
std::stringstream msg;
|
||||
msg << "CassandraResult::getBytes - error getting value: " << rc
|
||||
<< ", " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << msg.str();
|
||||
log_.error() << msg.str();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
curGetIndex_++;
|
||||
@@ -386,7 +410,7 @@ public:
|
||||
std::stringstream msg;
|
||||
msg << "CassandraResult::getuint256 - error getting value: " << rc
|
||||
<< ", " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << msg.str();
|
||||
log_.error() << msg.str();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
curGetIndex_++;
|
||||
@@ -406,7 +430,7 @@ public:
|
||||
std::stringstream msg;
|
||||
msg << "CassandraResult::getInt64 - error getting value: " << rc
|
||||
<< ", " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << msg.str();
|
||||
log_.error() << msg.str();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
++curGetIndex_;
|
||||
@@ -490,10 +514,9 @@ public:
|
||||
{
|
||||
if (!row_)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - no result";
|
||||
BOOST_LOG_TRIVIAL(error) << msg.str();
|
||||
throw std::runtime_error(msg.str());
|
||||
std::string msg{"No result"};
|
||||
log_.error() << msg;
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
cass_bool_t val;
|
||||
CassError rc =
|
||||
@@ -501,9 +524,8 @@ public:
|
||||
if (rc != CASS_OK)
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - error getting value: " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << msg.str();
|
||||
msg << "Error getting value: " << rc << ", " << cass_error_desc(rc);
|
||||
log_.error() << msg.str();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
++curGetIndex_;
|
||||
@@ -518,6 +540,7 @@ public:
|
||||
cass_iterator_free(iter_);
|
||||
}
|
||||
};
|
||||
|
||||
inline bool
|
||||
isTimeout(CassError rc)
|
||||
{
|
||||
@@ -597,6 +620,7 @@ private:
|
||||
return ret;
|
||||
}
|
||||
|
||||
clio::Logger log_{"Backend"};
|
||||
std::atomic<bool> open_{false};
|
||||
|
||||
std::unique_ptr<CassSession, void (*)(CassSession*)> session_{
|
||||
@@ -751,13 +775,11 @@ public:
|
||||
statement.bindNextInt(ledgerSequence_ - 1);
|
||||
if (!executeSyncUpdate(statement))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " Update failed for ledger "
|
||||
<< std::to_string(ledgerSequence_) << ". Returning";
|
||||
log_.warn() << "Update failed for ledger "
|
||||
<< std::to_string(ledgerSequence_) << ". Returning";
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " Committed ledger "
|
||||
<< std::to_string(ledgerSequence_);
|
||||
log_.info() << "Committed ledger " << std::to_string(ledgerSequence_);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -791,22 +813,20 @@ public:
|
||||
statement.bindNextInt(lastSync_);
|
||||
if (!executeSyncUpdate(statement))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " Update failed for ledger "
|
||||
<< std::to_string(ledgerSequence_) << ". Returning";
|
||||
log_.warn() << "Update failed for ledger "
|
||||
<< std::to_string(ledgerSequence_) << ". Returning";
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " Committed ledger "
|
||||
<< std::to_string(ledgerSequence_);
|
||||
log_.info() << "Committed ledger "
|
||||
<< std::to_string(ledgerSequence_);
|
||||
lastSync_ = ledgerSequence_;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " Skipping commit. sync interval is "
|
||||
<< std::to_string(syncInterval_) << " - last sync is "
|
||||
<< std::to_string(lastSync_) << " - ledger sequence is "
|
||||
<< std::to_string(ledgerSequence_);
|
||||
log_.info() << "Skipping commit. sync interval is "
|
||||
<< std::to_string(syncInterval_) << " - last sync is "
|
||||
<< std::to_string(lastSync_) << " - ledger sequence is "
|
||||
<< std::to_string(ledgerSequence_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -826,12 +846,12 @@ public:
|
||||
std::optional<std::uint32_t>
|
||||
fetchLatestLedgerSequence(boost::asio::yield_context& yield) const override
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__;
|
||||
log_.trace() << "called";
|
||||
CassandraStatement statement{selectLatestLedger_};
|
||||
CassandraResult result = executeAsyncRead(statement, yield);
|
||||
if (!result.hasResult())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
log_.error()
|
||||
<< "CassandraBackend::fetchLatestLedgerSequence - no rows";
|
||||
return {};
|
||||
}
|
||||
@@ -843,13 +863,13 @@ public:
|
||||
std::uint32_t const sequence,
|
||||
boost::asio::yield_context& yield) const override
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__;
|
||||
log_.trace() << "called";
|
||||
CassandraStatement statement{selectLedgerBySeq_};
|
||||
statement.bindNextInt(sequence);
|
||||
CassandraResult result = executeAsyncRead(statement, yield);
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " - no rows";
|
||||
log_.error() << "No rows";
|
||||
return {};
|
||||
}
|
||||
std::vector<unsigned char> header = result.getBytes();
|
||||
@@ -869,7 +889,7 @@ public:
|
||||
|
||||
if (!result.hasResult())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " - no rows returned";
|
||||
log_.debug() << "No rows returned";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -916,7 +936,7 @@ public:
|
||||
std::optional<int64_t>
|
||||
getToken(void const* key, boost::asio::yield_context& yield) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Fetching from cassandra";
|
||||
log_.trace() << "Fetching from cassandra";
|
||||
CassandraStatement statement{getToken_};
|
||||
statement.bindNextBytes(key, 32);
|
||||
|
||||
@@ -924,7 +944,7 @@ public:
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " - no rows";
|
||||
log_.error() << "No rows";
|
||||
return {};
|
||||
}
|
||||
int64_t token = result.getInt64();
|
||||
@@ -939,14 +959,14 @@ public:
|
||||
ripple::uint256 const& hash,
|
||||
boost::asio::yield_context& yield) const override
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__;
|
||||
log_.trace() << "called";
|
||||
CassandraStatement statement{selectTransaction_};
|
||||
statement.bindNextBytes(hash);
|
||||
CassandraResult result = executeAsyncRead(statement, yield);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " - no rows";
|
||||
log_.error() << "No rows";
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
@@ -1036,10 +1056,8 @@ public:
|
||||
std::unique_lock<std::mutex> lck(throttleMutex_);
|
||||
if (!canAddRequest())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Max outstanding requests reached. "
|
||||
<< "Waiting for other requests to finish";
|
||||
log_.debug() << "Max outstanding requests reached. "
|
||||
<< "Waiting for other requests to finish";
|
||||
throttleCv_.wait(lck, [this]() { return canAddRequest(); });
|
||||
}
|
||||
}
|
||||
@@ -1142,7 +1160,7 @@ public:
|
||||
ss << "Cassandra sync write error";
|
||||
ss << ", retrying";
|
||||
ss << ": " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(warning) << ss.str();
|
||||
log_.warn() << ss.str();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
} while (rc != CASS_OK);
|
||||
@@ -1166,7 +1184,7 @@ public:
|
||||
ss << "Cassandra sync update error";
|
||||
ss << ", retrying";
|
||||
ss << ": " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(warning) << ss.str();
|
||||
log_.warn() << ss.str();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
} while (rc != CASS_OK);
|
||||
@@ -1176,7 +1194,7 @@ public:
|
||||
CassRow const* row = cass_result_first_row(res);
|
||||
if (!row)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "executeSyncUpdate - no rows";
|
||||
log_.error() << "executeSyncUpdate - no rows";
|
||||
cass_result_free(res);
|
||||
return false;
|
||||
}
|
||||
@@ -1185,16 +1203,14 @@ public:
|
||||
if (rc != CASS_OK)
|
||||
{
|
||||
cass_result_free(res);
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "executeSyncUpdate - error getting result " << rc << ", "
|
||||
<< cass_error_desc(rc);
|
||||
log_.error() << "executeSyncUpdate - error getting result " << rc
|
||||
<< ", " << cass_error_desc(rc);
|
||||
return false;
|
||||
}
|
||||
cass_result_free(res);
|
||||
if (success != cass_true && timedOut)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " Update failed, but timedOut is true";
|
||||
log_.warn() << "Update failed, but timedOut is true";
|
||||
// if there was a timeout, the update may have succeeded in the
|
||||
// background on the first attempt. To determine if this happened,
|
||||
// we query the range from the db, making sure the range is what
|
||||
@@ -1230,15 +1246,14 @@ public:
|
||||
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Cannot read async cass_future_error_code";
|
||||
log_.error() << "Cannot read async cass_future_error_code";
|
||||
}
|
||||
if (rc != CASS_OK)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Cassandra executeAsyncRead error";
|
||||
ss << ": " << cass_error_desc(rc);
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log_.error() << ss.str();
|
||||
}
|
||||
if (isTimeout(rc))
|
||||
{
|
||||
@@ -1261,4 +1276,3 @@ public:
|
||||
};
|
||||
|
||||
} // namespace Backend
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef CLIO_BACKEND_DBHELPERS_H_INCLUDED
|
||||
#define CLIO_BACKEND_DBHELPERS_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
@@ -181,4 +199,3 @@ uint256ToString(ripple::uint256 const& uint)
|
||||
}
|
||||
|
||||
static constexpr std::uint32_t rippleEpochStart = 946684800;
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/SimpleCache.h>
|
||||
namespace Backend {
|
||||
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef CLIO_SIMPLECACHE_H_INCLUDED
|
||||
#define CLIO_SIMPLECACHE_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/basics/hardened_hash.h>
|
||||
@@ -78,4 +96,3 @@ public:
|
||||
};
|
||||
|
||||
} // namespace Backend
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
#ifndef CLIO_TYPES_H_INCLUDED
|
||||
#define CLIO_TYPES_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <ripple/protocol/AccountID.h>
|
||||
#include <optional>
|
||||
@@ -89,4 +108,3 @@ constexpr ripple::uint256 lastKey{
|
||||
constexpr ripple::uint256 hi192{
|
||||
"0000000000000000000000000000000000000000000000001111111111111111"};
|
||||
} // namespace Backend
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
#include <config/Config.h>
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <config/Config.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <fstream>
|
||||
|
||||
namespace clio {
|
||||
@@ -161,11 +180,10 @@ ConfigReader::open(std::filesystem::path path)
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Could not read configuration file from '"
|
||||
<< path.string() << "': " << e.what();
|
||||
LogService::error() << "Could not read configuration file from '"
|
||||
<< path.string() << "': " << e.what();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning) << "Using empty default configuration";
|
||||
return Config{};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_APP_CONFIG_H_INCLUDED
|
||||
#define RIPPLE_APP_CONFIG_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <config/detail/Helpers.h>
|
||||
|
||||
@@ -344,7 +362,7 @@ private:
|
||||
}
|
||||
else if constexpr (std::is_same_v<Return, double>)
|
||||
{
|
||||
if (not value.is_double())
|
||||
if (not value.is_number())
|
||||
has_error = true;
|
||||
}
|
||||
else if constexpr (
|
||||
@@ -385,5 +403,3 @@ public:
|
||||
};
|
||||
|
||||
} // namespace clio
|
||||
|
||||
#endif // RIPPLE_APP_CONFIG_H_INCLUDED
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_APP_CONFIG_DETAIL_H_INCLUDED
|
||||
#define RIPPLE_APP_CONFIG_DETAIL_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
@@ -144,5 +162,3 @@ typeName<double>()
|
||||
}
|
||||
|
||||
}; // namespace clio::detail
|
||||
|
||||
#endif // RIPPLE_APP_CONFIG_DETAIL_H_INCLUDED
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_ETLHELPERS_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_ETLHELPERS_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/basics/base_uint.h>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
@@ -171,5 +190,3 @@ getMarkers(size_t numMarkers)
|
||||
}
|
||||
return markers;
|
||||
}
|
||||
|
||||
#endif // RIPPLE_APP_REPORTING_ETLHELPERS_H_INCLUDED
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/beast/net/IPEndpoint.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <boost/asio/strand.hpp>
|
||||
@@ -5,18 +24,23 @@
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/json/src.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <etl/ETLSource.h>
|
||||
#include <etl/ProbingETLSource.h>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
void
|
||||
ForwardCache::freshen()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Freshening ForwardCache";
|
||||
log_.trace() << "Freshening ForwardCache";
|
||||
|
||||
auto numOutstanding =
|
||||
std::make_shared<std::atomic_uint>(latestForwarded_.size());
|
||||
@@ -127,15 +151,11 @@ ETLSourceImpl<Derived>::reconnect(boost::beast::error_code ec)
|
||||
if (ec != boost::asio::error::operation_aborted &&
|
||||
ec != boost::asio::error::connection_refused)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " : "
|
||||
<< "error code = " << ec << " - " << toString();
|
||||
log_.error() << "error code = " << ec << " - " << toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " : "
|
||||
<< "error code = " << ec << " - " << toString();
|
||||
log_.warn() << "error code = " << ec << " - " << toString();
|
||||
}
|
||||
|
||||
// exponentially increasing timeouts, with a max of 30 seconds
|
||||
@@ -144,7 +164,7 @@ ETLSourceImpl<Derived>::reconnect(boost::beast::error_code ec)
|
||||
timer_.expires_after(boost::asio::chrono::seconds(waitTime));
|
||||
timer_.async_wait([this](auto ec) {
|
||||
bool startAgain = (ec != boost::asio::error::operation_aborted);
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " async_wait : ec = " << ec;
|
||||
log_.trace() << "async_wait : ec = " << ec;
|
||||
derived().close(startAgain);
|
||||
});
|
||||
}
|
||||
@@ -168,8 +188,8 @@ PlainETLSource::close(bool startAgain)
|
||||
[this, startAgain](auto ec) {
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " async_close : "
|
||||
log_.error()
|
||||
<< " async_close : "
|
||||
<< "error code = " << ec << " - " << toString();
|
||||
}
|
||||
closing_ = false;
|
||||
@@ -213,8 +233,8 @@ SslETLSource::close(bool startAgain)
|
||||
[this, startAgain](auto ec) {
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " async_close : "
|
||||
log_.error()
|
||||
<< " async_close : "
|
||||
<< "error code = " << ec << " - " << toString();
|
||||
}
|
||||
closing_ = false;
|
||||
@@ -246,8 +266,7 @@ ETLSourceImpl<Derived>::onResolve(
|
||||
boost::beast::error_code ec,
|
||||
boost::asio::ip::tcp::resolver::results_type results)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : ec = " << ec << " - " << toString();
|
||||
log_.trace() << "ec = " << ec << " - " << toString();
|
||||
if (ec)
|
||||
{
|
||||
// try again
|
||||
@@ -269,8 +288,7 @@ PlainETLSource::onConnect(
|
||||
boost::beast::error_code ec,
|
||||
boost::asio::ip::tcp::resolver::results_type::endpoint_type endpoint)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : ec = " << ec << " - " << toString();
|
||||
log_.trace() << "ec = " << ec << " - " << toString();
|
||||
if (ec)
|
||||
{
|
||||
// start over
|
||||
@@ -311,8 +329,7 @@ SslETLSource::onConnect(
|
||||
boost::beast::error_code ec,
|
||||
boost::asio::ip::tcp::resolver::results_type::endpoint_type endpoint)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : ec = " << ec << " - " << toString();
|
||||
log_.trace() << "ec = " << ec << " - " << toString();
|
||||
if (ec)
|
||||
{
|
||||
// start over
|
||||
@@ -371,8 +388,7 @@ template <class Derived>
|
||||
void
|
||||
ETLSourceImpl<Derived>::onHandshake(boost::beast::error_code ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : ec = " << ec << " - " << toString();
|
||||
log_.trace() << "ec = " << ec << " - " << toString();
|
||||
if (auto action = hooks_.onConnected(ec);
|
||||
action == ETLSourceHooks::Action::STOP)
|
||||
return;
|
||||
@@ -389,7 +405,7 @@ ETLSourceImpl<Derived>::onHandshake(boost::beast::error_code ec)
|
||||
{"streams",
|
||||
{"ledger", "manifests", "validations", "transactions_proposed"}}};
|
||||
std::string s = boost::json::serialize(jv);
|
||||
BOOST_LOG_TRIVIAL(trace) << "Sending subscribe stream message";
|
||||
log_.trace() << "Sending subscribe stream message";
|
||||
|
||||
derived().ws().set_option(
|
||||
boost::beast::websocket::stream_base::decorator(
|
||||
@@ -415,8 +431,7 @@ ETLSourceImpl<Derived>::onWrite(
|
||||
boost::beast::error_code ec,
|
||||
size_t bytesWritten)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : ec = " << ec << " - " << toString();
|
||||
log_.trace() << "ec = " << ec << " - " << toString();
|
||||
if (ec)
|
||||
{
|
||||
// start over
|
||||
@@ -433,8 +448,7 @@ template <class Derived>
|
||||
void
|
||||
ETLSourceImpl<Derived>::onRead(boost::beast::error_code ec, size_t size)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : ec = " << ec << " - " << toString();
|
||||
log_.trace() << "ec = " << ec << " - " << toString();
|
||||
// if error or error reading message, start over
|
||||
if (ec)
|
||||
{
|
||||
@@ -446,8 +460,7 @@ ETLSourceImpl<Derived>::onRead(boost::beast::error_code ec, size_t size)
|
||||
boost::beast::flat_buffer buffer;
|
||||
swap(readBuffer_, buffer);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : calling async_read - " << toString();
|
||||
log_.trace() << "calling async_read - " << toString();
|
||||
derived().ws().async_read(
|
||||
readBuffer_, [this](auto ec, size_t size) { onRead(ec, size); });
|
||||
}
|
||||
@@ -457,7 +470,7 @@ template <class Derived>
|
||||
bool
|
||||
ETLSourceImpl<Derived>::handleMessage()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " : " << toString();
|
||||
log_.trace() << toString();
|
||||
|
||||
setLastMsgTime();
|
||||
connected_ = true;
|
||||
@@ -466,9 +479,9 @@ ETLSourceImpl<Derived>::handleMessage()
|
||||
std::string msg{
|
||||
static_cast<char const*>(readBuffer_.data().data()),
|
||||
readBuffer_.size()};
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << msg;
|
||||
log_.trace() << msg;
|
||||
boost::json::value raw = boost::json::parse(msg);
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " parsed";
|
||||
log_.trace() << "parsed";
|
||||
boost::json::object response = raw.as_object();
|
||||
|
||||
uint32_t ledgerIndex = 0;
|
||||
@@ -487,20 +500,16 @@ ETLSourceImpl<Derived>::handleMessage()
|
||||
setValidatedRange(
|
||||
{validatedLedgers.c_str(), validatedLedgers.size()});
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Received a message on ledger "
|
||||
<< " subscription stream. Message : " << response << " - "
|
||||
<< toString();
|
||||
log_.info() << "Received a message on ledger "
|
||||
<< " subscription stream. Message : " << response
|
||||
<< " - " << toString();
|
||||
}
|
||||
else if (
|
||||
response.contains("type") && response["type"] == "ledgerClosed")
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Received a message on ledger "
|
||||
<< " subscription stream. Message : " << response << " - "
|
||||
<< toString();
|
||||
log_.info() << "Received a message on ledger "
|
||||
<< " subscription stream. Message : " << response
|
||||
<< " - " << toString();
|
||||
if (response.contains("ledger_index"))
|
||||
{
|
||||
ledgerIndex = response["ledger_index"].as_int64();
|
||||
@@ -539,23 +548,23 @@ ETLSourceImpl<Derived>::handleMessage()
|
||||
|
||||
if (ledgerIndex != 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : "
|
||||
<< "Pushing ledger sequence = " << ledgerIndex << " - "
|
||||
<< toString();
|
||||
log_.trace() << "Pushing ledger sequence = " << ledgerIndex << " - "
|
||||
<< toString();
|
||||
networkValidatedLedgers_->push(ledgerIndex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Exception in handleMessage : " << e.what();
|
||||
log_.error() << "Exception in handleMessage : " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncCallData
|
||||
{
|
||||
clio::Logger log_{"ETL"};
|
||||
|
||||
std::unique_ptr<org::xrpl::rpc::v1::GetLedgerDataResponse> cur_;
|
||||
std::unique_ptr<org::xrpl::rpc::v1::GetLedgerDataResponse> next_;
|
||||
|
||||
@@ -585,11 +594,11 @@ public:
|
||||
|
||||
unsigned char prefix = marker.data()[0];
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Setting up AsyncCallData. marker = " << ripple::strHex(marker)
|
||||
<< " . prefix = " << ripple::strHex(std::string(1, prefix))
|
||||
<< " . nextPrefix_ = "
|
||||
<< ripple::strHex(std::string(1, nextPrefix_));
|
||||
log_.debug() << "Setting up AsyncCallData. marker = "
|
||||
<< ripple::strHex(marker)
|
||||
<< " . prefix = " << ripple::strHex(std::string(1, prefix))
|
||||
<< " . nextPrefix_ = "
|
||||
<< ripple::strHex(std::string(1, nextPrefix_));
|
||||
|
||||
assert(nextPrefix_ > prefix || nextPrefix_ == 0x00);
|
||||
|
||||
@@ -609,26 +618,24 @@ public:
|
||||
bool abort,
|
||||
bool cacheOnly = false)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing response. "
|
||||
<< "Marker prefix = " << getMarkerPrefix();
|
||||
log_.trace() << "Processing response. "
|
||||
<< "Marker prefix = " << getMarkerPrefix();
|
||||
if (abort)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "AsyncCallData aborted";
|
||||
log_.error() << "AsyncCallData aborted";
|
||||
return CallStatus::ERRORED;
|
||||
}
|
||||
if (!status_.ok())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "AsyncCallData status_ not ok: "
|
||||
<< " code = " << status_.error_code()
|
||||
<< " message = " << status_.error_message();
|
||||
log_.error() << "AsyncCallData status_ not ok: "
|
||||
<< " code = " << status_.error_code()
|
||||
<< " message = " << status_.error_message();
|
||||
return CallStatus::ERRORED;
|
||||
}
|
||||
if (!next_->is_unlimited())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "AsyncCallData is_unlimited is false. Make sure "
|
||||
"secure_gateway is set correctly at the ETL source";
|
||||
log_.warn() << "AsyncCallData is_unlimited is false. Make sure "
|
||||
"secure_gateway is set correctly at the ETL source";
|
||||
}
|
||||
|
||||
std::swap(cur_, next_);
|
||||
@@ -651,7 +658,7 @@ public:
|
||||
call(stub, cq);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Writing objects";
|
||||
log_.trace() << "Writing objects";
|
||||
std::vector<Backend::LedgerObject> cacheUpdates;
|
||||
cacheUpdates.reserve(cur_->ledger_objects().objects_size());
|
||||
for (int i = 0; i < cur_->ledger_objects().objects_size(); ++i)
|
||||
@@ -681,7 +688,7 @@ public:
|
||||
}
|
||||
backend.cache().update(
|
||||
cacheUpdates, request_.ledger().sequence(), cacheOnly);
|
||||
BOOST_LOG_TRIVIAL(trace) << "Wrote objects";
|
||||
log_.trace() << "Wrote objects";
|
||||
|
||||
return more ? CallStatus::MORE : CallStatus::DONE;
|
||||
}
|
||||
@@ -745,8 +752,8 @@ ETLSourceImpl<Derived>::loadInitialLedger(
|
||||
calls.emplace_back(sequence, markers[i], nextMarker);
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Starting data download for ledger " << sequence
|
||||
<< ". Using source = " << toString();
|
||||
log_.debug() << "Starting data download for ledger " << sequence
|
||||
<< ". Using source = " << toString();
|
||||
|
||||
for (auto& c : calls)
|
||||
c.call(stub_, cq);
|
||||
@@ -764,21 +771,19 @@ ETLSourceImpl<Derived>::loadInitialLedger(
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "loadInitialLedger - ok is false";
|
||||
log_.error() << "loadInitialLedger - ok is false";
|
||||
return false;
|
||||
// handle cancelled
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< "Marker prefix = " << ptr->getMarkerPrefix();
|
||||
log_.trace() << "Marker prefix = " << ptr->getMarkerPrefix();
|
||||
auto result = ptr->process(stub_, cq, *backend_, abort, cacheOnly);
|
||||
if (result != AsyncCallData::CallStatus::MORE)
|
||||
{
|
||||
numFinished++;
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Finished a marker. "
|
||||
<< "Current number of finished = " << numFinished;
|
||||
log_.debug() << "Finished a marker. "
|
||||
<< "Current number of finished = " << numFinished;
|
||||
std::string lastKey = ptr->getLastKey();
|
||||
if (lastKey.size())
|
||||
edgeKeys.push_back(ptr->getLastKey());
|
||||
@@ -789,89 +794,84 @@ ETLSourceImpl<Derived>::loadInitialLedger(
|
||||
}
|
||||
if (backend_->cache().size() > progress)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Downloaded " << backend_->cache().size()
|
||||
<< " records from rippled";
|
||||
log_.info() << "Downloaded " << backend_->cache().size()
|
||||
<< " records from rippled";
|
||||
progress += incr;
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " - finished loadInitialLedger. cache size = "
|
||||
<< backend_->cache().size();
|
||||
log_.info() << "Finished loadInitialLedger. cache size = "
|
||||
<< backend_->cache().size();
|
||||
size_t numWrites = 0;
|
||||
if (!abort)
|
||||
{
|
||||
backend_->cache().setFull();
|
||||
if (!cacheOnly)
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
for (auto& key : edgeKeys)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__
|
||||
<< " writing edge key = " << ripple::strHex(key);
|
||||
auto succ = backend_->cache().getSuccessor(
|
||||
*ripple::uint256::fromVoidChecked(key), sequence);
|
||||
if (succ)
|
||||
backend_->writeSuccessor(
|
||||
std::move(key), sequence, uint256ToString(succ->key));
|
||||
}
|
||||
ripple::uint256 prev = Backend::firstKey;
|
||||
while (auto cur = backend_->cache().getSuccessor(prev, sequence))
|
||||
{
|
||||
assert(cur);
|
||||
if (prev == Backend::firstKey)
|
||||
auto seconds = util::timed<std::chrono::seconds>([&]() {
|
||||
for (auto& key : edgeKeys)
|
||||
{
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(prev),
|
||||
sequence,
|
||||
uint256ToString(cur->key));
|
||||
log_.debug()
|
||||
<< "Writing edge key = " << ripple::strHex(key);
|
||||
auto succ = backend_->cache().getSuccessor(
|
||||
*ripple::uint256::fromVoidChecked(key), sequence);
|
||||
if (succ)
|
||||
backend_->writeSuccessor(
|
||||
std::move(key),
|
||||
sequence,
|
||||
uint256ToString(succ->key));
|
||||
}
|
||||
|
||||
if (isBookDir(cur->key, cur->blob))
|
||||
ripple::uint256 prev = Backend::firstKey;
|
||||
while (auto cur =
|
||||
backend_->cache().getSuccessor(prev, sequence))
|
||||
{
|
||||
auto base = getBookBase(cur->key);
|
||||
// make sure the base is not an actual object
|
||||
if (!backend_->cache().get(cur->key, sequence))
|
||||
assert(cur);
|
||||
if (prev == Backend::firstKey)
|
||||
{
|
||||
auto succ =
|
||||
backend_->cache().getSuccessor(base, sequence);
|
||||
assert(succ);
|
||||
if (succ->key == cur->key)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " Writing book successor = "
|
||||
<< ripple::strHex(base) << " - "
|
||||
<< ripple::strHex(cur->key);
|
||||
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(base),
|
||||
sequence,
|
||||
uint256ToString(cur->key));
|
||||
}
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(prev),
|
||||
sequence,
|
||||
uint256ToString(cur->key));
|
||||
}
|
||||
++numWrites;
|
||||
|
||||
if (isBookDir(cur->key, cur->blob))
|
||||
{
|
||||
auto base = getBookBase(cur->key);
|
||||
// make sure the base is not an actual object
|
||||
if (!backend_->cache().get(cur->key, sequence))
|
||||
{
|
||||
auto succ =
|
||||
backend_->cache().getSuccessor(base, sequence);
|
||||
assert(succ);
|
||||
if (succ->key == cur->key)
|
||||
{
|
||||
log_.debug() << "Writing book successor = "
|
||||
<< ripple::strHex(base) << " - "
|
||||
<< ripple::strHex(cur->key);
|
||||
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(base),
|
||||
sequence,
|
||||
uint256ToString(cur->key));
|
||||
}
|
||||
}
|
||||
++numWrites;
|
||||
}
|
||||
prev = std::move(cur->key);
|
||||
if (numWrites % 100000 == 0 && numWrites != 0)
|
||||
log_.info()
|
||||
<< "Wrote " << numWrites << " book successors";
|
||||
}
|
||||
prev = std::move(cur->key);
|
||||
if (numWrites % 100000 == 0 && numWrites != 0)
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " Wrote "
|
||||
<< numWrites << " book successors";
|
||||
}
|
||||
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(prev),
|
||||
sequence,
|
||||
uint256ToString(Backend::lastKey));
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(prev),
|
||||
sequence,
|
||||
uint256ToString(Backend::lastKey));
|
||||
|
||||
++numWrites;
|
||||
auto end = std::chrono::system_clock::now();
|
||||
auto seconds =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(end - start)
|
||||
.count();
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__
|
||||
<< " - Looping through cache and submitting all writes took "
|
||||
++numWrites;
|
||||
});
|
||||
log_.info()
|
||||
<< "Looping through cache and submitting all writes took "
|
||||
<< seconds
|
||||
<< " seconds. numWrites = " << std::to_string(numWrites);
|
||||
}
|
||||
@@ -902,11 +902,10 @@ ETLSourceImpl<Derived>::fetchLedger(
|
||||
grpc::Status status = stub_->GetLedger(&context, request, &response);
|
||||
if (status.ok() && !response.is_unlimited())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "ETLSourceImpl::fetchLedger - is_unlimited is "
|
||||
"false. Make sure secure_gateway is set "
|
||||
"correctly on the ETL source. source = "
|
||||
<< toString() << " status = " << status.error_message();
|
||||
log_.warn() << "ETLSourceImpl::fetchLedger - is_unlimited is "
|
||||
"false. Make sure secure_gateway is set "
|
||||
"correctly on the ETL source. source = "
|
||||
<< toString() << " status = " << status.error_message();
|
||||
}
|
||||
return {status, std::move(response)};
|
||||
}
|
||||
@@ -951,8 +950,7 @@ ETLLoadBalancer::ETLLoadBalancer(
|
||||
entry, ioContext, backend, subscriptions, nwvl, *this);
|
||||
|
||||
sources_.push_back(std::move(source));
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " : added etl source - "
|
||||
<< sources_.back()->toString();
|
||||
log_.info() << "Added etl source - " << sources_.back()->toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -965,9 +963,9 @@ ETLLoadBalancer::loadInitialLedger(uint32_t sequence, bool cacheOnly)
|
||||
source->loadInitialLedger(sequence, downloadRanges_, cacheOnly);
|
||||
if (!res)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Failed to download initial ledger."
|
||||
<< " Sequence = " << sequence
|
||||
<< " source = " << source->toString();
|
||||
log_.error() << "Failed to download initial ledger."
|
||||
<< " Sequence = " << sequence
|
||||
<< " source = " << source->toString();
|
||||
}
|
||||
return res;
|
||||
},
|
||||
@@ -982,26 +980,24 @@ ETLLoadBalancer::fetchLedger(
|
||||
{
|
||||
org::xrpl::rpc::v1::GetLedgerResponse response;
|
||||
bool success = execute(
|
||||
[&response, ledgerSequence, getObjects, getObjectNeighbors](
|
||||
[&response, ledgerSequence, getObjects, getObjectNeighbors, log = log_](
|
||||
auto& source) {
|
||||
auto [status, data] = source->fetchLedger(
|
||||
ledgerSequence, getObjects, getObjectNeighbors);
|
||||
response = std::move(data);
|
||||
if (status.ok() && response.validated())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Successfully fetched ledger = " << ledgerSequence
|
||||
<< " from source = " << source->toString();
|
||||
log.info() << "Successfully fetched ledger = " << ledgerSequence
|
||||
<< " from source = " << source->toString();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "Error getting ledger = " << ledgerSequence
|
||||
<< " Reply : " << response.DebugString()
|
||||
<< " error_code : " << status.error_code()
|
||||
<< " error_msg : " << status.error_message()
|
||||
<< " source = " << source->toString();
|
||||
log.warn() << "Error getting ledger = " << ledgerSequence
|
||||
<< ", Reply: " << response.DebugString()
|
||||
<< ", error_code: " << status.error_code()
|
||||
<< ", error_msg: " << status.error_message()
|
||||
<< ", source = " << source->toString();
|
||||
return false;
|
||||
}
|
||||
},
|
||||
@@ -1042,7 +1038,7 @@ ETLSourceImpl<Derived>::forwardToRippled(
|
||||
{
|
||||
if (auto resp = forwardCache_.get(request); resp)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "request hit forwardCache";
|
||||
log_.debug() << "request hit forwardCache";
|
||||
return resp;
|
||||
}
|
||||
|
||||
@@ -1056,14 +1052,13 @@ ETLSourceImpl<Derived>::requestFromRippled(
|
||||
std::string const& clientIp,
|
||||
boost::asio::yield_context& yield) const
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Attempting to forward request to tx. "
|
||||
<< "request = " << boost::json::serialize(request);
|
||||
log_.trace() << "Attempting to forward request to tx. "
|
||||
<< "request = " << boost::json::serialize(request);
|
||||
|
||||
boost::json::object response;
|
||||
if (!connected_)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Attempted to proxy but failed to connect to tx";
|
||||
log_.error() << "Attempted to proxy but failed to connect to tx";
|
||||
return {};
|
||||
}
|
||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||
@@ -1077,7 +1072,7 @@ ETLSourceImpl<Derived>::requestFromRippled(
|
||||
// These objects perform our I/O
|
||||
tcp::resolver resolver{ioc_};
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Creating websocket";
|
||||
log_.trace() << "Creating websocket";
|
||||
auto ws = std::make_unique<websocket::stream<beast::tcp_stream>>(ioc_);
|
||||
|
||||
// Look up the domain name
|
||||
@@ -1087,7 +1082,7 @@ ETLSourceImpl<Derived>::requestFromRippled(
|
||||
|
||||
ws->next_layer().expires_after(std::chrono::seconds(3));
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Connecting websocket";
|
||||
log_.trace() << "Connecting websocket";
|
||||
// Make the connection on the IP address we get from a lookup
|
||||
ws->next_layer().async_connect(results, yield[ec]);
|
||||
if (ec)
|
||||
@@ -1106,15 +1101,15 @@ ETLSourceImpl<Derived>::requestFromRippled(
|
||||
" websocket-client-coro");
|
||||
req.set(http::field::forwarded, "for=" + clientIp);
|
||||
}));
|
||||
BOOST_LOG_TRIVIAL(trace) << "client ip: " << clientIp;
|
||||
log_.trace() << "client ip: " << clientIp;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Performing websocket handshake";
|
||||
log_.trace() << "Performing websocket handshake";
|
||||
// Perform the websocket handshake
|
||||
ws->async_handshake(ip_, "/", yield[ec]);
|
||||
if (ec)
|
||||
return {};
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Sending request";
|
||||
log_.trace() << "Sending request";
|
||||
// Send the message
|
||||
ws->async_write(
|
||||
net::buffer(boost::json::serialize(request)), yield[ec]);
|
||||
@@ -1132,11 +1127,11 @@ ETLSourceImpl<Derived>::requestFromRippled(
|
||||
|
||||
if (!parsed.is_object())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Error parsing response: " << std::string{begin, end};
|
||||
log_.error() << "Error parsing response: "
|
||||
<< std::string{begin, end};
|
||||
return {};
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(trace) << "Successfully forward request";
|
||||
log_.trace() << "Successfully forward request";
|
||||
|
||||
response = parsed.as_object();
|
||||
|
||||
@@ -1145,7 +1140,7 @@ ETLSourceImpl<Derived>::requestFromRippled(
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "Encountered exception : " << e.what();
|
||||
log_.error() << "Encountered exception : " << e.what();
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@@ -1162,47 +1157,38 @@ ETLLoadBalancer::execute(Func f, uint32_t ledgerSequence)
|
||||
{
|
||||
auto& source = sources_[sourceIdx];
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Attempting to execute func. ledger sequence = "
|
||||
<< ledgerSequence << " - source = " << source->toString();
|
||||
log_.debug() << "Attempting to execute func. ledger sequence = "
|
||||
<< ledgerSequence << " - source = " << source->toString();
|
||||
if (source->hasLedger(ledgerSequence) || true)
|
||||
{
|
||||
bool res = f(source);
|
||||
if (res)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Successfully executed func at source = "
|
||||
<< source->toString()
|
||||
<< " - ledger sequence = " << ledgerSequence;
|
||||
log_.debug() << "Successfully executed func at source = "
|
||||
<< source->toString()
|
||||
<< " - ledger sequence = " << ledgerSequence;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " : "
|
||||
<< "Failed to execute func at source = "
|
||||
<< source->toString()
|
||||
<< " - ledger sequence = " << ledgerSequence;
|
||||
log_.warn() << "Failed to execute func at source = "
|
||||
<< source->toString()
|
||||
<< " - ledger sequence = " << ledgerSequence;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " : "
|
||||
<< "Ledger not present at source = " << source->toString()
|
||||
<< " - ledger sequence = " << ledgerSequence;
|
||||
log_.warn() << "Ledger not present at source = "
|
||||
<< source->toString()
|
||||
<< " - ledger sequence = " << ledgerSequence;
|
||||
}
|
||||
sourceIdx = (sourceIdx + 1) % sources_.size();
|
||||
numAttempts++;
|
||||
if (numAttempts % sources_.size() == 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " : "
|
||||
<< "Error executing function "
|
||||
<< " - ledger sequence = " << ledgerSequence
|
||||
<< " - Tried all sources. Sleeping and trying again";
|
||||
log_.error() << "Error executing function "
|
||||
<< " - ledger sequence = " << ledgerSequence
|
||||
<< " - Tried all sources. Sleeping and trying again";
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_ETLSOURCE_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_ETLSOURCE_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <config/Config.h>
|
||||
#include <etl/ETLHelpers.h>
|
||||
#include <log/Logger.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
|
||||
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
|
||||
#include <grpcpp/grpcpp.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
@@ -8,14 +35,6 @@
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <config/Config.h>
|
||||
#include <etl/ETLHelpers.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
|
||||
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
|
||||
#include <grpcpp/grpcpp.h>
|
||||
|
||||
class ETLLoadBalancer;
|
||||
class ETLSource;
|
||||
class ProbingETLSource;
|
||||
@@ -32,6 +51,7 @@ class ForwardCache
|
||||
{
|
||||
using response_type = std::optional<boost::json::object>;
|
||||
|
||||
clio::Logger log_{"ETL"};
|
||||
mutable std::atomic_bool stopping_ = false;
|
||||
mutable std::shared_mutex mtx_;
|
||||
std::unordered_map<std::string, response_type> latestForwarded_;
|
||||
@@ -127,6 +147,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
clio::Logger log_{"ETL"};
|
||||
|
||||
private:
|
||||
friend ForwardCache;
|
||||
friend ProbingETLSource;
|
||||
@@ -161,7 +184,7 @@ class ETLSourceImpl : public ETLSource
|
||||
|
||||
std::vector<std::pair<uint32_t, uint32_t>> validatedLedgers_;
|
||||
|
||||
std::string validatedLedgersRaw_;
|
||||
std::string validatedLedgersRaw_{"N/A"};
|
||||
|
||||
std::shared_ptr<NetworkValidatedLedgers> networkValidatedLedgers_;
|
||||
|
||||
@@ -218,7 +241,7 @@ protected:
|
||||
void
|
||||
run() override
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " : " << toString();
|
||||
log_.trace() << toString();
|
||||
|
||||
auto const host = ip_;
|
||||
auto const port = wsPort_;
|
||||
@@ -292,14 +315,12 @@ public:
|
||||
stub_ = org::xrpl::rpc::v1::XRPLedgerAPIService::NewStub(
|
||||
grpc::CreateCustomChannel(
|
||||
ss.str(), grpc::InsecureChannelCredentials(), chArgs));
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Made stub for remote = " << toString();
|
||||
log_.debug() << "Made stub for remote = " << toString();
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Exception while creating stub = " << e.what()
|
||||
<< " . Remote = " << toString();
|
||||
log_.debug() << "Exception while creating stub = " << e.what()
|
||||
<< " . Remote = " << toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,7 +392,6 @@ public:
|
||||
getValidatedRange() const
|
||||
{
|
||||
std::lock_guard lck(mtx_);
|
||||
|
||||
return validatedLedgersRaw_;
|
||||
}
|
||||
|
||||
@@ -389,9 +409,8 @@ public:
|
||||
std::string
|
||||
toString() const override
|
||||
{
|
||||
return "{ validated_ledger : " + getValidatedRange() +
|
||||
" , ip : " + ip_ + " , web socket port : " + wsPort_ +
|
||||
", grpc port : " + grpcPort_ + " }";
|
||||
return "{validated_ledger: " + getValidatedRange() + ", ip: " + ip_ +
|
||||
", web socket port: " + wsPort_ + ", grpc port: " + grpcPort_ + "}";
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
@@ -592,8 +611,8 @@ public:
|
||||
class ETLLoadBalancer
|
||||
{
|
||||
private:
|
||||
clio::Logger log_{"ETL"};
|
||||
std::vector<std::unique_ptr<ETLSource>> sources_;
|
||||
|
||||
std::uint32_t downloadRanges_ = 16;
|
||||
|
||||
public:
|
||||
@@ -703,5 +722,3 @@ private:
|
||||
bool
|
||||
execute(Func f, uint32_t ledgerSequence);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||
#include <ripple/protocol/STBase.h>
|
||||
#include <ripple/protocol/STTx.h>
|
||||
@@ -107,8 +126,7 @@ getNFTokenMintData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
|
||||
NFTsData(tokenIDResult.front(), *owner, txMeta, false)};
|
||||
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - unexpected NFTokenMint data in tx "
|
||||
<< sttx.getTransactionID();
|
||||
msg << " - unexpected NFTokenMint data in tx " << sttx.getTransactionID();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
|
||||
@@ -173,7 +191,7 @@ getNFTokenBurnData(ripple::TxMeta const& txMeta, ripple::STTx const& sttx)
|
||||
}
|
||||
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - could not determine owner at burntime for tx "
|
||||
msg << " - could not determine owner at burntime for tx "
|
||||
<< sttx.getTransactionID();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
@@ -198,7 +216,7 @@ getNFTokenAcceptOfferData(
|
||||
if (affectedBuyOffer == txMeta.getNodes().end())
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - unexpected NFTokenAcceptOffer data in tx "
|
||||
msg << " - unexpected NFTokenAcceptOffer data in tx "
|
||||
<< sttx.getTransactionID();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
@@ -228,7 +246,7 @@ getNFTokenAcceptOfferData(
|
||||
if (affectedSellOffer == txMeta.getNodes().end())
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - unexpected NFTokenAcceptOffer data in tx "
|
||||
msg << " - unexpected NFTokenAcceptOffer data in tx "
|
||||
<< sttx.getTransactionID();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
@@ -278,7 +296,7 @@ getNFTokenAcceptOfferData(
|
||||
}
|
||||
|
||||
std::stringstream msg;
|
||||
msg << __func__ << " - unexpected NFTokenAcceptOffer data in tx "
|
||||
msg << " - unexpected NFTokenAcceptOffer data in tx "
|
||||
<< sttx.getTransactionID();
|
||||
throw std::runtime_error(msg.str());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,26 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <etl/ProbingETLSource.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
ProbingETLSource::ProbingETLSource(
|
||||
clio::Config const& config,
|
||||
@@ -85,7 +107,8 @@ std::string
|
||||
ProbingETLSource::toString() const
|
||||
{
|
||||
if (!currentSrc_)
|
||||
return "{ probing }";
|
||||
return "{probing... ws: " + plainSrc_->toString() +
|
||||
", wss: " + sslSrc_->toString() + "}";
|
||||
return currentSrc_->toString();
|
||||
}
|
||||
|
||||
@@ -148,9 +171,8 @@ ProbingETLSource::make_SSLHooks() noexcept
|
||||
{
|
||||
plainSrc_->pause();
|
||||
currentSrc_ = sslSrc_;
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Selected WSS as the main source: "
|
||||
<< currentSrc_->toString();
|
||||
log_.info() << "Selected WSS as the main source: "
|
||||
<< currentSrc_->toString();
|
||||
}
|
||||
return ETLSourceHooks::Action::PROCEED;
|
||||
},
|
||||
@@ -179,9 +201,8 @@ ProbingETLSource::make_PlainHooks() noexcept
|
||||
{
|
||||
sslSrc_->pause();
|
||||
currentSrc_ = plainSrc_;
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Selected Plain WS as the main source: "
|
||||
<< currentSrc_->toString();
|
||||
log_.info() << "Selected Plain WS as the main source: "
|
||||
<< currentSrc_->toString();
|
||||
}
|
||||
return ETLSourceHooks::Action::PROCEED;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_PROBINGETLSOURCE_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_PROBINGETLSOURCE_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
@@ -11,6 +29,7 @@
|
||||
|
||||
#include <config/Config.h>
|
||||
#include <etl/ETLSource.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
/// This ETLSource implementation attempts to connect over both secure websocket
|
||||
/// and plain websocket. First to connect pauses the other and the probing is
|
||||
@@ -18,6 +37,8 @@
|
||||
/// connection the probing is kickstarted again.
|
||||
class ProbingETLSource : public ETLSource
|
||||
{
|
||||
clio::Logger log_{"ETL"};
|
||||
|
||||
std::mutex mtx_;
|
||||
boost::asio::ssl::context sslCtx_;
|
||||
std::shared_ptr<ETLSource> sslSrc_;
|
||||
@@ -89,5 +110,3 @@ private:
|
||||
ETLSourceHooks
|
||||
make_PlainHooks() noexcept;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,20 +1,45 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/beast/core/CurrentThreadName.h>
|
||||
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <log/Logger.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
#include <ripple/beast/core/CurrentThreadName.h>
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <thread>
|
||||
#include <variant>
|
||||
|
||||
namespace detail {
|
||||
using namespace clio;
|
||||
|
||||
namespace clio::detail {
|
||||
/// Convenience function for printing out basic ledger info
|
||||
std::string
|
||||
toString(ripple::LedgerInfo const& info)
|
||||
@@ -26,7 +51,7 @@ toString(ripple::LedgerInfo const& info)
|
||||
<< " ParentHash : " << strHex(info.parentHash) << " }";
|
||||
return ss.str();
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace clio::detail
|
||||
|
||||
FormattedTransactionsData
|
||||
ReportingETL::insertTransactions(
|
||||
@@ -43,9 +68,7 @@ ReportingETL::insertTransactions(
|
||||
ripple::SerialIter it{raw->data(), raw->size()};
|
||||
ripple::STTx sttx{it};
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : "
|
||||
<< "Inserting transaction = " << sttx.getTransactionID();
|
||||
log_.trace() << "Inserting transaction = " << sttx.getTransactionID();
|
||||
|
||||
ripple::TxMeta txMeta{
|
||||
sttx.getTransactionID(), ledger.seq, txn.metadata_blob()};
|
||||
@@ -97,8 +120,7 @@ ReportingETL::loadInitialLedger(uint32_t startingSequence)
|
||||
auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||
if (rng)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(fatal) << __func__ << " : "
|
||||
<< "Database is not empty";
|
||||
log_.fatal() << "Database is not empty";
|
||||
assert(false);
|
||||
return {};
|
||||
}
|
||||
@@ -114,56 +136,52 @@ ReportingETL::loadInitialLedger(uint32_t startingSequence)
|
||||
ripple::LedgerInfo lgrInfo =
|
||||
deserializeHeader(ripple::makeSlice(ledgerData->ledger_header()));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Deserialized ledger header. " << detail::toString(lgrInfo);
|
||||
log_.debug() << "Deserialized ledger header. " << detail::toString(lgrInfo);
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
auto timeDiff = util::timed<std::chrono::duration<double>>([&]() {
|
||||
backend_->startWrites();
|
||||
|
||||
backend_->startWrites();
|
||||
log_.debug() << "Started writes";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " started writes";
|
||||
backend_->writeLedger(
|
||||
lgrInfo, std::move(*ledgerData->mutable_ledger_header()));
|
||||
|
||||
backend_->writeLedger(
|
||||
lgrInfo, std::move(*ledgerData->mutable_ledger_header()));
|
||||
log_.debug() << "Wrote ledger";
|
||||
FormattedTransactionsData insertTxResult =
|
||||
insertTransactions(lgrInfo, *ledgerData);
|
||||
log_.debug() << "Inserted txns";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " wrote ledger";
|
||||
FormattedTransactionsData insertTxResult =
|
||||
insertTransactions(lgrInfo, *ledgerData);
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " inserted txns";
|
||||
// download the full account state map. This function downloads full
|
||||
// ledger data and pushes the downloaded data into the writeQueue.
|
||||
// asyncWriter consumes from the queue and inserts the data into the
|
||||
// Ledger object. Once the below call returns, all data has been pushed
|
||||
// into the queue
|
||||
loadBalancer_->loadInitialLedger(startingSequence);
|
||||
|
||||
// download the full account state map. This function downloads full ledger
|
||||
// data and pushes the downloaded data into the writeQueue. asyncWriter
|
||||
// consumes from the queue and inserts the data into the Ledger object.
|
||||
// Once the below call returns, all data has been pushed into the queue
|
||||
loadBalancer_->loadInitialLedger(startingSequence);
|
||||
log_.debug() << "Loaded initial ledger";
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " loaded initial ledger";
|
||||
|
||||
if (!stopping_)
|
||||
{
|
||||
backend_->writeAccountTransactions(
|
||||
std::move(insertTxResult.accountTxData));
|
||||
backend_->writeNFTs(std::move(insertTxResult.nfTokensData));
|
||||
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData));
|
||||
}
|
||||
backend_->finishWrites(startingSequence);
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
BOOST_LOG_TRIVIAL(debug) << "Time to download and store ledger = "
|
||||
<< ((end - start).count()) / 1000000000.0;
|
||||
if (!stopping_)
|
||||
{
|
||||
backend_->writeAccountTransactions(
|
||||
std::move(insertTxResult.accountTxData));
|
||||
backend_->writeNFTs(std::move(insertTxResult.nfTokensData));
|
||||
backend_->writeNFTTransactions(
|
||||
std::move(insertTxResult.nfTokenTxData));
|
||||
}
|
||||
backend_->finishWrites(startingSequence);
|
||||
});
|
||||
log_.debug() << "Time to download and store ledger = " << timeDiff;
|
||||
return lgrInfo;
|
||||
}
|
||||
|
||||
void
|
||||
ReportingETL::publishLedger(ripple::LedgerInfo const& lgrInfo)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " - Publishing ledger " << std::to_string(lgrInfo.seq);
|
||||
log_.info() << "Publishing ledger " << std::to_string(lgrInfo.seq);
|
||||
|
||||
if (!writing_)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " - Updating cache";
|
||||
log_.info() << "Updating cache";
|
||||
|
||||
std::vector<Backend::LedgerObject> diff =
|
||||
Backend::synchronousAndRetryOnTimeout([&](auto yield) {
|
||||
@@ -205,12 +223,11 @@ ReportingETL::publishLedger(ripple::LedgerInfo const& lgrInfo)
|
||||
|
||||
subscriptions_->pubBookChanges(lgrInfo, transactions);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " - Published ledger "
|
||||
<< std::to_string(lgrInfo.seq);
|
||||
log_.info() << "Published ledger " << std::to_string(lgrInfo.seq);
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " - Skipping publishing ledger "
|
||||
<< std::to_string(lgrInfo.seq);
|
||||
log_.info() << "Skipping publishing ledger "
|
||||
<< std::to_string(lgrInfo.seq);
|
||||
setLastPublish();
|
||||
}
|
||||
|
||||
@@ -219,9 +236,7 @@ ReportingETL::publishLedger(
|
||||
uint32_t ledgerSequence,
|
||||
std::optional<uint32_t> maxAttempts)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " : "
|
||||
<< "Attempting to publish ledger = " << ledgerSequence;
|
||||
log_.info() << "Attempting to publish ledger = " << ledgerSequence;
|
||||
size_t numAttempts = 0;
|
||||
while (!stopping_)
|
||||
{
|
||||
@@ -229,17 +244,15 @@ ReportingETL::publishLedger(
|
||||
|
||||
if (!range || range->maxSequence < ledgerSequence)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "Trying to publish. Could not find "
|
||||
"ledger with sequence = "
|
||||
<< ledgerSequence;
|
||||
log_.debug() << "Trying to publish. Could not find "
|
||||
"ledger with sequence = "
|
||||
<< ledgerSequence;
|
||||
// We try maxAttempts times to publish the ledger, waiting one
|
||||
// second in between each attempt.
|
||||
if (maxAttempts && numAttempts >= maxAttempts)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "Failed to publish ledger after "
|
||||
<< numAttempts << " attempts.";
|
||||
log_.debug() << "Failed to publish ledger after " << numAttempts
|
||||
<< " attempts.";
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
@@ -264,25 +277,19 @@ ReportingETL::publishLedger(
|
||||
std::optional<org::xrpl::rpc::v1::GetLedgerResponse>
|
||||
ReportingETL::fetchLedgerData(uint32_t seq)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Attempting to fetch ledger with sequence = " << seq;
|
||||
log_.debug() << "Attempting to fetch ledger with sequence = " << seq;
|
||||
|
||||
std::optional<org::xrpl::rpc::v1::GetLedgerResponse> response =
|
||||
loadBalancer_->fetchLedger(seq, false, false);
|
||||
if (response)
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : "
|
||||
<< "GetLedger reply = " << response->DebugString();
|
||||
log_.trace() << "GetLedger reply = " << response->DebugString();
|
||||
return response;
|
||||
}
|
||||
|
||||
std::optional<org::xrpl::rpc::v1::GetLedgerResponse>
|
||||
ReportingETL::fetchLedgerDataAndDiff(uint32_t seq)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Attempting to fetch ledger with sequence = " << seq;
|
||||
log_.debug() << "Attempting to fetch ledger with sequence = " << seq;
|
||||
|
||||
std::optional<org::xrpl::rpc::v1::GetLedgerResponse> response =
|
||||
loadBalancer_->fetchLedger(
|
||||
@@ -291,47 +298,36 @@ ReportingETL::fetchLedgerDataAndDiff(uint32_t seq)
|
||||
!backend_->cache().isFull() ||
|
||||
backend_->cache().latestLedgerSequence() >= seq);
|
||||
if (response)
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " : "
|
||||
<< "GetLedger reply = " << response->DebugString();
|
||||
log_.trace() << "GetLedger reply = " << response->DebugString();
|
||||
return response;
|
||||
}
|
||||
|
||||
std::pair<ripple::LedgerInfo, bool>
|
||||
ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "Beginning ledger update";
|
||||
|
||||
log_.debug() << "Beginning ledger update";
|
||||
ripple::LedgerInfo lgrInfo =
|
||||
deserializeHeader(ripple::makeSlice(rawData.ledger_header()));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Deserialized ledger header. " << detail::toString(lgrInfo);
|
||||
|
||||
log_.debug() << "Deserialized ledger header. " << detail::toString(lgrInfo);
|
||||
backend_->startWrites();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "started writes";
|
||||
log_.debug() << "started writes";
|
||||
|
||||
backend_->writeLedger(lgrInfo, std::move(*rawData.mutable_ledger_header()));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "wrote ledger header";
|
||||
log_.debug() << "wrote ledger header";
|
||||
|
||||
// Write successor info, if included from rippled
|
||||
if (rawData.object_neighbors_included())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " object neighbors included";
|
||||
log_.debug() << "object neighbors included";
|
||||
for (auto& obj : *(rawData.mutable_book_successors()))
|
||||
{
|
||||
auto firstBook = std::move(*obj.mutable_first_book());
|
||||
if (!firstBook.size())
|
||||
firstBook = uint256ToString(Backend::lastKey);
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " writing book successor "
|
||||
<< ripple::strHex(obj.book_base()) << " - "
|
||||
<< ripple::strHex(firstBook);
|
||||
log_.debug() << "writing book successor "
|
||||
<< ripple::strHex(obj.book_base()) << " - "
|
||||
<< ripple::strHex(firstBook);
|
||||
|
||||
backend_->writeSuccessor(
|
||||
std::move(*obj.mutable_book_base()),
|
||||
@@ -352,23 +348,20 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
if (obj.mod_type() ==
|
||||
org::xrpl::rpc::v1::RawLedgerObject::DELETED)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__
|
||||
<< " modifying successors for deleted object "
|
||||
<< ripple::strHex(obj.key()) << " - "
|
||||
<< ripple::strHex(*predPtr) << " - "
|
||||
<< ripple::strHex(*succPtr);
|
||||
log_.debug() << "Modifying successors for deleted object "
|
||||
<< ripple::strHex(obj.key()) << " - "
|
||||
<< ripple::strHex(*predPtr) << " - "
|
||||
<< ripple::strHex(*succPtr);
|
||||
|
||||
backend_->writeSuccessor(
|
||||
std::move(*predPtr), lgrInfo.seq, std::move(*succPtr));
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " adding successor for new object "
|
||||
<< ripple::strHex(obj.key()) << " - "
|
||||
<< ripple::strHex(*predPtr) << " - "
|
||||
<< ripple::strHex(*succPtr);
|
||||
log_.debug() << "adding successor for new object "
|
||||
<< ripple::strHex(obj.key()) << " - "
|
||||
<< ripple::strHex(*predPtr) << " - "
|
||||
<< ripple::strHex(*succPtr);
|
||||
|
||||
backend_->writeSuccessor(
|
||||
std::move(*predPtr),
|
||||
@@ -381,8 +374,7 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
}
|
||||
}
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " object modified "
|
||||
<< ripple::strHex(obj.key());
|
||||
log_.debug() << "object modified " << ripple::strHex(obj.key());
|
||||
}
|
||||
}
|
||||
std::vector<Backend::LedgerObject> cacheUpdates;
|
||||
@@ -396,15 +388,13 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
assert(key);
|
||||
cacheUpdates.push_back(
|
||||
{*key, {obj.mutable_data()->begin(), obj.mutable_data()->end()}});
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " key = " << ripple::strHex(*key)
|
||||
<< " - mod type = " << obj.mod_type();
|
||||
log_.debug() << "key = " << ripple::strHex(*key)
|
||||
<< " - mod type = " << obj.mod_type();
|
||||
|
||||
if (obj.mod_type() != org::xrpl::rpc::v1::RawLedgerObject::MODIFIED &&
|
||||
!rawData.object_neighbors_included())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " object neighbors not included. using cache";
|
||||
log_.debug() << "object neighbors not included. using cache";
|
||||
if (!backend_->cache().isFull() ||
|
||||
backend_->cache().latestLedgerSequence() != lgrInfo.seq - 1)
|
||||
throw std::runtime_error(
|
||||
@@ -423,9 +413,7 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
checkBookBase = isBookDir(*key, *blob);
|
||||
if (checkBookBase)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__
|
||||
<< " is book dir. key = " << ripple::strHex(*key);
|
||||
log_.debug() << "Is book dir. key = " << ripple::strHex(*key);
|
||||
auto bookBase = getBookBase(*key);
|
||||
auto oldFirstDir =
|
||||
backend_->cache().getSuccessor(bookBase, lgrInfo.seq - 1);
|
||||
@@ -435,9 +423,8 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
if ((isDeleted && key == oldFirstDir->key) ||
|
||||
(!isDeleted && key < oldFirstDir->key))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__
|
||||
<< " Need to recalculate book base successor. base = "
|
||||
log_.debug()
|
||||
<< "Need to recalculate book base successor. base = "
|
||||
<< ripple::strHex(bookBase)
|
||||
<< " - key = " << ripple::strHex(*key)
|
||||
<< " - isDeleted = " << isDeleted
|
||||
@@ -458,8 +445,7 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
// rippled didn't send successor information, so use our cache
|
||||
if (!rawData.object_neighbors_included())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " object neighbors not included. using cache";
|
||||
log_.debug() << "object neighbors not included. using cache";
|
||||
if (!backend_->cache().isFull() ||
|
||||
backend_->cache().latestLedgerSequence() != lgrInfo.seq)
|
||||
throw std::runtime_error(
|
||||
@@ -477,11 +463,10 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
ub = {Backend::lastKey, {}};
|
||||
if (obj.blob.size() == 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " writing successor for deleted object "
|
||||
<< ripple::strHex(obj.key) << " - "
|
||||
<< ripple::strHex(lb->key) << " - "
|
||||
<< ripple::strHex(ub->key);
|
||||
log_.debug() << "writing successor for deleted object "
|
||||
<< ripple::strHex(obj.key) << " - "
|
||||
<< ripple::strHex(lb->key) << " - "
|
||||
<< ripple::strHex(ub->key);
|
||||
|
||||
backend_->writeSuccessor(
|
||||
uint256ToString(lb->key),
|
||||
@@ -499,11 +484,10 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
lgrInfo.seq,
|
||||
uint256ToString(ub->key));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " writing successor for new object "
|
||||
<< ripple::strHex(lb->key) << " - "
|
||||
<< ripple::strHex(obj.key) << " - "
|
||||
<< ripple::strHex(ub->key);
|
||||
log_.debug() << "writing successor for new object "
|
||||
<< ripple::strHex(lb->key) << " - "
|
||||
<< ripple::strHex(obj.key) << " - "
|
||||
<< ripple::strHex(ub->key);
|
||||
}
|
||||
}
|
||||
for (auto const& base : bookSuccessorsToCalculate)
|
||||
@@ -516,10 +500,9 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
lgrInfo.seq,
|
||||
uint256ToString(succ->key));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " Updating book successor "
|
||||
<< ripple::strHex(base) << " - "
|
||||
<< ripple::strHex(succ->key);
|
||||
log_.debug()
|
||||
<< "Updating book successor " << ripple::strHex(base)
|
||||
<< " - " << ripple::strHex(succ->key);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -528,42 +511,31 @@ ReportingETL::buildNextLedger(org::xrpl::rpc::v1::GetLedgerResponse& rawData)
|
||||
lgrInfo.seq,
|
||||
uint256ToString(Backend::lastKey));
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " Updating book successor "
|
||||
<< ripple::strHex(base) << " - "
|
||||
<< ripple::strHex(Backend::lastKey);
|
||||
log_.debug()
|
||||
<< "Updating book successor " << ripple::strHex(base)
|
||||
<< " - " << ripple::strHex(Backend::lastKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
log_.debug()
|
||||
<< "Inserted/modified/deleted all objects. Number of objects = "
|
||||
<< rawData.ledger_objects().objects_size();
|
||||
FormattedTransactionsData insertTxResult =
|
||||
insertTransactions(lgrInfo, rawData);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Inserted all transactions. Number of transactions = "
|
||||
<< rawData.transactions_list().transactions_size();
|
||||
log_.debug() << "Inserted all transactions. Number of transactions = "
|
||||
<< rawData.transactions_list().transactions_size();
|
||||
backend_->writeAccountTransactions(std::move(insertTxResult.accountTxData));
|
||||
backend_->writeNFTs(std::move(insertTxResult.nfTokensData));
|
||||
backend_->writeNFTTransactions(std::move(insertTxResult.nfTokenTxData));
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "wrote account_tx";
|
||||
auto start = std::chrono::system_clock::now();
|
||||
log_.debug() << "wrote account_tx";
|
||||
|
||||
bool success = backend_->finishWrites(lgrInfo.seq);
|
||||
auto [success, duration] = util::timed<std::chrono::duration<double>>(
|
||||
[&]() { return backend_->finishWrites(lgrInfo.seq); });
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
log_.debug() << "Finished writes. took " << std::to_string(duration);
|
||||
log_.debug() << "Finished ledger update. " << detail::toString(lgrInfo);
|
||||
|
||||
auto duration = ((end - start).count()) / 1000000000.0;
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " finished writes. took " << std::to_string(duration);
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Finished ledger update. " << detail::toString(lgrInfo);
|
||||
return {lgrInfo, success};
|
||||
}
|
||||
|
||||
@@ -596,8 +568,7 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
* optional, returns.
|
||||
*/
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "Starting etl pipeline";
|
||||
log_.debug() << "Starting etl pipeline";
|
||||
writing_ = true;
|
||||
|
||||
auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||
@@ -647,12 +618,10 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
currentSequence) &&
|
||||
!writeConflict && !isStopping())
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
std::optional<org::xrpl::rpc::v1::GetLedgerResponse>
|
||||
fetchResponse{fetchLedgerDataAndDiff(currentSequence)};
|
||||
auto end = std::chrono::system_clock::now();
|
||||
|
||||
auto time = ((end - start).count()) / 1000000000.0;
|
||||
auto [fetchResponse, time] =
|
||||
util::timed<std::chrono::duration<double>>([&]() {
|
||||
return fetchLedgerDataAndDiff(currentSequence);
|
||||
});
|
||||
totalTime += time;
|
||||
|
||||
// if the fetch is unsuccessful, stop. fetchLedger only
|
||||
@@ -670,13 +639,12 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
fetchResponse->transactions_list().transactions_size() /
|
||||
time;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Extract phase time = " << time
|
||||
<< " . Extract phase tps = " << tps
|
||||
<< " . Avg extract time = "
|
||||
<< totalTime / (currentSequence - startSequence + 1)
|
||||
<< " . thread num = " << i
|
||||
<< " . seq = " << currentSequence;
|
||||
log_.info() << "Extract phase time = " << time
|
||||
<< " . Extract phase tps = " << tps
|
||||
<< " . Avg extract time = "
|
||||
<< totalTime / (currentSequence - startSequence + 1)
|
||||
<< " . thread num = " << i
|
||||
<< " . seq = " << currentSequence;
|
||||
|
||||
transformQueue->push(std::move(fetchResponse));
|
||||
currentSequence += numExtractors;
|
||||
@@ -720,7 +688,7 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
|
||||
auto duration = ((end - start).count()) / 1000000000.0;
|
||||
if (success)
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
log_.info()
|
||||
<< "Load phase of etl : "
|
||||
<< "Successfully wrote ledger! Ledger info: "
|
||||
<< detail::toString(lgrInfo) << ". txn count = " << numTxns
|
||||
@@ -729,7 +697,7 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
<< ". load txns per second = " << numTxns / duration
|
||||
<< ". load objs per second = " << numObjects / duration;
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
log_.error()
|
||||
<< "Error writing ledger. " << detail::toString(lgrInfo);
|
||||
// success is false if the ledger was already written
|
||||
if (success)
|
||||
@@ -747,7 +715,7 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
{
|
||||
deleting_ = true;
|
||||
ioContext_.post([this, &minSequence]() {
|
||||
BOOST_LOG_TRIVIAL(info) << "Running online delete";
|
||||
log_.info() << "Running online delete";
|
||||
|
||||
Backend::synchronous(
|
||||
[&](boost::asio::yield_context& yield) {
|
||||
@@ -755,7 +723,7 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
*onlineDeleteInterval_, yield);
|
||||
});
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Finished online delete";
|
||||
log_.info() << "Finished online delete";
|
||||
auto rng = backend_->fetchLedgerRange();
|
||||
minSequence = rng->minSequence;
|
||||
deleting_ = false;
|
||||
@@ -774,13 +742,12 @@ ReportingETL::runETLPipeline(uint32_t startSequence, int numExtractors)
|
||||
for (auto& t : extractors)
|
||||
t.join();
|
||||
auto end = std::chrono::system_clock::now();
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Extracted and wrote " << *lastPublishedSequence - startSequence
|
||||
<< " in " << ((end - begin).count()) / 1000000000.0;
|
||||
log_.debug() << "Extracted and wrote "
|
||||
<< *lastPublishedSequence - startSequence << " in "
|
||||
<< ((end - begin).count()) / 1000000000.0;
|
||||
writing_ = false;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : "
|
||||
<< "Stopping etl pipeline";
|
||||
log_.debug() << "Stopping etl pipeline";
|
||||
|
||||
return lastPublishedSequence;
|
||||
}
|
||||
@@ -801,40 +768,34 @@ ReportingETL::monitor()
|
||||
auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||
if (!rng)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " : "
|
||||
<< "Database is empty. Will download a ledger "
|
||||
"from the network.";
|
||||
log_.info() << "Database is empty. Will download a ledger "
|
||||
"from the network.";
|
||||
std::optional<ripple::LedgerInfo> ledger;
|
||||
if (startSequence_)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " : "
|
||||
<< "ledger sequence specified in config. "
|
||||
<< "Will begin ETL process starting with ledger "
|
||||
<< *startSequence_;
|
||||
log_.info() << "ledger sequence specified in config. "
|
||||
<< "Will begin ETL process starting with ledger "
|
||||
<< *startSequence_;
|
||||
ledger = loadInitialLedger(*startSequence_);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " : "
|
||||
log_.info()
|
||||
<< "Waiting for next ledger to be validated by network...";
|
||||
std::optional<uint32_t> mostRecentValidated =
|
||||
networkValidatedLedgers_->getMostRecent();
|
||||
if (mostRecentValidated)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " : "
|
||||
<< "Ledger " << *mostRecentValidated
|
||||
<< " has been validated. "
|
||||
<< "Downloading...";
|
||||
log_.info() << "Ledger " << *mostRecentValidated
|
||||
<< " has been validated. "
|
||||
<< "Downloading...";
|
||||
ledger = loadInitialLedger(*mostRecentValidated);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " : "
|
||||
<< "The wait for the next validated "
|
||||
<< "ledger has been aborted. "
|
||||
<< "Exiting monitor loop";
|
||||
log_.info() << "The wait for the next validated "
|
||||
<< "ledger has been aborted. "
|
||||
<< "Exiting monitor loop";
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -842,8 +803,7 @@ ReportingETL::monitor()
|
||||
rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " : "
|
||||
log_.error()
|
||||
<< "Failed to load initial ledger. Exiting monitor loop";
|
||||
return;
|
||||
}
|
||||
@@ -852,21 +812,18 @@ ReportingETL::monitor()
|
||||
{
|
||||
if (startSequence_)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
log_.warn()
|
||||
<< "start sequence specified but db is already populated";
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " : "
|
||||
log_.info()
|
||||
<< "Database already populated. Picking up from the tip of history";
|
||||
loadCache(rng->maxSequence);
|
||||
}
|
||||
assert(rng);
|
||||
uint32_t nextSequence = rng->maxSequence + 1;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : "
|
||||
<< "Database is populated. "
|
||||
<< "Starting monitor loop. sequence = " << nextSequence;
|
||||
log_.debug() << "Database is populated. "
|
||||
<< "Starting monitor loop. sequence = " << nextSequence;
|
||||
while (true)
|
||||
{
|
||||
if (auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||
@@ -878,11 +835,9 @@ ReportingETL::monitor()
|
||||
else if (networkValidatedLedgers_->waitUntilValidatedByNetwork(
|
||||
nextSequence, 1000))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " : "
|
||||
<< "Ledger with sequence = " << nextSequence
|
||||
<< " has been validated by the network. "
|
||||
<< "Attempting to find in database and publish";
|
||||
log_.info() << "Ledger with sequence = " << nextSequence
|
||||
<< " has been validated by the network. "
|
||||
<< "Attempting to find in database and publish";
|
||||
// Attempt to take over responsibility of ETL writer after 10 failed
|
||||
// attempts to publish the ledger. publishLedger() fails if the
|
||||
// ledger that has been validated by the network is not found in the
|
||||
@@ -893,17 +848,13 @@ ReportingETL::monitor()
|
||||
bool success = publishLedger(nextSequence, timeoutSeconds);
|
||||
if (!success)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__ << " : "
|
||||
<< "Failed to publish ledger with sequence = "
|
||||
<< nextSequence << " . Beginning ETL";
|
||||
log_.warn() << "Failed to publish ledger with sequence = "
|
||||
<< nextSequence << " . Beginning ETL";
|
||||
// doContinousETLPipelined returns the most recent sequence
|
||||
// published empty optional if no sequence was published
|
||||
std::optional<uint32_t> lastPublished =
|
||||
runETLPipeline(nextSequence, extractorThreads_);
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " : "
|
||||
<< "Aborting ETL. Falling back to publishing";
|
||||
log_.info() << "Aborting ETL. Falling back to publishing";
|
||||
// if no ledger was published, don't increment nextSequence
|
||||
if (lastPublished)
|
||||
nextSequence = *lastPublished + 1;
|
||||
@@ -920,8 +871,8 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
std::string const& port,
|
||||
boost::asio::yield_context& yield)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Loading cache from peer. ip = " << ip << " . port = " << port;
|
||||
log_.info() << "Loading cache from peer. ip = " << ip
|
||||
<< " . port = " << port;
|
||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||
namespace websocket = beast::websocket; // from
|
||||
@@ -933,7 +884,7 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
// These objects perform our I/O
|
||||
tcp::resolver resolver{ioContext_};
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " Creating websocket";
|
||||
log_.trace() << "Creating websocket";
|
||||
auto ws =
|
||||
std::make_unique<websocket::stream<beast::tcp_stream>>(ioContext_);
|
||||
|
||||
@@ -942,14 +893,13 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
if (ec)
|
||||
return {};
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " Connecting websocket";
|
||||
log_.trace() << "Connecting websocket";
|
||||
// Make the connection on the IP address we get from a lookup
|
||||
ws->next_layer().async_connect(results, yield[ec]);
|
||||
if (ec)
|
||||
return false;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " Performing websocket handshake";
|
||||
log_.trace() << "Performing websocket handshake";
|
||||
// Perform the websocket handshake
|
||||
ws->async_handshake(ip, "/", yield[ec]);
|
||||
if (ec)
|
||||
@@ -957,7 +907,7 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
|
||||
std::optional<boost::json::value> marker;
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " Sending request";
|
||||
log_.trace() << "Sending request";
|
||||
auto getRequest = [&](auto marker) {
|
||||
boost::json::object request = {
|
||||
{"command", "ledger_data"},
|
||||
@@ -981,8 +931,7 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
yield[ec]);
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " error writing = " << ec.message();
|
||||
log_.error() << "error writing = " << ec.message();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -990,8 +939,7 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
ws->async_read(buffer, yield[ec]);
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " error reading = " << ec.message();
|
||||
log_.error() << "error reading = " << ec.message();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1000,36 +948,30 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
|
||||
if (!parsed.is_object())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " Error parsing response: " << raw;
|
||||
log_.error() << "Error parsing response: " << raw;
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
<< __func__ << " Successfully parsed response " << parsed;
|
||||
log_.trace() << "Successfully parsed response " << parsed;
|
||||
|
||||
if (auto const& response = parsed.as_object();
|
||||
response.contains("error"))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " Response contains error: " << response;
|
||||
log_.error() << "Response contains error: " << response;
|
||||
auto const& err = response.at("error");
|
||||
if (err.is_string() && err.as_string() == "lgrNotFound")
|
||||
{
|
||||
++numAttempts;
|
||||
if (numAttempts >= 5)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__
|
||||
log_.error()
|
||||
<< " ledger not found at peer after 5 attempts. "
|
||||
"peer = "
|
||||
<< ip << " ledger = " << ledgerIndex
|
||||
<< ". Check your config and the health of the peer";
|
||||
return false;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__
|
||||
<< " ledger not found. ledger = " << ledgerIndex
|
||||
<< ". Sleeping and trying again";
|
||||
log_.warn() << "Ledger not found. ledger = " << ledgerIndex
|
||||
<< ". Sleeping and trying again";
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
continue;
|
||||
}
|
||||
@@ -1041,8 +983,7 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
if (!response.contains("cache_full") ||
|
||||
!response.at("cache_full").as_bool())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " cache not full for clio node. ip = " << ip;
|
||||
log_.error() << "cache not full for clio node. ip = " << ip;
|
||||
return false;
|
||||
}
|
||||
if (response.contains("marker"))
|
||||
@@ -1063,8 +1004,7 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
if (!stateObject.key.parseHex(
|
||||
obj.at("index").as_string().c_str()))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " failed to parse object id";
|
||||
log_.error() << "failed to parse object id";
|
||||
return false;
|
||||
}
|
||||
boost::algorithm::unhex(
|
||||
@@ -1075,22 +1015,19 @@ ReportingETL::loadCacheFromClioPeer(
|
||||
backend_->cache().update(objects, ledgerIndex, true);
|
||||
|
||||
if (marker)
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " - At marker " << *marker;
|
||||
|
||||
log_.debug() << "At marker " << *marker;
|
||||
} while (marker || !started);
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__
|
||||
<< " Finished downloading ledger from clio node. ip = " << ip;
|
||||
backend_->cache().setFull();
|
||||
|
||||
log_.info() << "Finished downloading ledger from clio node. ip = "
|
||||
<< ip;
|
||||
|
||||
backend_->cache().setFull();
|
||||
return true;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " Encountered exception : " << e.what()
|
||||
<< " - ip = " << ip;
|
||||
log_.error() << "Encountered exception : " << e.what()
|
||||
<< " - ip = " << ip;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1101,7 +1038,7 @@ ReportingETL::loadCache(uint32_t seq)
|
||||
if (cacheLoadStyle_ == CacheLoadStyle::NOT_AT_ALL)
|
||||
{
|
||||
backend_->cache().setDisabled();
|
||||
BOOST_LOG_TRIVIAL(warning) << "Cache is disabled. Not loading";
|
||||
log_.warn() << "Cache is disabled. Not loading";
|
||||
return;
|
||||
}
|
||||
// sanity check to make sure we are not calling this multiple times
|
||||
@@ -1142,12 +1079,11 @@ ReportingETL::loadCache(uint32_t seq)
|
||||
while (cacheLoadStyle_ == CacheLoadStyle::SYNC &&
|
||||
!backend_->cache().isFull())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Cache not full. Cache size = " << backend_->cache().size()
|
||||
<< ". Sleeping ...";
|
||||
log_.debug() << "Cache not full. Cache size = "
|
||||
<< backend_->cache().size() << ". Sleeping ...";
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Cache is full. Cache size = " << backend_->cache().size();
|
||||
log_.info() << "Cache is full. Cache size = "
|
||||
<< backend_->cache().size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1198,9 +1134,8 @@ ReportingETL::loadCacheFromDb(uint32_t seq)
|
||||
if (c)
|
||||
cursorStr << ripple::strHex(*c) << ", ";
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Loading cache. num cursors = " << cursors.size() - 1;
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " cursors = " << cursorStr.str();
|
||||
log_.info() << "Loading cache. num cursors = " << cursors.size() - 1;
|
||||
log_.trace() << "cursors = " << cursorStr.str();
|
||||
|
||||
cacheDownloader_ = std::thread{[this, seq, cursors]() {
|
||||
auto startTime = std::chrono::system_clock::now();
|
||||
@@ -1221,9 +1156,8 @@ ReportingETL::loadCacheFromDb(uint32_t seq)
|
||||
std::string cursorStr = cursor.has_value()
|
||||
? ripple::strHex(cursor.value())
|
||||
: ripple::strHex(Backend::firstKey);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Starting a cursor: " << cursorStr
|
||||
<< " markers = " << *markers;
|
||||
log_.debug() << "Starting a cursor: " << cursorStr
|
||||
<< " markers = " << *markers;
|
||||
|
||||
while (!stopping_)
|
||||
{
|
||||
@@ -1237,7 +1171,7 @@ ReportingETL::loadCacheFromDb(uint32_t seq)
|
||||
backend_->cache().update(res.objects, seq, true);
|
||||
if (!res.cursor || (end && *(res.cursor) > *end))
|
||||
break;
|
||||
BOOST_LOG_TRIVIAL(trace)
|
||||
log_.trace()
|
||||
<< "Loading cache. cache size = "
|
||||
<< backend_->cache().size() << " - cursor = "
|
||||
<< ripple::strHex(res.cursor.value())
|
||||
@@ -1254,18 +1188,16 @@ ReportingETL::loadCacheFromDb(uint32_t seq)
|
||||
auto duration =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(
|
||||
endTime - startTime);
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Finished loading cache. cache size = "
|
||||
<< backend_->cache().size() << ". Took "
|
||||
<< duration.count() << " seconds";
|
||||
log_.info() << "Finished loading cache. cache size = "
|
||||
<< backend_->cache().size() << ". Took "
|
||||
<< duration.count() << " seconds";
|
||||
backend_->cache().setFull();
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Finished a cursor. num remaining = "
|
||||
<< *numRemaining << " start = " << cursorStr
|
||||
<< " markers = " << *markers;
|
||||
log_.info() << "Finished a cursor. num remaining = "
|
||||
<< *numRemaining << " start = " << cursorStr
|
||||
<< " markers = " << *markers;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1275,7 +1207,7 @@ ReportingETL::loadCacheFromDb(uint32_t seq)
|
||||
void
|
||||
ReportingETL::monitorReadOnly()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << "Starting reporting in strict read only mode";
|
||||
log_.debug() << "Starting reporting in strict read only mode";
|
||||
auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||
uint32_t latestSequence;
|
||||
if (!rng)
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_APP_REPORTING_REPORTINGETL_H_INCLUDED
|
||||
#define RIPPLE_APP_REPORTING_REPORTINGETL_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/ledger/ReadView.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -8,6 +26,7 @@
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <etl/ETLSource.h>
|
||||
#include <log/Logger.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
|
||||
#include "org/xrpl/rpc/v1/xrp_ledger.grpc.pb.h"
|
||||
@@ -55,6 +74,8 @@ class SubscriptionManager;
|
||||
class ReportingETL
|
||||
{
|
||||
private:
|
||||
clio::Logger log_{"ETL"};
|
||||
|
||||
std::shared_ptr<BackendInterface> backend_;
|
||||
std::shared_ptr<SubscriptionManager> subscriptions_;
|
||||
std::shared_ptr<ETLLoadBalancer> loadBalancer_;
|
||||
@@ -300,7 +321,7 @@ private:
|
||||
void
|
||||
run()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting reporting etl";
|
||||
log_.info() << "Starting reporting etl";
|
||||
stopping_ = false;
|
||||
|
||||
doWork();
|
||||
@@ -337,8 +358,8 @@ public:
|
||||
|
||||
~ReportingETL()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "onStop called";
|
||||
BOOST_LOG_TRIVIAL(debug) << "Stopping Reporting ETL";
|
||||
log_.info() << "onStop called";
|
||||
log_.debug() << "Stopping Reporting ETL";
|
||||
stopping_ = true;
|
||||
|
||||
if (worker_.joinable())
|
||||
@@ -346,7 +367,7 @@ public:
|
||||
if (cacheDownloader_.joinable())
|
||||
cacheDownloader_.join();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "Joined ReportingETL worker thread";
|
||||
log_.debug() << "Joined ReportingETL worker thread";
|
||||
}
|
||||
|
||||
boost::json::object
|
||||
@@ -392,5 +413,3 @@ public:
|
||||
return now - (rippleEpochStart + closeTime);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
209
src/log/Logger.cpp
Normal file
209
src/log/Logger.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <config/Config.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
|
||||
namespace clio {
|
||||
|
||||
Logger LogService::general_log_ = Logger{"General"};
|
||||
Logger LogService::alert_log_ = Logger{"Alert"};
|
||||
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, Severity sev)
|
||||
{
|
||||
static constexpr std::array<const char*, 6> labels = {
|
||||
"TRC",
|
||||
"DBG",
|
||||
"NFO",
|
||||
"WRN",
|
||||
"ERR",
|
||||
"FTL",
|
||||
};
|
||||
|
||||
return stream << labels.at(static_cast<int>(sev));
|
||||
}
|
||||
|
||||
Severity
|
||||
tag_invoke(boost::json::value_to_tag<Severity>, boost::json::value const& value)
|
||||
{
|
||||
if (not value.is_string())
|
||||
throw std::runtime_error("`log_level` must be a string");
|
||||
auto const& logLevel = value.as_string();
|
||||
|
||||
if (boost::iequals(logLevel, "trace"))
|
||||
return Severity::TRC;
|
||||
else if (boost::iequals(logLevel, "debug"))
|
||||
return Severity::DBG;
|
||||
else if (boost::iequals(logLevel, "info"))
|
||||
return Severity::NFO;
|
||||
else if (
|
||||
boost::iequals(logLevel, "warning") || boost::iequals(logLevel, "warn"))
|
||||
return Severity::WRN;
|
||||
else if (boost::iequals(logLevel, "error"))
|
||||
return Severity::ERR;
|
||||
else if (boost::iequals(logLevel, "fatal"))
|
||||
return Severity::FTL;
|
||||
else
|
||||
throw std::runtime_error(
|
||||
"Could not parse `log_level`: expected `trace`, `debug`, `info`, "
|
||||
"`warning`, `error` or `fatal`");
|
||||
}
|
||||
|
||||
void
|
||||
LogService::init(Config const& config)
|
||||
{
|
||||
namespace src = boost::log::sources;
|
||||
namespace keywords = boost::log::keywords;
|
||||
namespace sinks = boost::log::sinks;
|
||||
|
||||
boost::log::add_common_attributes();
|
||||
boost::log::register_simple_formatter_factory<Severity, char>("Severity");
|
||||
auto const defaultFormat =
|
||||
"%TimeStamp% (%SourceLocation%) [%ThreadID%] %Channel%:%Severity% "
|
||||
"%Message%";
|
||||
std::string format =
|
||||
config.valueOr<std::string>("log_format", defaultFormat);
|
||||
|
||||
if (config.valueOr("log_to_console", false))
|
||||
{
|
||||
boost::log::add_console_log(std::cout, keywords::format = format);
|
||||
}
|
||||
|
||||
auto logDir = config.maybeValue<std::string>("log_directory");
|
||||
if (logDir)
|
||||
{
|
||||
boost::filesystem::path dirPath{logDir.value()};
|
||||
if (!boost::filesystem::exists(dirPath))
|
||||
boost::filesystem::create_directories(dirPath);
|
||||
auto const rotationSize =
|
||||
config.valueOr<uint64_t>("log_rotation_size", 2048u) * 1024u *
|
||||
1024u;
|
||||
auto const rotationPeriod =
|
||||
config.valueOr<uint32_t>("log_rotation_hour_interval", 12u);
|
||||
auto const dirSize =
|
||||
config.valueOr<uint64_t>("log_directory_max_size", 50u * 1024u) *
|
||||
1024u * 1024u;
|
||||
auto fileSink = boost::log::add_file_log(
|
||||
keywords::file_name = dirPath / "clio.log",
|
||||
keywords::target_file_name = dirPath / "clio_%Y-%m-%d_%H-%M-%S.log",
|
||||
keywords::auto_flush = true,
|
||||
keywords::format = format,
|
||||
keywords::open_mode = std::ios_base::app,
|
||||
keywords::rotation_size = rotationSize,
|
||||
keywords::time_based_rotation =
|
||||
sinks::file::rotation_at_time_interval(
|
||||
boost::posix_time::hours(rotationPeriod)));
|
||||
fileSink->locked_backend()->set_file_collector(
|
||||
sinks::file::make_collector(
|
||||
keywords::target = dirPath, keywords::max_size = dirSize));
|
||||
fileSink->locked_backend()->scan_for_files();
|
||||
}
|
||||
|
||||
// get default severity, can be overridden per channel using
|
||||
// the `log_channels` array
|
||||
auto defaultSeverity = config.valueOr<Severity>("log_level", Severity::NFO);
|
||||
static constexpr std::array<const char*, 7> channels = {
|
||||
"General",
|
||||
"WebServer",
|
||||
"Backend",
|
||||
"RPC",
|
||||
"ETL",
|
||||
"Subscriptions",
|
||||
"Performance",
|
||||
};
|
||||
|
||||
auto core = boost::log::core::get();
|
||||
auto min_severity = boost::log::expressions::channel_severity_filter(
|
||||
log_channel, log_severity);
|
||||
|
||||
for (auto const& channel : channels)
|
||||
min_severity[channel] = defaultSeverity;
|
||||
min_severity["Alert"] =
|
||||
Severity::WRN; // Channel for alerts, always warning severity
|
||||
|
||||
for (auto const overrides = config.arrayOr("log_channels", {});
|
||||
auto const& cfg : overrides)
|
||||
{
|
||||
auto name = cfg.valueOrThrow<std::string>(
|
||||
"channel", "Channel name is required");
|
||||
if (not std::count(std::begin(channels), std::end(channels), name))
|
||||
throw std::runtime_error(
|
||||
"Can't override settings for log channel " + name +
|
||||
": invalid channel");
|
||||
|
||||
min_severity[name] =
|
||||
cfg.valueOr<Severity>("log_level", defaultSeverity);
|
||||
}
|
||||
|
||||
core->set_filter(min_severity);
|
||||
LogService::info() << "Default log level = " << defaultSeverity;
|
||||
}
|
||||
|
||||
Logger::Pump
|
||||
Logger::trace(source_location_t const& loc) const
|
||||
{
|
||||
return {logger_, Severity::TRC, loc};
|
||||
};
|
||||
Logger::Pump
|
||||
Logger::debug(source_location_t const& loc) const
|
||||
{
|
||||
return {logger_, Severity::DBG, loc};
|
||||
};
|
||||
Logger::Pump
|
||||
Logger::info(source_location_t const& loc) const
|
||||
{
|
||||
return {logger_, Severity::NFO, loc};
|
||||
};
|
||||
Logger::Pump
|
||||
Logger::warn(source_location_t const& loc) const
|
||||
{
|
||||
return {logger_, Severity::WRN, loc};
|
||||
};
|
||||
Logger::Pump
|
||||
Logger::error(source_location_t const& loc) const
|
||||
{
|
||||
return {logger_, Severity::ERR, loc};
|
||||
};
|
||||
Logger::Pump
|
||||
Logger::fatal(source_location_t const& loc) const
|
||||
{
|
||||
return {logger_, Severity::FTL, loc};
|
||||
};
|
||||
|
||||
std::string
|
||||
Logger::Pump::pretty_path(source_location_t const& loc, size_t max_depth) const
|
||||
{
|
||||
auto const file_path = std::string{loc.file_name()};
|
||||
auto idx = file_path.size();
|
||||
while (max_depth-- > 0)
|
||||
{
|
||||
idx = file_path.rfind('/', idx - 1);
|
||||
if (idx == std::string::npos || idx == 0)
|
||||
break;
|
||||
}
|
||||
return file_path.substr(idx == std::string::npos ? 0 : idx + 1) + ':' +
|
||||
std::to_string(loc.line());
|
||||
}
|
||||
|
||||
} // namespace clio
|
||||
314
src/log/Logger.h
Normal file
314
src/log/Logger.h
Normal file
@@ -0,0 +1,314 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/log/core/core.hpp>
|
||||
#include <boost/log/expressions/predicates/channel_severity_filter.hpp>
|
||||
#include <boost/log/sinks/unlocked_frontend.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/severity_channel_logger.hpp>
|
||||
#include <boost/log/sources/severity_feature.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/utility/manipulators/add_value.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/log/utility/setup/formatter_parser.hpp>
|
||||
|
||||
#if defined(HAS_SOURCE_LOCATION) && __has_builtin(__builtin_source_location)
|
||||
// this is used by fully compatible compilers like gcc
|
||||
#include <source_location>
|
||||
|
||||
#elif defined(HAS_EXPERIMENTAL_SOURCE_LOCATION)
|
||||
// this is used by clang on linux where source_location is still not out of
|
||||
// experimental headers
|
||||
#include <experimental/source_location>
|
||||
#endif
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace clio {
|
||||
|
||||
class Config;
|
||||
#if defined(HAS_SOURCE_LOCATION) && __has_builtin(__builtin_source_location)
|
||||
using source_location_t = std::source_location;
|
||||
#define CURRENT_SRC_LOCATION source_location_t::current()
|
||||
|
||||
#elif defined(HAS_EXPERIMENTAL_SOURCE_LOCATION)
|
||||
using source_location_t = std::experimental::source_location;
|
||||
#define CURRENT_SRC_LOCATION source_location_t::current()
|
||||
|
||||
#else
|
||||
// A workaround for AppleClang that is lacking source_location atm.
|
||||
// TODO: remove this workaround when all compilers catch up to c++20
|
||||
class SourceLocation
|
||||
{
|
||||
std::string_view file_;
|
||||
std::size_t line_;
|
||||
|
||||
public:
|
||||
SourceLocation(std::string_view file, std::size_t line)
|
||||
: file_{file}, line_{line}
|
||||
{
|
||||
}
|
||||
std::string_view
|
||||
file_name() const
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
std::size_t
|
||||
line() const
|
||||
{
|
||||
return line_;
|
||||
}
|
||||
};
|
||||
using source_location_t = SourceLocation;
|
||||
#define CURRENT_SRC_LOCATION \
|
||||
source_location_t(__builtin_FILE(), __builtin_LINE())
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Custom severity levels for @ref Logger.
|
||||
*/
|
||||
enum class Severity {
|
||||
TRC,
|
||||
DBG,
|
||||
NFO,
|
||||
WRN,
|
||||
ERR,
|
||||
FTL,
|
||||
};
|
||||
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(log_severity, "Severity", Severity);
|
||||
BOOST_LOG_ATTRIBUTE_KEYWORD(log_channel, "Channel", std::string);
|
||||
|
||||
/**
|
||||
* @brief Custom labels for @ref Severity in log output.
|
||||
*
|
||||
* @param stream std::ostream The output stream
|
||||
* @param sev Severity The severity to output to the ostream
|
||||
* @return std::ostream& The same ostream we were given
|
||||
*/
|
||||
std::ostream&
|
||||
operator<<(std::ostream& stream, Severity sev);
|
||||
|
||||
/**
|
||||
* @brief Custom JSON parser for @ref Severity.
|
||||
*
|
||||
* @param value The JSON string to parse
|
||||
* @return Severity The parsed severity
|
||||
* @throws std::runtime_error Thrown if severity is not in the right format
|
||||
*/
|
||||
Severity
|
||||
tag_invoke(
|
||||
boost::json::value_to_tag<Severity>,
|
||||
boost::json::value const& value);
|
||||
|
||||
/**
|
||||
* @brief A simple thread-safe logger for the channel specified
|
||||
* in the constructor.
|
||||
*
|
||||
* This is cheap to copy and move. Designed to be used as a member variable or
|
||||
* otherwise. See @ref LogService::init() for setup of the logging core and
|
||||
* severity levels for each channel.
|
||||
*/
|
||||
class Logger final
|
||||
{
|
||||
using logger_t =
|
||||
boost::log::sources::severity_channel_logger_mt<Severity, std::string>;
|
||||
mutable logger_t logger_;
|
||||
|
||||
friend class LogService; // to expose the Pump interface
|
||||
|
||||
/**
|
||||
* @brief Helper that pumps data into a log record via `operator<<`.
|
||||
*/
|
||||
class Pump final
|
||||
{
|
||||
using pump_opt_t =
|
||||
std::optional<boost::log::aux::record_pump<logger_t>>;
|
||||
|
||||
boost::log::record rec_;
|
||||
pump_opt_t pump_ = std::nullopt;
|
||||
|
||||
public:
|
||||
~Pump() = default;
|
||||
Pump(logger_t& logger, Severity sev, source_location_t const& loc)
|
||||
: rec_{logger.open_record(boost::log::keywords::severity = sev)}
|
||||
{
|
||||
if (rec_)
|
||||
{
|
||||
pump_.emplace(boost::log::aux::make_record_pump(logger, rec_));
|
||||
pump_->stream() << boost::log::add_value(
|
||||
"SourceLocation", pretty_path(loc));
|
||||
}
|
||||
}
|
||||
|
||||
Pump(Pump&&) = delete;
|
||||
Pump(Pump const&) = delete;
|
||||
Pump&
|
||||
operator=(Pump const&) = delete;
|
||||
Pump&
|
||||
operator=(Pump&&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Perfectly forwards any incoming data into the underlying
|
||||
* boost::log pump if the pump is available. nop otherwise.
|
||||
*
|
||||
* @tparam T Type of data to pump
|
||||
* @param data The data to pump
|
||||
* @return Pump& Reference to itself for chaining
|
||||
*/
|
||||
template <typename T>
|
||||
[[maybe_unused]] Pump&
|
||||
operator<<(T&& data)
|
||||
{
|
||||
if (pump_)
|
||||
pump_->stream() << std::forward<T>(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] std::string
|
||||
pretty_path(source_location_t const& loc, size_t max_depth = 3) const;
|
||||
};
|
||||
|
||||
public:
|
||||
~Logger() = default;
|
||||
/**
|
||||
* @brief Construct a new Logger object that produces loglines for the
|
||||
* specified channel.
|
||||
*
|
||||
* See @ref LogService::init() for general setup and configuration of
|
||||
* severity levels per channel.
|
||||
*
|
||||
* @param channel The channel this logger will report into.
|
||||
*/
|
||||
Logger(std::string channel)
|
||||
: logger_{boost::log::keywords::channel = channel}
|
||||
{
|
||||
}
|
||||
Logger(Logger const&) = default;
|
||||
Logger(Logger&&) = default;
|
||||
Logger&
|
||||
operator=(Logger const&) = default;
|
||||
Logger&
|
||||
operator=(Logger&&) = default;
|
||||
|
||||
/*! Interface for logging at @ref Severity::TRC severity */
|
||||
[[nodiscard]] Pump
|
||||
trace(source_location_t const& loc = CURRENT_SRC_LOCATION) const;
|
||||
|
||||
/*! Interface for logging at @ref Severity::DBG severity */
|
||||
[[nodiscard]] Pump
|
||||
debug(source_location_t const& loc = CURRENT_SRC_LOCATION) const;
|
||||
|
||||
/*! Interface for logging at @ref Severity::INFO severity */
|
||||
[[nodiscard]] Pump
|
||||
info(source_location_t const& loc = CURRENT_SRC_LOCATION) const;
|
||||
|
||||
/*! Interface for logging at @ref Severity::WRN severity */
|
||||
[[nodiscard]] Pump
|
||||
warn(source_location_t const& loc = CURRENT_SRC_LOCATION) const;
|
||||
|
||||
/*! Interface for logging at @ref Severity::ERR severity */
|
||||
[[nodiscard]] Pump
|
||||
error(source_location_t const& loc = CURRENT_SRC_LOCATION) const;
|
||||
|
||||
/*! Interface for logging at @ref Severity::FTL severity */
|
||||
[[nodiscard]] Pump
|
||||
fatal(source_location_t const& loc = CURRENT_SRC_LOCATION) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A global logging service.
|
||||
*
|
||||
* Used to initialize and setup the logging core as well as a globally available
|
||||
* entrypoint for logging into the `General` channel as well as raising alerts.
|
||||
*/
|
||||
class LogService
|
||||
{
|
||||
static Logger general_log_; /*! Global logger for General channel */
|
||||
static Logger alert_log_; /*! Global logger for Alerts channel */
|
||||
|
||||
public:
|
||||
LogService() = delete;
|
||||
|
||||
/**
|
||||
* @brief Global log core initialization from a @ref Config
|
||||
*/
|
||||
static void
|
||||
init(Config const& config);
|
||||
|
||||
/*! Globally accesible General logger at @ref Severity::TRC severity */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
trace(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return general_log_.trace(loc);
|
||||
}
|
||||
|
||||
/*! Globally accesible General logger at @ref Severity::DBG severity */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
debug(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return general_log_.debug(loc);
|
||||
}
|
||||
|
||||
/*! Globally accesible General logger at @ref Severity::NFO severity */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
info(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return general_log_.info(loc);
|
||||
}
|
||||
|
||||
/*! Globally accesible General logger at @ref Severity::WRN severity */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
warn(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return general_log_.warn(loc);
|
||||
}
|
||||
|
||||
/*! Globally accesible General logger at @ref Severity::ERR severity */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
error(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return general_log_.error(loc);
|
||||
}
|
||||
|
||||
/*! Globally accesible General logger at @ref Severity::FTL severity */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
fatal(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return general_log_.fatal(loc);
|
||||
}
|
||||
|
||||
/*! Globally accesible Alert logger */
|
||||
[[nodiscard]] static Logger::Pump
|
||||
alert(source_location_t const& loc = CURRENT_SRC_LOCATION)
|
||||
{
|
||||
return alert_log_.warn(loc);
|
||||
}
|
||||
};
|
||||
|
||||
}; // namespace clio
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef CLIO_BUILD_INFO_H
|
||||
#define CLIO_BUILD_INFO_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -12,5 +30,3 @@ std::string const&
|
||||
getClioFullVersionString();
|
||||
|
||||
} // namespace Build
|
||||
|
||||
#endif // CLIO_BUILD_INFO_H
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/beast/core/SemanticVersion.h>
|
||||
#include <boost/preprocessor/stringize.hpp>
|
||||
#include <algorithm>
|
||||
@@ -12,17 +31,15 @@ namespace Build {
|
||||
// and follow the format described at http://semver.org/
|
||||
//------------------------------------------------------------------------------
|
||||
// clang-format off
|
||||
char const* const versionString = "1.0.2"
|
||||
// clang-format on
|
||||
char const* const versionString = "1.0.4"
|
||||
// clang-format on
|
||||
|
||||
#if defined(DEBUG) || defined(SANITIZER)
|
||||
"+"
|
||||
#ifdef CLIO_GIT_COMMIT_HASH
|
||||
CLIO_GIT_COMMIT_HASH
|
||||
"."
|
||||
#ifdef CLIO_BUILD
|
||||
CLIO_BUILD
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
"DEBUG"
|
||||
".DEBUG"
|
||||
#ifdef SANITIZER
|
||||
"."
|
||||
#endif
|
||||
@@ -31,12 +48,10 @@ char const* const versionString = "1.0.2"
|
||||
#ifdef SANITIZER
|
||||
BOOST_PP_STRINGIZE(SANITIZER)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef PKG
|
||||
"-release"
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
;
|
||||
|
||||
std::string const&
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <grpc/impl/codegen/port_platform.h>
|
||||
#ifdef GRPC_TSAN_ENABLED
|
||||
#undef GRPC_TSAN_ENABLED
|
||||
@@ -6,26 +25,22 @@
|
||||
#undef GRPC_ASAN_ENABLED
|
||||
#endif
|
||||
|
||||
#include <backend/BackendFactory.h>
|
||||
#include <config/Config.h>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <log/Logger.h>
|
||||
#include <webserver/Listener.h>
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/strand.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/sinks/text_file_backend.hpp>
|
||||
#include <boost/log/sources/record_ostream.hpp>
|
||||
#include <boost/log/sources/severity_logger.hpp>
|
||||
#include <boost/log/support/date_time.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||
#include <boost/log/utility/setup/console.hpp>
|
||||
#include <boost/log/utility/setup/file.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <backend/BackendFactory.h>
|
||||
#include <config/Config.h>
|
||||
#include <cstdlib>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
@@ -35,12 +50,67 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <webserver/Listener.h>
|
||||
|
||||
using namespace clio;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
/**
|
||||
* @brief Parse command line and return path to configuration file
|
||||
*
|
||||
* @param argc
|
||||
* @param argv
|
||||
* @return std::string Path to configuration file
|
||||
*/
|
||||
std::string
|
||||
parseCli(int argc, char* argv[])
|
||||
{
|
||||
static constexpr char defaultConfigPath[] = "/etc/opt/clio/config.json";
|
||||
|
||||
// clang-format off
|
||||
po::options_description description("Options");
|
||||
description.add_options()
|
||||
("help,h", "print help message and exit")
|
||||
("version,v", "print version and exit")
|
||||
("conf,c", po::value<std::string>()->default_value(defaultConfigPath), "configuration file")
|
||||
;
|
||||
// clang-format on
|
||||
po::positional_options_description positional;
|
||||
positional.add("conf", 1);
|
||||
|
||||
po::variables_map parsed;
|
||||
po::store(
|
||||
po::command_line_parser(argc, argv)
|
||||
.options(description)
|
||||
.positional(positional)
|
||||
.run(),
|
||||
parsed);
|
||||
po::notify(parsed);
|
||||
|
||||
if (parsed.count("version"))
|
||||
{
|
||||
std::cout << Build::getClioFullVersionString() << '\n';
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
if (parsed.count("help"))
|
||||
{
|
||||
std::cout << "Clio server " << Build::getClioFullVersionString()
|
||||
<< "\n\n"
|
||||
<< description;
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
return parsed["conf"].as<std::string>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse certificates from configuration file
|
||||
*
|
||||
* @param config The configuration
|
||||
* @return std::optional<ssl::context> SSL context if certificates were parsed
|
||||
*/
|
||||
std::optional<ssl::context>
|
||||
parse_certs(Config const& config)
|
||||
parseCerts(Config const& config)
|
||||
{
|
||||
if (!config.contains("ssl_cert_file") || !config.contains("ssl_key_file"))
|
||||
return {};
|
||||
@@ -80,85 +150,12 @@ parse_certs(Config const& config)
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void
|
||||
initLogging(Config const& config)
|
||||
{
|
||||
namespace src = boost::log::sources;
|
||||
namespace keywords = boost::log::keywords;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace trivial = boost::log::trivial;
|
||||
boost::log::add_common_attributes();
|
||||
std::string format = "[%TimeStamp%] [%ThreadID%] [%Severity%] %Message%";
|
||||
if (config.valueOr<bool>("log_to_console", true))
|
||||
{
|
||||
boost::log::add_console_log(std::cout, keywords::format = format);
|
||||
}
|
||||
|
||||
if (auto logDir = config.maybeValue<std::string>("log_directory"); logDir)
|
||||
{
|
||||
boost::filesystem::path dirPath{*logDir};
|
||||
if (!boost::filesystem::exists(dirPath))
|
||||
boost::filesystem::create_directories(dirPath);
|
||||
auto const rotationSize =
|
||||
config.valueOr<int64_t>("log_rotation_size", 2 * 1024) * 1024 *
|
||||
1024u;
|
||||
if (rotationSize <= 0)
|
||||
throw std::runtime_error(
|
||||
"log rotation size must be greater than 0");
|
||||
auto const rotationPeriod =
|
||||
config.valueOr<int64_t>("log_rotation_hour_interval", 12u);
|
||||
if (rotationPeriod <= 0)
|
||||
throw std::runtime_error(
|
||||
"log rotation time interval must be greater than 0");
|
||||
auto const dirSize =
|
||||
config.valueOr<int64_t>("log_directory_max_size", 50 * 1024) *
|
||||
1024 * 1024u;
|
||||
if (dirSize <= 0)
|
||||
throw std::runtime_error(
|
||||
"log rotation directory max size must be greater than 0");
|
||||
auto fileSink = boost::log::add_file_log(
|
||||
keywords::file_name = dirPath / "clio.log",
|
||||
keywords::target_file_name = dirPath / "clio_%Y-%m-%d_%H-%M-%S.log",
|
||||
keywords::auto_flush = true,
|
||||
keywords::format = format,
|
||||
keywords::open_mode = std::ios_base::app,
|
||||
keywords::rotation_size = rotationSize,
|
||||
keywords::time_based_rotation =
|
||||
sinks::file::rotation_at_time_interval(
|
||||
boost::posix_time::hours(rotationPeriod)));
|
||||
fileSink->locked_backend()->set_file_collector(
|
||||
sinks::file::make_collector(
|
||||
keywords::target = dirPath, keywords::max_size = dirSize));
|
||||
fileSink->locked_backend()->scan_for_files();
|
||||
}
|
||||
auto const logLevel = config.valueOr<std::string>("log_level", "info");
|
||||
if (boost::iequals(logLevel, "trace"))
|
||||
boost::log::core::get()->set_filter(
|
||||
trivial::severity >= trivial::trace);
|
||||
else if (boost::iequals(logLevel, "debug"))
|
||||
boost::log::core::get()->set_filter(
|
||||
trivial::severity >= trivial::debug);
|
||||
else if (boost::iequals(logLevel, "info"))
|
||||
boost::log::core::get()->set_filter(trivial::severity >= trivial::info);
|
||||
else if (
|
||||
boost::iequals(logLevel, "warning") || boost::iequals(logLevel, "warn"))
|
||||
boost::log::core::get()->set_filter(
|
||||
trivial::severity >= trivial::warning);
|
||||
else if (boost::iequals(logLevel, "error"))
|
||||
boost::log::core::get()->set_filter(
|
||||
trivial::severity >= trivial::error);
|
||||
else if (boost::iequals(logLevel, "fatal"))
|
||||
boost::log::core::get()->set_filter(
|
||||
trivial::severity >= trivial::fatal);
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning) << "Unrecognized log level: " << logLevel
|
||||
<< ". Setting log level to info";
|
||||
boost::log::core::get()->set_filter(trivial::severity >= trivial::info);
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Log level = " << logLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start context threads
|
||||
*
|
||||
* @param ioc Context
|
||||
* @param numThreads Number of worker threads to start
|
||||
*/
|
||||
void
|
||||
start(boost::asio::io_context& ioc, std::uint32_t numThreads)
|
||||
{
|
||||
@@ -172,36 +169,21 @@ start(boost::asio::io_context& ioc, std::uint32_t numThreads)
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
try
|
||||
{
|
||||
// Check command line arguments.
|
||||
if (argc != 2)
|
||||
{
|
||||
std::cerr << "Usage: clio_server "
|
||||
"<config_file> \n"
|
||||
<< "Example:\n"
|
||||
<< " clio_server config.json \n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (std::string{argv[1]} == "-v" || std::string{argv[1]} == "--version")
|
||||
{
|
||||
std::cout << Build::getClioFullVersionString() << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
auto const config = ConfigReader::open(argv[1]);
|
||||
auto const configPath = parseCli(argc, argv);
|
||||
auto const config = ConfigReader::open(configPath);
|
||||
if (!config)
|
||||
{
|
||||
std::cerr << "Couldnt parse config. Exiting..." << std::endl;
|
||||
std::cerr << "Couldnt parse config '" << configPath << "'."
|
||||
<< std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
initLogging(config);
|
||||
LogService::init(config);
|
||||
LogService::info() << "Clio version: " << Build::getClioFullVersionString();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Clio version: " << Build::getClioFullVersionString();
|
||||
|
||||
auto ctx = parse_certs(config);
|
||||
auto ctx = parseCerts(config);
|
||||
auto ctxRef = ctx
|
||||
? std::optional<std::reference_wrapper<ssl::context>>{ctx.value()}
|
||||
: std::nullopt;
|
||||
@@ -209,30 +191,29 @@ main(int argc, char* argv[])
|
||||
auto const threads = config.valueOr("io_threads", 2);
|
||||
if (threads <= 0)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(fatal) << "io_threads is less than 0";
|
||||
LogService::fatal() << "io_threads is less than 0";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << "Number of io threads = " << threads;
|
||||
LogService::info() << "Number of io threads = " << threads;
|
||||
|
||||
// io context to handle all incoming requests, as well as other things
|
||||
// IO context to handle all incoming requests, as well as other things
|
||||
// This is not the only io context in the application
|
||||
boost::asio::io_context ioc{threads};
|
||||
|
||||
// Rate limiter, to prevent abuse
|
||||
DOSGuard dosGuard{config, ioc};
|
||||
auto sweepHandler = IntervalSweepHandler{config, ioc};
|
||||
auto dosGuard = DOSGuard{config, sweepHandler};
|
||||
|
||||
// Interface to the database
|
||||
std::shared_ptr<BackendInterface> backend{
|
||||
Backend::make_Backend(ioc, config)};
|
||||
auto backend = Backend::make_Backend(ioc, config);
|
||||
|
||||
// Manages clients subscribed to streams
|
||||
std::shared_ptr<SubscriptionManager> subscriptions{
|
||||
SubscriptionManager::make_SubscriptionManager(config, backend)};
|
||||
auto subscriptions =
|
||||
SubscriptionManager::make_SubscriptionManager(config, backend);
|
||||
|
||||
// Tracks which ledgers have been validated by the
|
||||
// network
|
||||
std::shared_ptr<NetworkValidatedLedgers> ledgers{
|
||||
NetworkValidatedLedgers::make_ValidatedLedgers()};
|
||||
auto ledgers = NetworkValidatedLedgers::make_ValidatedLedgers();
|
||||
|
||||
// Handles the connection to one or more rippled nodes.
|
||||
// ETL uses the balancer to extract data.
|
||||
@@ -258,3 +239,7 @@ main(int argc, char* argv[])
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
LogService::fatal() << "Exit on exception: " << e.what();
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/Counters.h>
|
||||
#include <rpc/RPC.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RPC_COUNTERS_H
|
||||
#define RPC_COUNTERS_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/json.hpp>
|
||||
#include <chrono>
|
||||
@@ -53,5 +71,3 @@ public:
|
||||
};
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
#endif // RPC_COUNTERS_H
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/Errors.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -55,6 +74,13 @@ getErrorInfo(ClioError code)
|
||||
{ClioError::rpcMALFORMED_CURRENCY,
|
||||
"malformedCurrency",
|
||||
"Malformed currency."},
|
||||
{ClioError::rpcMALFORMED_REQUEST,
|
||||
"malformedRequest",
|
||||
"Malformed request."},
|
||||
{ClioError::rpcMALFORMED_OWNER, "malformedOwner", "Malformed owner."},
|
||||
{ClioError::rpcMALFORMED_ADDRESS,
|
||||
"malformedAddress",
|
||||
"Malformed address."},
|
||||
};
|
||||
|
||||
auto matchByCode = [code](auto const& info) { return info.code == code; };
|
||||
@@ -106,7 +132,7 @@ makeError(Status const& status)
|
||||
return str.empty() ? nullopt : make_optional(str);
|
||||
};
|
||||
|
||||
return visit(
|
||||
auto res = visit(
|
||||
overloadSet{
|
||||
[&status, &wrapOptional](RippledError err) {
|
||||
if (err == ripple::rpcUNKNOWN)
|
||||
@@ -130,6 +156,14 @@ makeError(Status const& status)
|
||||
},
|
||||
},
|
||||
status.code);
|
||||
if (status.extraInfo)
|
||||
{
|
||||
for (auto& [key, value] : status.extraInfo.value())
|
||||
{
|
||||
res[key] = value;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef REPORTING_RPC_ERRORS_H_INCLUDED
|
||||
#define REPORTING_RPC_ERRORS_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
|
||||
@@ -17,6 +35,9 @@ namespace RPC {
|
||||
*/
|
||||
enum class ClioError {
|
||||
rpcMALFORMED_CURRENCY = 5000,
|
||||
rpcMALFORMED_REQUEST = 5001,
|
||||
rpcMALFORMED_OWNER = 5002,
|
||||
rpcMALFORMED_ADDRESS = 5003,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -50,9 +71,12 @@ struct Status
|
||||
CombinedError code = RippledError::rpcSUCCESS;
|
||||
std::string error = "";
|
||||
std::string message = "";
|
||||
std::optional<boost::json::object> extraInfo;
|
||||
|
||||
Status() = default;
|
||||
/* implicit */ Status(CombinedError code) : code(code){};
|
||||
Status(CombinedError code, boost::json::object&& extraInfo)
|
||||
: code(code), extraInfo(std::move(extraInfo)){};
|
||||
|
||||
// HACK. Some rippled handlers explicitly specify errors.
|
||||
// This means that we have to be able to duplicate this
|
||||
@@ -81,6 +105,34 @@ struct Status
|
||||
return *err != RippledError::rpcSUCCESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the Status contains the desired @ref RippledError
|
||||
*
|
||||
* @param other The RippledError to match
|
||||
* @return bool true if status matches given error; false otherwise
|
||||
*/
|
||||
bool
|
||||
operator==(RippledError other) const
|
||||
{
|
||||
if (auto err = std::get_if<RippledError>(&code))
|
||||
return *err == other;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the Status contains the desired @ref ClioError
|
||||
*
|
||||
* @param other The RippledError to match
|
||||
* @return bool true if status matches given error; false otherwise
|
||||
*/
|
||||
bool
|
||||
operator==(ClioError other) const
|
||||
{
|
||||
if (auto err = std::get_if<ClioError>(&code))
|
||||
return *err == other;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -202,5 +254,3 @@ makeError(
|
||||
std::optional<std::string_view> customMessage = std::nullopt);
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
#endif // REPORTING_RPC_ERRORS_H_INCLUDED
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef REPORTING_HANDLERS_H_INCLUDED
|
||||
#define REPORTING_HANDLERS_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <rpc/RPC.h>
|
||||
|
||||
@@ -102,4 +120,3 @@ doServerInfo(Context const& context);
|
||||
Result
|
||||
doRandom(Context const& context);
|
||||
} // namespace RPC
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,24 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <etl/ETLSource.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/Handlers.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <webserver/HttpBase.h>
|
||||
@@ -9,9 +29,15 @@
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
using namespace clio;
|
||||
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gPerfLog{"Performance"};
|
||||
clio::Logger gLog{"RPC"};
|
||||
} // namespace
|
||||
|
||||
namespace RPC {
|
||||
|
||||
Context::Context(
|
||||
boost::asio::yield_context& yield_,
|
||||
string const& command_,
|
||||
@@ -40,7 +66,7 @@ Context::Context(
|
||||
, counters(counters_)
|
||||
, clientIp(clientIp_)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << tag() << "new Context created";
|
||||
gPerfLog.debug() << tag() << "new Context created";
|
||||
}
|
||||
|
||||
optional<Context>
|
||||
@@ -314,9 +340,6 @@ buildResponse(Context const& ctx)
|
||||
if (!res)
|
||||
return Status{RippledError::rpcFAILED_TO_FORWARD};
|
||||
|
||||
if (res->contains("result") && res->at("result").is_object())
|
||||
return res->at("result").as_object();
|
||||
|
||||
return *res;
|
||||
}
|
||||
|
||||
@@ -325,8 +348,7 @@ buildResponse(Context const& ctx)
|
||||
|
||||
if (ctx.backend->isTooBusy())
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__ << " Database is too busy. Rejecting request";
|
||||
gLog.error() << "Database is too busy. Rejecting request";
|
||||
return Status{RippledError::rpcTOO_BUSY};
|
||||
}
|
||||
|
||||
@@ -337,18 +359,16 @@ buildResponse(Context const& ctx)
|
||||
|
||||
try
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< ctx.tag() << __func__ << " start executing rpc `" << ctx.method
|
||||
<< '`';
|
||||
gPerfLog.debug() << ctx.tag() << " start executing rpc `" << ctx.method
|
||||
<< '`';
|
||||
auto v = (*method)(ctx);
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< ctx.tag() << __func__ << " finish executing rpc `" << ctx.method
|
||||
<< '`';
|
||||
gPerfLog.debug() << ctx.tag() << " finish executing rpc `" << ctx.method
|
||||
<< '`';
|
||||
|
||||
if (auto object = get_if<boost::json::object>(&v);
|
||||
object && not shouldSuppressValidatedFlag(ctx))
|
||||
{
|
||||
(*object)["validated"] = true;
|
||||
(*object)[JS(validated)] = true;
|
||||
}
|
||||
|
||||
return v;
|
||||
@@ -363,13 +383,12 @@ buildResponse(Context const& ctx)
|
||||
}
|
||||
catch (Backend::DatabaseTimeout const& t)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << __func__ << " Database timeout";
|
||||
gLog.error() << "Database timeout";
|
||||
return Status{RippledError::rpcTOO_BUSY};
|
||||
}
|
||||
catch (exception const& err)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< ctx.tag() << __func__ << " caught exception : " << err.what();
|
||||
gLog.error() << ctx.tag() << " caught exception: " << err.what();
|
||||
return Status{RippledError::rpcINTERNAL};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,33 @@
|
||||
#ifndef REPORTING_RPC_H_INCLUDED
|
||||
#define REPORTING_RPC_H_INCLUDED
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/Counters.h>
|
||||
#include <rpc/Errors.h>
|
||||
#include <util/Taggable.h>
|
||||
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/Counters.h>
|
||||
#include <util/Taggable.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
@@ -34,6 +52,7 @@ namespace RPC {
|
||||
|
||||
struct Context : public util::Taggable
|
||||
{
|
||||
clio::Logger perfLog_{"Performance"};
|
||||
boost::asio::yield_context& yield;
|
||||
std::string method;
|
||||
std::uint32_t version;
|
||||
@@ -72,13 +91,13 @@ struct AccountCursor
|
||||
std::uint32_t hint;
|
||||
|
||||
std::string
|
||||
toString()
|
||||
toString() const
|
||||
{
|
||||
return ripple::strHex(index) + "," + std::to_string(hint);
|
||||
}
|
||||
|
||||
bool
|
||||
isNonZero()
|
||||
isNonZero() const
|
||||
{
|
||||
return index.isNonZero() || hint != 0;
|
||||
}
|
||||
@@ -129,6 +148,7 @@ template <class T>
|
||||
void
|
||||
logDuration(Context const& ctx, T const& dur)
|
||||
{
|
||||
static clio::Logger log{"RPC"};
|
||||
std::stringstream ss;
|
||||
ss << ctx.tag() << "Request processing duration = "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(dur).count()
|
||||
@@ -136,13 +156,11 @@ logDuration(Context const& ctx, T const& dur)
|
||||
auto seconds =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(dur).count();
|
||||
if (seconds > 10)
|
||||
BOOST_LOG_TRIVIAL(error) << ss.str();
|
||||
log.error() << ss.str();
|
||||
else if (seconds > 1)
|
||||
BOOST_LOG_TRIVIAL(warning) << ss.str();
|
||||
log.warn() << ss.str();
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(info) << ss.str();
|
||||
log.info() << ss.str();
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
#endif // REPORTING_RPC_H_INCLUDED
|
||||
|
||||
@@ -1,10 +1,37 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
using namespace clio;
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gLog{"RPC"};
|
||||
} // namespace
|
||||
|
||||
namespace RPC {
|
||||
|
||||
@@ -74,35 +101,11 @@ getRequiredUInt(boost::json::object const& request, std::string const& field)
|
||||
throw InvalidParamsError("Missing field " + field);
|
||||
}
|
||||
|
||||
bool
|
||||
isOwnedByAccount(ripple::SLE const& sle, ripple::AccountID const& accountID)
|
||||
{
|
||||
if (sle.getType() == ripple::ltRIPPLE_STATE)
|
||||
{
|
||||
return (sle.getFieldAmount(ripple::sfLowLimit).getIssuer() ==
|
||||
accountID) ||
|
||||
(sle.getFieldAmount(ripple::sfHighLimit).getIssuer() == accountID);
|
||||
}
|
||||
else if (sle.isFieldPresent(ripple::sfAccount))
|
||||
{
|
||||
return sle.getAccountID(ripple::sfAccount) == accountID;
|
||||
}
|
||||
else if (sle.getType() == ripple::ltSIGNER_LIST)
|
||||
{
|
||||
ripple::Keylet const accountSignerList =
|
||||
ripple::keylet::signers(accountID);
|
||||
return sle.key() == accountSignerList.key;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<AccountCursor>
|
||||
parseAccountCursor(
|
||||
BackendInterface const& backend,
|
||||
std::uint32_t seq,
|
||||
std::optional<std::string> jsonCursor,
|
||||
ripple::AccountID const& accountID,
|
||||
boost::asio::yield_context& yield)
|
||||
{
|
||||
ripple::uint256 cursorIndex = beast::zero;
|
||||
@@ -133,19 +136,6 @@ parseAccountCursor(
|
||||
return {};
|
||||
}
|
||||
|
||||
// We then must check if the object pointed to by the marker is actually
|
||||
// owned by the account in the request.
|
||||
auto const ownedNode = backend.fetchLedgerObject(cursorIndex, seq, yield);
|
||||
|
||||
if (!ownedNode)
|
||||
return {};
|
||||
|
||||
ripple::SerialIter it{ownedNode->data(), ownedNode->size()};
|
||||
ripple::SLE sle{it, cursorIndex};
|
||||
|
||||
if (!isOwnedByAccount(sle, accountID))
|
||||
return {};
|
||||
|
||||
return AccountCursor({cursorIndex, startHint});
|
||||
}
|
||||
|
||||
@@ -412,12 +402,11 @@ deserializeTxPlusMeta(Backend::TransactionAndMetadata const& blobs)
|
||||
blobs.metadata.begin(),
|
||||
blobs.metadata.end(),
|
||||
std::ostream_iterator<unsigned char>(meta));
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< __func__
|
||||
<< " Failed to deserialize transaction. txn = " << txn.str()
|
||||
<< " - meta = " << meta.str()
|
||||
<< " txn length = " << std::to_string(blobs.transaction.size())
|
||||
<< " meta length = " << std::to_string(blobs.metadata.size());
|
||||
gLog.error() << "Failed to deserialize transaction. txn = " << txn.str()
|
||||
<< " - meta = " << meta.str() << " txn length = "
|
||||
<< std::to_string(blobs.transaction.size())
|
||||
<< " meta length = "
|
||||
<< std::to_string(blobs.metadata.size());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -657,13 +646,11 @@ traverseOwnedNodes(
|
||||
ripple::keylet::account(accountID).key, sequence, yield))
|
||||
return Status{RippledError::rpcACT_NOT_FOUND};
|
||||
|
||||
auto parsedCursor =
|
||||
parseAccountCursor(backend, sequence, jsonCursor, accountID, yield);
|
||||
|
||||
if (!parsedCursor)
|
||||
auto maybeCursor = parseAccountCursor(backend, sequence, jsonCursor, yield);
|
||||
if (!maybeCursor)
|
||||
return Status(ripple::rpcINVALID_PARAMS, "Malformed cursor");
|
||||
|
||||
auto [hexCursor, startHint] = *parsedCursor;
|
||||
auto [hexCursor, startHint] = *maybeCursor;
|
||||
|
||||
return traverseOwnedNodes(
|
||||
backend,
|
||||
@@ -709,22 +696,21 @@ traverseOwnedNodes(
|
||||
auto hintDir =
|
||||
backend.fetchLedgerObject(hintIndex.key, sequence, yield);
|
||||
|
||||
if (hintDir)
|
||||
{
|
||||
ripple::SerialIter it{hintDir->data(), hintDir->size()};
|
||||
ripple::SLE sle{it, hintIndex.key};
|
||||
if (!hintDir)
|
||||
return Status(ripple::rpcINVALID_PARAMS, "Invalid marker");
|
||||
|
||||
for (auto const& key : sle.getFieldV256(ripple::sfIndexes))
|
||||
{
|
||||
if (key == hexMarker)
|
||||
{
|
||||
// We found the hint, we can start here
|
||||
currentIndex = hintIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ripple::SerialIter it{hintDir->data(), hintDir->size()};
|
||||
ripple::SLE sle{it, hintIndex.key};
|
||||
|
||||
if (auto const& indexes = sle.getFieldV256(ripple::sfIndexes);
|
||||
std::find(std::begin(indexes), std::end(indexes), hexMarker) ==
|
||||
std::end(indexes))
|
||||
{
|
||||
// result in empty dataset
|
||||
return AccountCursor({beast::zero, 0});
|
||||
}
|
||||
|
||||
currentIndex = hintIndex;
|
||||
bool found = false;
|
||||
for (;;)
|
||||
{
|
||||
@@ -807,21 +793,17 @@ traverseOwnedNodes(
|
||||
}
|
||||
auto end = std::chrono::system_clock::now();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Time loading owned directories: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
gLog.debug() << "Time loading owned directories: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
|
||||
start = std::chrono::system_clock::now();
|
||||
auto objects = backend.fetchLedgerObjects(keys, sequence, yield);
|
||||
end = std::chrono::system_clock::now();
|
||||
auto [objects, timeDiff] = util::timed(
|
||||
[&]() { return backend.fetchLedgerObjects(keys, sequence, yield); });
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< "Time loading owned entries: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
gLog.debug() << "Time loading owned entries: " << timeDiff
|
||||
<< " milliseconds";
|
||||
|
||||
for (auto i = 0; i < objects.size(); ++i)
|
||||
{
|
||||
@@ -1341,7 +1323,7 @@ postProcessOrderBook(
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error) << "caught exception: " << e.what();
|
||||
gLog.error() << "caught exception: " << e.what();
|
||||
}
|
||||
}
|
||||
return jsonOffers;
|
||||
@@ -1351,46 +1333,48 @@ std::variant<Status, ripple::Book>
|
||||
parseBook(boost::json::object const& request)
|
||||
{
|
||||
if (!request.contains("taker_pays"))
|
||||
return Status{RippledError::rpcBAD_MARKET, "missingTakerPays"};
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "Missing field 'taker_pays'"};
|
||||
|
||||
if (!request.contains("taker_gets"))
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "missingTakerGets"};
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "Missing field 'taker_gets'"};
|
||||
|
||||
if (!request.at("taker_pays").is_object())
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "takerPaysNotObject"};
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS,
|
||||
"Field 'taker_pays' is not an object"};
|
||||
|
||||
if (!request.at("taker_gets").is_object())
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "takerGetsNotObject"};
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS,
|
||||
"Field 'taker_gets' is not an object"};
|
||||
|
||||
auto taker_pays = request.at("taker_pays").as_object();
|
||||
if (!taker_pays.contains("currency"))
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "missingTakerPaysCurrency"};
|
||||
return Status{RippledError::rpcSRC_CUR_MALFORMED};
|
||||
|
||||
if (!taker_pays.at("currency").is_string())
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "takerPaysCurrencyNotString"};
|
||||
return Status{RippledError::rpcSRC_CUR_MALFORMED};
|
||||
|
||||
auto taker_gets = request.at("taker_gets").as_object();
|
||||
if (!taker_gets.contains("currency"))
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "missingTakerGetsCurrency"};
|
||||
return Status{RippledError::rpcDST_AMT_MALFORMED};
|
||||
|
||||
if (!taker_gets.at("currency").is_string())
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "takerGetsCurrencyNotString"};
|
||||
RippledError::rpcDST_AMT_MALFORMED,
|
||||
};
|
||||
|
||||
ripple::Currency pay_currency;
|
||||
if (!ripple::to_currency(
|
||||
pay_currency, taker_pays.at("currency").as_string().c_str()))
|
||||
return Status{
|
||||
RippledError::rpcSRC_CUR_MALFORMED, "badTakerPaysCurrency"};
|
||||
return Status{RippledError::rpcSRC_CUR_MALFORMED};
|
||||
|
||||
ripple::Currency get_currency;
|
||||
if (!ripple::to_currency(
|
||||
get_currency, taker_gets["currency"].as_string().c_str()))
|
||||
return Status{
|
||||
RippledError::rpcDST_AMT_MALFORMED, "badTakerGetsCurrency"};
|
||||
return Status{RippledError::rpcDST_AMT_MALFORMED};
|
||||
|
||||
ripple::AccountID pay_issuer;
|
||||
if (taker_pays.contains("issuer"))
|
||||
@@ -1401,13 +1385,10 @@ parseBook(boost::json::object const& request)
|
||||
|
||||
if (!ripple::to_issuer(
|
||||
pay_issuer, taker_pays.at("issuer").as_string().c_str()))
|
||||
return Status{
|
||||
RippledError::rpcSRC_ISR_MALFORMED, "badTakerPaysIssuer"};
|
||||
return Status{RippledError::rpcSRC_ISR_MALFORMED};
|
||||
|
||||
if (pay_issuer == ripple::noAccount())
|
||||
return Status{
|
||||
RippledError::rpcSRC_ISR_MALFORMED,
|
||||
"badTakerPaysIssuerAccountOne"};
|
||||
return Status{RippledError::rpcSRC_ISR_MALFORMED};
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1573,47 +1554,52 @@ traverseTransactions(
|
||||
}
|
||||
|
||||
auto minIndex = context.range.minSequence;
|
||||
auto maxIndex = context.range.maxSequence;
|
||||
std::optional<int64_t> min;
|
||||
std::optional<int64_t> max;
|
||||
|
||||
if (request.contains(JS(ledger_index_min)))
|
||||
{
|
||||
auto& min = request.at(JS(ledger_index_min));
|
||||
|
||||
if (!min.is_int64())
|
||||
if (!request.at(JS(ledger_index_min)).is_int64())
|
||||
{
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "ledgerSeqMinNotNumber"};
|
||||
}
|
||||
|
||||
if (min.as_int64() != -1)
|
||||
min = request.at(JS(ledger_index_min)).as_int64();
|
||||
|
||||
if (*min != -1)
|
||||
{
|
||||
if (context.range.maxSequence < min.as_int64() ||
|
||||
context.range.minSequence > min.as_int64())
|
||||
if (context.range.maxSequence < *min ||
|
||||
context.range.minSequence > *min)
|
||||
return Status{
|
||||
RippledError::rpcLGR_IDX_MALFORMED,
|
||||
"ledgerSeqMinOutOfRange"};
|
||||
else
|
||||
minIndex = boost::json::value_to<std::uint32_t>(min);
|
||||
minIndex = static_cast<uint32_t>(*min);
|
||||
}
|
||||
|
||||
if (forward && !cursor)
|
||||
cursor = {minIndex, 0};
|
||||
}
|
||||
|
||||
auto maxIndex = context.range.maxSequence;
|
||||
if (request.contains(JS(ledger_index_max)))
|
||||
{
|
||||
auto& max = request.at(JS(ledger_index_max));
|
||||
|
||||
if (!max.is_int64())
|
||||
if (!request.at(JS(ledger_index_max)).is_int64())
|
||||
{
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "ledgerSeqMaxNotNumber"};
|
||||
}
|
||||
|
||||
if (max.as_int64() != -1)
|
||||
max = request.at(JS(ledger_index_max)).as_int64();
|
||||
|
||||
if (*max != -1)
|
||||
{
|
||||
if (context.range.maxSequence < max.as_int64() ||
|
||||
context.range.minSequence > max.as_int64())
|
||||
return Status{
|
||||
RippledError::rpcLGR_IDX_MALFORMED,
|
||||
"ledgerSeqMaxOutOfRange"};
|
||||
if (context.range.maxSequence < *max ||
|
||||
context.range.minSequence > *max)
|
||||
return Status{RippledError::rpcLGR_IDXS_INVALID};
|
||||
else
|
||||
maxIndex = boost::json::value_to<std::uint32_t>(max);
|
||||
maxIndex = static_cast<uint32_t>(*max);
|
||||
}
|
||||
|
||||
if (minIndex > maxIndex)
|
||||
@@ -1623,6 +1609,11 @@ traverseTransactions(
|
||||
cursor = {maxIndex, INT32_MAX};
|
||||
}
|
||||
|
||||
if (max && min && *max < *min)
|
||||
{
|
||||
return Status{RippledError::rpcLGR_IDXS_INVALID, "lgrIdxsInvalid"};
|
||||
}
|
||||
|
||||
if (request.contains(JS(ledger_index)) || request.contains(JS(ledger_hash)))
|
||||
{
|
||||
if (request.contains(JS(ledger_index_max)) ||
|
||||
@@ -1656,64 +1647,59 @@ traverseTransactions(
|
||||
boost::json::array txns;
|
||||
auto [blobs, retCursor] = transactionFetcher(
|
||||
context.backend, limit, forward, cursor, context.yield);
|
||||
auto serializationStart = std::chrono::system_clock::now();
|
||||
|
||||
if (retCursor)
|
||||
{
|
||||
boost::json::object cursorJson;
|
||||
cursorJson[JS(ledger)] = retCursor->ledgerSequence;
|
||||
cursorJson[JS(seq)] = retCursor->transactionIndex;
|
||||
response[JS(marker)] = cursorJson;
|
||||
}
|
||||
|
||||
for (auto const& txnPlusMeta : blobs)
|
||||
{
|
||||
if ((txnPlusMeta.ledgerSequence < minIndex && !forward) ||
|
||||
(txnPlusMeta.ledgerSequence > maxIndex && forward))
|
||||
auto timeDiff = util::timed([&, &retCursor = retCursor, &blobs = blobs]() {
|
||||
if (retCursor)
|
||||
{
|
||||
response.erase(JS(marker));
|
||||
break;
|
||||
}
|
||||
else if (txnPlusMeta.ledgerSequence > maxIndex && !forward)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__
|
||||
<< " skipping over transactions from incomplete ledger";
|
||||
continue;
|
||||
boost::json::object cursorJson;
|
||||
cursorJson[JS(ledger)] = retCursor->ledgerSequence;
|
||||
cursorJson[JS(seq)] = retCursor->transactionIndex;
|
||||
response[JS(marker)] = cursorJson;
|
||||
}
|
||||
|
||||
boost::json::object obj;
|
||||
|
||||
if (!binary)
|
||||
for (auto const& txnPlusMeta : blobs)
|
||||
{
|
||||
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
||||
obj[JS(meta)] = meta;
|
||||
obj[JS(tx)] = txn;
|
||||
obj[JS(tx)].as_object()[JS(ledger_index)] =
|
||||
txnPlusMeta.ledgerSequence;
|
||||
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
|
||||
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
|
||||
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
||||
obj[JS(date)] = txnPlusMeta.date;
|
||||
}
|
||||
obj[JS(validated)] = true;
|
||||
txns.push_back(obj);
|
||||
}
|
||||
if ((txnPlusMeta.ledgerSequence < minIndex && !forward) ||
|
||||
(txnPlusMeta.ledgerSequence > maxIndex && forward))
|
||||
{
|
||||
response.erase(JS(marker));
|
||||
break;
|
||||
}
|
||||
else if (txnPlusMeta.ledgerSequence > maxIndex && !forward)
|
||||
{
|
||||
gLog.debug()
|
||||
<< "Skipping over transactions from incomplete ledger";
|
||||
continue;
|
||||
}
|
||||
|
||||
response[JS(ledger_index_min)] = minIndex;
|
||||
response[JS(ledger_index_max)] = maxIndex;
|
||||
response[JS(transactions)] = txns;
|
||||
boost::json::object obj;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< __func__ << " serialization took "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - serializationStart)
|
||||
.count()
|
||||
<< " milliseconds";
|
||||
if (!binary)
|
||||
{
|
||||
auto [txn, meta] = toExpandedJson(txnPlusMeta);
|
||||
obj[JS(meta)] = meta;
|
||||
obj[JS(tx)] = txn;
|
||||
obj[JS(tx)].as_object()[JS(ledger_index)] =
|
||||
txnPlusMeta.ledgerSequence;
|
||||
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
||||
}
|
||||
else
|
||||
{
|
||||
obj[JS(meta)] = ripple::strHex(txnPlusMeta.metadata);
|
||||
obj[JS(tx_blob)] = ripple::strHex(txnPlusMeta.transaction);
|
||||
obj[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
||||
obj[JS(date)] = txnPlusMeta.date;
|
||||
}
|
||||
obj[JS(validated)] = true;
|
||||
txns.push_back(obj);
|
||||
}
|
||||
|
||||
response[JS(ledger_index_min)] = minIndex;
|
||||
response[JS(ledger_index_max)] = maxIndex;
|
||||
response[JS(transactions)] = txns;
|
||||
});
|
||||
gLog.info() << "serialization took " << timeDiff
|
||||
|
||||
<< " milliseconds";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef XRPL_REPORTING_RPCHELPERS_H_INCLUDED
|
||||
#define XRPL_REPORTING_RPCHELPERS_H_INCLUDED
|
||||
/*
|
||||
* This file contains a variety of utility functions used when executing
|
||||
* the handlers
|
||||
@@ -36,7 +54,6 @@ parseAccountCursor(
|
||||
BackendInterface const& backend,
|
||||
std::uint32_t seq,
|
||||
std::optional<std::string> jsonCursor,
|
||||
ripple::AccountID const& accountID,
|
||||
boost::asio::yield_context& yield);
|
||||
|
||||
// TODO this function should probably be in a different file and namespace
|
||||
@@ -273,4 +290,3 @@ computeBookChanges(
|
||||
std::vector<Backend::TransactionAndMetadata> const& transactions);
|
||||
|
||||
} // namespace RPC
|
||||
#endif
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/WorkQueue.h>
|
||||
|
||||
WorkQueue::WorkQueue(std::uint32_t numWorkers, uint32_t maxSize)
|
||||
|
||||
@@ -1,10 +1,29 @@
|
||||
#ifndef CLIO_WORK_QUEUE_H
|
||||
#define CLIO_WORK_QUEUE_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <log/Logger.h>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
#include <boost/json.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
@@ -20,6 +39,7 @@ class WorkQueue
|
||||
|
||||
std::atomic_uint64_t curSize_ = 0;
|
||||
uint32_t maxSize_ = std::numeric_limits<uint32_t>::max();
|
||||
clio::Logger log_{"RPC"};
|
||||
|
||||
public:
|
||||
WorkQueue(std::uint32_t numWorkers, uint32_t maxSize = 0);
|
||||
@@ -30,10 +50,8 @@ public:
|
||||
{
|
||||
if (curSize_ >= maxSize_ && !isWhiteListed)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< __func__
|
||||
<< " queue is full. rejecting job. current size = " << curSize_
|
||||
<< " max size = " << maxSize_;
|
||||
log_.warn() << "Queue is full. rejecting job. current size = "
|
||||
<< curSize_ << " max size = " << maxSize_;
|
||||
return false;
|
||||
}
|
||||
++curSize_;
|
||||
@@ -52,8 +70,8 @@ public:
|
||||
// durationUs_
|
||||
++queued_;
|
||||
durationUs_ += wait;
|
||||
BOOST_LOG_TRIVIAL(debug) << "WorkQueue wait time = " << wait
|
||||
<< " queue size = " << curSize_;
|
||||
log_.info() << "WorkQueue wait time = " << wait
|
||||
<< " queue size = " << curSize_;
|
||||
f(yield);
|
||||
--curSize_;
|
||||
});
|
||||
@@ -77,5 +95,3 @@ private:
|
||||
boost::asio::io_context ioc_ = {};
|
||||
std::optional<boost::asio::io_context::work> work_{ioc_};
|
||||
};
|
||||
|
||||
#endif // CLIO_WORK_QUEUE_H
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <boost/json.hpp>
|
||||
@@ -50,17 +69,12 @@ doAccountInfo(Context const& context)
|
||||
if (!accountID)
|
||||
return Status{RippledError::rpcACT_MALFORMED};
|
||||
|
||||
assert(accountID.has_value());
|
||||
|
||||
auto key = ripple::keylet::account(accountID.value());
|
||||
|
||||
std::optional<std::vector<unsigned char>> dbResponse =
|
||||
context.backend->fetchLedgerObject(key.key, lgrInfo.seq, context.yield);
|
||||
|
||||
if (!dbResponse)
|
||||
{
|
||||
return Status{RippledError::rpcACT_NOT_FOUND};
|
||||
}
|
||||
|
||||
ripple::STLedgerEntry sle{
|
||||
ripple::SerialIter{dbResponse->data(), dbResponse->size()}, key.key};
|
||||
@@ -68,12 +82,6 @@ doAccountInfo(Context const& context)
|
||||
if (!key.check(sle))
|
||||
return Status{RippledError::rpcDB_DESERIALIZATION};
|
||||
|
||||
// if (!binary)
|
||||
// response[JS(account_data)] = getJson(sle);
|
||||
// else
|
||||
// response[JS(account_data)] = ripple::strHex(*dbResponse);
|
||||
// response[JS(db_time)] = time;
|
||||
|
||||
response[JS(account_data)] = toJson(sle);
|
||||
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||
response[JS(ledger_index)] = lgrInfo.seq;
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/paths/TrustLine.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/paths/TrustLine.h>
|
||||
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||
@@ -202,8 +221,7 @@ doAccountObjects(Context const& context)
|
||||
if (auto status = std::get_if<RPC::Status>(&next))
|
||||
return *status;
|
||||
|
||||
auto nextMarker = std::get<RPC::AccountCursor>(next);
|
||||
|
||||
auto const& nextMarker = std::get<RPC::AccountCursor>(next);
|
||||
if (nextMarker.isNonZero())
|
||||
response[JS(marker)] = nextMarker.toString();
|
||||
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/app/paths/TrustLine.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gLog{"RPC"};
|
||||
} // namespace
|
||||
|
||||
namespace RPC {
|
||||
|
||||
@@ -18,15 +46,13 @@ doAccountTx(Context const& context)
|
||||
bool const forward,
|
||||
std::optional<Backend::TransactionsCursor> const& cursorIn,
|
||||
boost::asio::yield_context& yield) {
|
||||
auto const start = std::chrono::system_clock::now();
|
||||
auto const txnsAndCursor = backend->fetchAccountTransactions(
|
||||
accountID, limit, forward, cursorIn, yield);
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< outerFuncName << " db fetch took "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count()
|
||||
<< " milliseconds - num blobs = " << txnsAndCursor.txns.size();
|
||||
auto [txnsAndCursor, timeDiff] = util::timed([&]() {
|
||||
return backend->fetchAccountTransactions(
|
||||
accountID, limit, forward, cursorIn, yield);
|
||||
});
|
||||
gLog.info() << outerFuncName << " db fetch took " << timeDiff
|
||||
<< " milliseconds - num blobs = "
|
||||
<< txnsAndCursor.txns.size();
|
||||
return txnsAndCursor;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/basics/ToString.h>
|
||||
|
||||
|
||||
@@ -1,15 +1,43 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <boost/json.hpp>
|
||||
#include <algorithm>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gLog{"RPC"};
|
||||
} // namespace
|
||||
|
||||
namespace RPC {
|
||||
|
||||
@@ -64,11 +92,11 @@ doBookOffers(Context const& context)
|
||||
bookBase, lgrInfo.seq, limit, marker, context.yield);
|
||||
auto end = std::chrono::system_clock::now();
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "Time loading books: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end - start)
|
||||
.count()
|
||||
<< " milliseconds - request = " << request;
|
||||
gLog.warn() << "Time loading books: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end - start)
|
||||
.count()
|
||||
<< " milliseconds - request = " << request;
|
||||
|
||||
response[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||
response[JS(ledger_index)] = lgrInfo.seq;
|
||||
@@ -78,11 +106,11 @@ doBookOffers(Context const& context)
|
||||
|
||||
auto end2 = std::chrono::system_clock::now();
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< "Time transforming to json: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(end2 - end)
|
||||
.count()
|
||||
<< " milliseconds - request = " << request;
|
||||
gLog.warn() << "Time transforming to json: "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
end2 - end)
|
||||
.count()
|
||||
<< " milliseconds - request = " << request;
|
||||
|
||||
if (retMarker)
|
||||
response["marker"] = ripple::strHex(*retMarker);
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/PayChan.h>
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/PayChan.h>
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
@@ -25,7 +44,7 @@ doGatewayBalances(Context const& context)
|
||||
std::map<ripple::AccountID, std::vector<ripple::STAmount>> frozenBalances;
|
||||
std::set<ripple::AccountID> hotWallets;
|
||||
|
||||
if (request.contains("hot_wallet"))
|
||||
if (request.contains(JS(hotwallet)))
|
||||
{
|
||||
auto getAccountID =
|
||||
[](auto const& j) -> std::optional<ripple::AccountID> {
|
||||
@@ -44,7 +63,7 @@ doGatewayBalances(Context const& context)
|
||||
return {};
|
||||
};
|
||||
|
||||
auto const& hw = request.at("hot_wallet");
|
||||
auto const& hw = request.at(JS(hotwallet));
|
||||
bool valid = true;
|
||||
|
||||
// null is treated as a valid 0-sized array of hotwallet
|
||||
@@ -110,7 +129,7 @@ doGatewayBalances(Context const& context)
|
||||
if (hotWallets.count(peer) > 0)
|
||||
{
|
||||
// This is a specified hot wallet
|
||||
hotBalances[peer].push_back(balance);
|
||||
hotBalances[peer].push_back(-balance);
|
||||
}
|
||||
else if (balSign > 0)
|
||||
{
|
||||
@@ -120,7 +139,7 @@ doGatewayBalances(Context const& context)
|
||||
else if (freeze)
|
||||
{
|
||||
// An obligation the gateway has frozen
|
||||
frozenBalances[peer].push_back(balance);
|
||||
frozenBalances[peer].push_back(-balance);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/LedgerToJson.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
// Get state nodes from a ledger
|
||||
// Inputs:
|
||||
// limit: integer, maximum number of entries
|
||||
@@ -18,6 +39,13 @@
|
||||
//
|
||||
//
|
||||
|
||||
using namespace clio;
|
||||
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gLog{"RPC"};
|
||||
} // namespace
|
||||
|
||||
namespace RPC {
|
||||
|
||||
using boost::json::value_to;
|
||||
@@ -65,7 +93,7 @@ doLedgerData(Context const& context)
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << __func__ << " : parsing marker";
|
||||
gLog.debug() << "Parsing marker";
|
||||
|
||||
marker = ripple::uint256{};
|
||||
if (!marker->parseHex(request.at(JS(marker)).as_string().c_str()))
|
||||
@@ -98,7 +126,6 @@ doLedgerData(Context const& context)
|
||||
header[JS(close_time_human)] = ripple::to_string(lgrInfo.closeTime);
|
||||
header[JS(close_time_resolution)] =
|
||||
lgrInfo.closeTimeResolution.count();
|
||||
header[JS(closed)] = true;
|
||||
header[JS(hash)] = ripple::strHex(lgrInfo.hash);
|
||||
header[JS(ledger_hash)] = ripple::strHex(lgrInfo.hash);
|
||||
header[JS(ledger_index)] = std::to_string(lgrInfo.seq);
|
||||
@@ -111,6 +138,7 @@ doLedgerData(Context const& context)
|
||||
header[JS(transaction_hash)] = ripple::strHex(lgrInfo.txHash);
|
||||
}
|
||||
|
||||
header[JS(closed)] = true;
|
||||
response[JS(ledger)] = header;
|
||||
}
|
||||
else
|
||||
@@ -168,9 +196,8 @@ doLedgerData(Context const& context)
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start)
|
||||
.count();
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " number of results = " << results.size()
|
||||
<< " fetched in " << time << " microseconds";
|
||||
gLog.debug() << "Number of results = " << results.size() << " fetched in "
|
||||
<< time << " microseconds";
|
||||
boost::json::array objects;
|
||||
objects.reserve(results.size());
|
||||
for (auto const& [key, object] : results)
|
||||
@@ -194,9 +221,8 @@ doLedgerData(Context const& context)
|
||||
|
||||
time = std::chrono::duration_cast<std::chrono::microseconds>(end2 - end)
|
||||
.count();
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " number of results = " << results.size()
|
||||
<< " serialized in " << time << " microseconds";
|
||||
gLog.debug() << "Number of results = " << results.size()
|
||||
<< " serialized in " << time << " microseconds";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <boost/json.hpp>
|
||||
@@ -29,13 +48,16 @@ doLedgerEntry(Context const& context)
|
||||
auto lgrInfo = std::get<ripple::LedgerInfo>(v);
|
||||
|
||||
ripple::uint256 key;
|
||||
|
||||
// Note: according to docs, only 1 of the below should be specified at any
|
||||
// time. see https://xrpl.org/ledger_entry.html#ledger_entry
|
||||
if (request.contains(JS(index)))
|
||||
{
|
||||
if (!request.at(JS(index)).is_string())
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "indexNotString"};
|
||||
|
||||
if (!key.parseHex(request.at(JS(index)).as_string().c_str()))
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "malformedIndex"};
|
||||
return Status{ClioError::rpcMALFORMED_REQUEST};
|
||||
}
|
||||
else if (request.contains(JS(account_root)))
|
||||
{
|
||||
@@ -46,7 +68,7 @@ doLedgerEntry(Context const& context)
|
||||
auto const account = ripple::parseBase58<ripple::AccountID>(
|
||||
request.at(JS(account_root)).as_string().c_str());
|
||||
if (!account || account->isZero())
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||
return Status{ClioError::rpcMALFORMED_ADDRESS};
|
||||
else
|
||||
key = ripple::keylet::account(*account).key;
|
||||
}
|
||||
@@ -172,8 +194,7 @@ doLedgerEntry(Context const& context)
|
||||
|
||||
if (!ownerID)
|
||||
{
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||
return Status{ClioError::rpcMALFORMED_ADDRESS};
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -219,8 +240,7 @@ doLedgerEntry(Context const& context)
|
||||
.c_str());
|
||||
|
||||
if (!id)
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||
return Status{ClioError::rpcMALFORMED_ADDRESS};
|
||||
else
|
||||
{
|
||||
std::uint32_t seq =
|
||||
@@ -256,8 +276,7 @@ doLedgerEntry(Context const& context)
|
||||
offer.at(JS(account)).as_string().c_str());
|
||||
|
||||
if (!id)
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedAddress"};
|
||||
return Status{ClioError::rpcMALFORMED_ADDRESS};
|
||||
else
|
||||
{
|
||||
std::uint32_t seq =
|
||||
@@ -310,7 +329,7 @@ doLedgerEntry(Context const& context)
|
||||
|
||||
if (!id1 || !id2)
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedAddresses"};
|
||||
ClioError::rpcMALFORMED_ADDRESS, "malformedAddresses"};
|
||||
|
||||
else if (!ripple::to_currency(
|
||||
currency, state.at(JS(currency)).as_string().c_str()))
|
||||
@@ -325,24 +344,24 @@ doLedgerEntry(Context const& context)
|
||||
{
|
||||
if (!request.at(JS(ticket)).is_string())
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "ticketNotString"};
|
||||
ClioError::rpcMALFORMED_REQUEST, "ticketNotString"};
|
||||
|
||||
if (!key.parseHex(request.at(JS(ticket)).as_string().c_str()))
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedTicket"};
|
||||
ClioError::rpcMALFORMED_REQUEST, "malformedTicket"};
|
||||
}
|
||||
else if (
|
||||
!request.at(JS(ticket)).as_object().contains(JS(owner)) ||
|
||||
!request.at(JS(ticket)).as_object().at(JS(owner)).is_string())
|
||||
{
|
||||
return Status{RippledError::rpcINVALID_PARAMS, "malformedOwner"};
|
||||
return Status{ClioError::rpcMALFORMED_REQUEST};
|
||||
}
|
||||
else if (
|
||||
!request.at(JS(ticket)).as_object().contains(JS(ticket_seq)) ||
|
||||
!request.at(JS(ticket)).as_object().at(JS(ticket_seq)).is_int64())
|
||||
{
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedTicketSeq"};
|
||||
ClioError::rpcMALFORMED_REQUEST, "malformedTicketSeq"};
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -354,8 +373,7 @@ doLedgerEntry(Context const& context)
|
||||
.c_str());
|
||||
|
||||
if (!id)
|
||||
return Status{
|
||||
RippledError::rpcINVALID_PARAMS, "malformedOwner"};
|
||||
return Status{ClioError::rpcMALFORMED_OWNER};
|
||||
else
|
||||
{
|
||||
std::uint32_t seq = request.at(JS(offer))
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
@@ -1,4 +1,32 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
using namespace clio;
|
||||
|
||||
// local to compilation unit loggers
|
||||
namespace {
|
||||
clio::Logger gLog{"RPC"};
|
||||
} // namespace
|
||||
|
||||
namespace RPC {
|
||||
|
||||
@@ -20,15 +48,14 @@ doNFTHistory(Context const& context)
|
||||
std::optional<Backend::TransactionsCursor> const& cursorIn,
|
||||
boost::asio::yield_context& yield)
|
||||
-> Backend::TransactionsAndCursor {
|
||||
auto const start = std::chrono::system_clock::now();
|
||||
auto const txnsAndCursor = backend->fetchNFTTransactions(
|
||||
tokenID, limit, forward, cursorIn, yield);
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< outerFuncName << " db fetch took "
|
||||
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count()
|
||||
<< " milliseconds - num blobs = " << txnsAndCursor.txns.size();
|
||||
auto const [txnsAndCursor, timeDiff] =
|
||||
util::timed([&, &tokenID = tokenID]() {
|
||||
return backend->fetchNFTTransactions(
|
||||
tokenID, limit, forward, cursorIn, yield);
|
||||
});
|
||||
gLog.info() << outerFuncName << " db fetch took " << timeDiff
|
||||
<< " milliseconds - num blobs = "
|
||||
<< txnsAndCursor.txns.size();
|
||||
return txnsAndCursor;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/tx/impl/details/NFTokenUtils.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <boost/json.hpp>
|
||||
|
||||
@@ -1,13 +1,34 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
#include <ripple/basics/StringUtilities.h>
|
||||
#include <ripple/protocol/ErrorCodes.h>
|
||||
#include <ripple/protocol/Indexes.h>
|
||||
#include <ripple/protocol/STLedgerEntry.h>
|
||||
#include <ripple/protocol/jss.h>
|
||||
#include <boost/json.hpp>
|
||||
#include <algorithm>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace json = boost::json;
|
||||
|
||||
namespace ripple {
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <ripple/protocol/TxFlags.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
// rngfill.h doesn't compile without this include
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <ripple/beast/utility/rngfill.h>
|
||||
#include <ripple/crypto/csprng.h>
|
||||
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
namespace RPC {
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <etl/ETLSource.h>
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <boost/json.hpp>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <webserver/WsBase.h>
|
||||
@@ -442,8 +461,7 @@ doUnsubscribe(Context const& context)
|
||||
if (request.contains("books"))
|
||||
unsubscribeToBooks(books, context.session, *context.subscriptions);
|
||||
|
||||
boost::json::object response = {{"status", "success"}};
|
||||
return response;
|
||||
return boost::json::object{};
|
||||
}
|
||||
|
||||
} // namespace RPC
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
namespace RPC {
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
|
||||
@@ -31,6 +50,17 @@ doTx(Context const& context)
|
||||
|
||||
binary = request.at(JS(binary)).as_bool();
|
||||
}
|
||||
auto minLedger = getUInt(request, JS(min_ledger));
|
||||
auto maxLedger = getUInt(request, JS(max_ledger));
|
||||
bool rangeSupplied = minLedger && maxLedger;
|
||||
|
||||
if (rangeSupplied)
|
||||
{
|
||||
if (*minLedger > *maxLedger)
|
||||
return Status{RippledError::rpcINVALID_LGR_RANGE};
|
||||
if (*maxLedger - *minLedger > 1000)
|
||||
return Status{RippledError::rpcEXCESSIVE_LGR_RANGE};
|
||||
}
|
||||
|
||||
auto range = context.backend->fetchLedgerRange();
|
||||
if (!range)
|
||||
@@ -38,7 +68,17 @@ doTx(Context const& context)
|
||||
|
||||
auto dbResponse = context.backend->fetchTransaction(hash, context.yield);
|
||||
if (!dbResponse)
|
||||
{
|
||||
if (rangeSupplied)
|
||||
{
|
||||
bool searchedAll = range->maxSequence >= *maxLedger &&
|
||||
range->minSequence <= *minLedger;
|
||||
boost::json::object extra;
|
||||
extra["searched_all"] = searchedAll;
|
||||
return Status{RippledError::rpcTXN_NOT_FOUND, std::move(extra)};
|
||||
}
|
||||
return Status{RippledError::rpcTXN_NOT_FOUND};
|
||||
}
|
||||
|
||||
if (!binary)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef CLIO_SUBSCRIPTION_MESSAGE_H
|
||||
#define CLIO_SUBSCRIPTION_MESSAGE_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -36,5 +54,3 @@ public:
|
||||
return message_.size();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CLIO_SUBSCRIPTION_MESSAGE_H
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <webserver/WsBase.h>
|
||||
@@ -155,7 +174,9 @@ SubscriptionManager::subLedger(
|
||||
boost::asio::yield_context& yield,
|
||||
std::shared_ptr<WsBase> session)
|
||||
{
|
||||
ledgerSubscribers_.subscribe(session);
|
||||
subscribeHelper(session, ledgerSubscribers_, [this](session_ptr session) {
|
||||
unsubLedger(session);
|
||||
});
|
||||
|
||||
auto ledgerRange = backend_->fetchLedgerRange();
|
||||
assert(ledgerRange);
|
||||
@@ -185,7 +206,9 @@ SubscriptionManager::unsubLedger(std::shared_ptr<WsBase> session)
|
||||
void
|
||||
SubscriptionManager::subTransactions(std::shared_ptr<WsBase> session)
|
||||
{
|
||||
txSubscribers_.subscribe(session);
|
||||
subscribeHelper(session, txSubscribers_, [this](session_ptr session) {
|
||||
unsubTransactions(session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -199,12 +222,13 @@ SubscriptionManager::subAccount(
|
||||
ripple::AccountID const& account,
|
||||
std::shared_ptr<WsBase>& session)
|
||||
{
|
||||
accountSubscribers_.subscribe(session, account);
|
||||
|
||||
std::unique_lock lk(cleanupMtx_);
|
||||
cleanupFuncs_[session].emplace_back([this, account](session_ptr session) {
|
||||
unsubAccount(account, session);
|
||||
});
|
||||
subscribeHelper(
|
||||
session,
|
||||
account,
|
||||
accountSubscribers_,
|
||||
[this, account](session_ptr session) {
|
||||
unsubAccount(account, session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -220,11 +244,10 @@ SubscriptionManager::subBook(
|
||||
ripple::Book const& book,
|
||||
std::shared_ptr<WsBase> session)
|
||||
{
|
||||
bookSubscribers_.subscribe(session, book);
|
||||
|
||||
std::unique_lock lk(cleanupMtx_);
|
||||
cleanupFuncs_[session].emplace_back(
|
||||
[this, book](session_ptr session) { unsubBook(book, session); });
|
||||
subscribeHelper(
|
||||
session, book, bookSubscribers_, [this, book](session_ptr session) {
|
||||
unsubBook(book, session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -238,11 +261,10 @@ SubscriptionManager::unsubBook(
|
||||
void
|
||||
SubscriptionManager::subBookChanges(std::shared_ptr<WsBase> session)
|
||||
{
|
||||
bookChangesSubscribers_.subscribe(session);
|
||||
|
||||
std::unique_lock lk(cleanupMtx_);
|
||||
cleanupFuncs_[session].emplace_back(
|
||||
[this](session_ptr session) { unsubBookChanges(session); });
|
||||
subscribeHelper(
|
||||
session, bookChangesSubscribers_, [this](session_ptr session) {
|
||||
unsubBookChanges(session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -408,13 +430,21 @@ SubscriptionManager::subProposedAccount(
|
||||
ripple::AccountID const& account,
|
||||
std::shared_ptr<WsBase> session)
|
||||
{
|
||||
accountProposedSubscribers_.subscribe(session, account);
|
||||
subscribeHelper(
|
||||
session,
|
||||
account,
|
||||
accountProposedSubscribers_,
|
||||
[this, account](session_ptr session) {
|
||||
unsubProposedAccount(account, session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
SubscriptionManager::subManifest(std::shared_ptr<WsBase> session)
|
||||
{
|
||||
manifestSubscribers_.subscribe(session);
|
||||
subscribeHelper(session, manifestSubscribers_, [this](session_ptr session) {
|
||||
unsubManifest(session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -426,7 +456,10 @@ SubscriptionManager::unsubManifest(std::shared_ptr<WsBase> session)
|
||||
void
|
||||
SubscriptionManager::subValidation(std::shared_ptr<WsBase> session)
|
||||
{
|
||||
validationsSubscribers_.subscribe(session);
|
||||
subscribeHelper(
|
||||
session, validationsSubscribers_, [this](session_ptr session) {
|
||||
unsubValidation(session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -446,7 +479,10 @@ SubscriptionManager::unsubProposedAccount(
|
||||
void
|
||||
SubscriptionManager::subProposedTransactions(std::shared_ptr<WsBase> session)
|
||||
{
|
||||
txProposedSubscribers_.subscribe(session);
|
||||
subscribeHelper(
|
||||
session, txProposedSubscribers_, [this](session_ptr session) {
|
||||
unsubProposedTransactions(session);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
@@ -454,6 +490,28 @@ SubscriptionManager::unsubProposedTransactions(std::shared_ptr<WsBase> session)
|
||||
{
|
||||
txProposedSubscribers_.unsubscribe(session);
|
||||
}
|
||||
void
|
||||
SubscriptionManager::subscribeHelper(
|
||||
std::shared_ptr<WsBase>& session,
|
||||
Subscription& subs,
|
||||
CleanupFunction&& func)
|
||||
{
|
||||
subs.subscribe(session);
|
||||
std::unique_lock lk(cleanupMtx_);
|
||||
cleanupFuncs_[session].push_back(std::move(func));
|
||||
}
|
||||
template <typename Key>
|
||||
void
|
||||
SubscriptionManager::subscribeHelper(
|
||||
std::shared_ptr<WsBase>& session,
|
||||
Key const& k,
|
||||
SubscriptionMap<Key>& subs,
|
||||
CleanupFunction&& func)
|
||||
{
|
||||
subs.subscribe(session, k);
|
||||
std::unique_lock lk(cleanupMtx_);
|
||||
cleanupFuncs_[session].push_back(std::move(func));
|
||||
}
|
||||
|
||||
void
|
||||
SubscriptionManager::cleanup(std::shared_ptr<WsBase> session)
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
#ifndef SUBSCRIPTION_MANAGER_H
|
||||
#define SUBSCRIPTION_MANAGER_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <config/Config.h>
|
||||
#include <memory>
|
||||
#include <log/Logger.h>
|
||||
#include <subscriptions/Message.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class WsBase;
|
||||
|
||||
class Subscription
|
||||
@@ -87,6 +107,7 @@ public:
|
||||
class SubscriptionManager
|
||||
{
|
||||
using session_ptr = std::shared_ptr<WsBase>;
|
||||
clio::Logger log_{"Subscriptions"};
|
||||
|
||||
std::vector<std::thread> workers_;
|
||||
boost::asio::io_context ioc_;
|
||||
@@ -134,8 +155,8 @@ public:
|
||||
// We will eventually want to clamp this to be the number of strands,
|
||||
// since adding more threads than we have strands won't see any
|
||||
// performance benefits
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting subscription manager with "
|
||||
<< numThreads << " workers";
|
||||
log_.info() << "Starting subscription manager with " << numThreads
|
||||
<< " workers";
|
||||
|
||||
workers_.reserve(numThreads);
|
||||
for (auto i = numThreads; i > 0; --i)
|
||||
@@ -256,16 +277,29 @@ private:
|
||||
void
|
||||
sendAll(std::string const& pubMsg, std::unordered_set<session_ptr>& subs);
|
||||
|
||||
using CleanupFunction = std::function<void(session_ptr)>;
|
||||
|
||||
void
|
||||
subscribeHelper(
|
||||
std::shared_ptr<WsBase>& session,
|
||||
Subscription& subs,
|
||||
CleanupFunction&& func);
|
||||
|
||||
template <typename Key>
|
||||
void
|
||||
subscribeHelper(
|
||||
std::shared_ptr<WsBase>& session,
|
||||
Key const& k,
|
||||
SubscriptionMap<Key>& subs,
|
||||
CleanupFunction&& func);
|
||||
|
||||
/**
|
||||
* This is how we chose to cleanup subscriptions that have been closed.
|
||||
* Each time we add a subscriber, we add the opposite lambda that
|
||||
* unsubscribes that subscriber when cleanup is called with the session that
|
||||
* closed.
|
||||
*/
|
||||
using CleanupFunction = std::function<void(session_ptr)>;
|
||||
std::mutex cleanupMtx_;
|
||||
std::unordered_map<session_ptr, std::vector<CleanupFunction>>
|
||||
cleanupFuncs_ = {};
|
||||
};
|
||||
|
||||
#endif // SUBSCRIPTION_MANAGER_H
|
||||
|
||||
59
src/util/Profiler.h
Normal file
59
src/util/Profiler.h
Normal file
@@ -0,0 +1,59 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace util {
|
||||
|
||||
/**
|
||||
* @brief Profiler function to measure the time consuming
|
||||
* @param func function object, can be a lamdba or function wrapper
|
||||
* @return return a pair if function wrapper has return value: result of
|
||||
* function wrapper and the elapsed time(ms) during executing the given
|
||||
* function only return the elapsed time if function wrapper does not have
|
||||
* return value
|
||||
*/
|
||||
template <typename U = std::chrono::milliseconds, typename F>
|
||||
[[nodiscard]] auto
|
||||
timed(F&& func)
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
if constexpr (std::is_same_v<decltype(func()), void>)
|
||||
{
|
||||
func();
|
||||
return std::chrono::duration_cast<U>(
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ret = func();
|
||||
auto elapsed = std::chrono::duration_cast<U>(
|
||||
std::chrono::system_clock::now() - start)
|
||||
.count();
|
||||
return std::make_pair(ret, elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <util/Taggable.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_UTIL_TAGDECORATOR_H
|
||||
#define RIPPLE_UTIL_TAGDECORATOR_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/json.hpp>
|
||||
@@ -209,7 +227,8 @@ private:
|
||||
friend Type
|
||||
tag_invoke(boost::json::value_to_tag<Type>, boost::json::value const& value)
|
||||
{
|
||||
assert(value.is_string());
|
||||
if (not value.is_string())
|
||||
throw std::runtime_error("`log_tag_style` must be a string");
|
||||
auto const& style = value.as_string();
|
||||
|
||||
if (boost::iequals(style, "int") || boost::iequals(style, "uint"))
|
||||
@@ -258,5 +277,3 @@ public:
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
#endif // RIPPLE_UTIL_TAGDECORATOR_H
|
||||
|
||||
@@ -1,93 +1,239 @@
|
||||
#ifndef RIPPLE_REPORTING_DOS_GUARD_H
|
||||
#define RIPPLE_REPORTING_DOS_GUARD_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <config/Config.h>
|
||||
|
||||
class DOSGuard
|
||||
namespace clio {
|
||||
|
||||
class BaseDOSGuard
|
||||
{
|
||||
boost::asio::io_context& ctx_;
|
||||
std::mutex mtx_; // protects ipFetchCount_
|
||||
std::unordered_map<std::string, std::uint32_t> ipFetchCount_;
|
||||
public:
|
||||
virtual ~BaseDOSGuard() = default;
|
||||
|
||||
virtual void
|
||||
clear() noexcept = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A simple denial of service guard used for rate limiting.
|
||||
*
|
||||
* @tparam SweepHandler Type of the sweep handler
|
||||
*/
|
||||
template <typename SweepHandler>
|
||||
class BasicDOSGuard : public BaseDOSGuard
|
||||
{
|
||||
// Accumulated state per IP, state will be reset accordingly
|
||||
struct ClientState
|
||||
{
|
||||
// accumulated transfered byte
|
||||
std::uint32_t transferedByte = 0;
|
||||
// accumulated served requests count
|
||||
std::uint32_t requestsCount = 0;
|
||||
};
|
||||
|
||||
mutable std::mutex mtx_;
|
||||
// accumulated states map
|
||||
std::unordered_map<std::string, ClientState> ipState_;
|
||||
std::unordered_map<std::string, std::uint32_t> ipConnCount_;
|
||||
std::unordered_set<std::string> const whitelist_;
|
||||
|
||||
std::uint32_t const maxFetches_;
|
||||
std::uint32_t const sweepInterval_;
|
||||
std::uint32_t const maxConnCount_;
|
||||
std::uint32_t const maxRequestCount_;
|
||||
clio::Logger log_{"RPC"};
|
||||
|
||||
public:
|
||||
DOSGuard(clio::Config const& config, boost::asio::io_context& ctx)
|
||||
: ctx_{ctx}
|
||||
, whitelist_{getWhitelist(config)}
|
||||
, maxFetches_{config.valueOr("dos_guard.max_fetches", 100u)}
|
||||
, sweepInterval_{config.valueOr("dos_guard.sweep_interval", 1u)}
|
||||
/**
|
||||
* @brief Constructs a new DOS guard.
|
||||
*
|
||||
* @param config Clio config
|
||||
* @param sweepHandler Sweep handler that implements the sweeping behaviour
|
||||
*/
|
||||
BasicDOSGuard(clio::Config const& config, SweepHandler& sweepHandler)
|
||||
: whitelist_{getWhitelist(config)}
|
||||
, maxFetches_{config.valueOr("dos_guard.max_fetches", 100000000u)}
|
||||
, maxConnCount_{config.valueOr("dos_guard.max_connections", 1u)}
|
||||
, maxRequestCount_{config.valueOr("dos_guard.max_requests", 10u)}
|
||||
{
|
||||
createTimer();
|
||||
sweepHandler.setup(this);
|
||||
}
|
||||
|
||||
void
|
||||
createTimer()
|
||||
{
|
||||
auto wait = std::chrono::seconds(sweepInterval_);
|
||||
std::shared_ptr<boost::asio::steady_timer> timer =
|
||||
std::make_shared<boost::asio::steady_timer>(
|
||||
ctx_, std::chrono::steady_clock::now() + wait);
|
||||
timer->async_wait(
|
||||
[timer, this](const boost::system::error_code& error) {
|
||||
clear();
|
||||
createTimer();
|
||||
});
|
||||
}
|
||||
|
||||
bool
|
||||
isWhiteListed(std::string const& ip)
|
||||
/**
|
||||
* @brief Check whether an ip address is in the whitelist or not
|
||||
*
|
||||
* @param ip The ip address to check
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isWhiteListed(std::string const& ip) const noexcept
|
||||
{
|
||||
return whitelist_.contains(ip);
|
||||
}
|
||||
|
||||
bool
|
||||
isOk(std::string const& ip)
|
||||
{
|
||||
if (whitelist_.contains(ip))
|
||||
return true;
|
||||
|
||||
std::unique_lock lck(mtx_);
|
||||
auto it = ipFetchCount_.find(ip);
|
||||
if (it == ipFetchCount_.end())
|
||||
return true;
|
||||
|
||||
return it->second < maxFetches_;
|
||||
}
|
||||
|
||||
bool
|
||||
add(std::string const& ip, uint32_t numObjects)
|
||||
/**
|
||||
* @brief Check whether an ip address is currently rate limited or not
|
||||
*
|
||||
* @param ip The ip address to check
|
||||
* @return true If not rate limited
|
||||
* @return false If rate limited and the request should not be processed
|
||||
*/
|
||||
[[nodiscard]] bool
|
||||
isOk(std::string const& ip) const noexcept
|
||||
{
|
||||
if (whitelist_.contains(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::unique_lock lck(mtx_);
|
||||
auto it = ipFetchCount_.find(ip);
|
||||
if (it == ipFetchCount_.end())
|
||||
ipFetchCount_[ip] = numObjects;
|
||||
else
|
||||
it->second += numObjects;
|
||||
if (ipState_.find(ip) != ipState_.end())
|
||||
{
|
||||
auto [transferedByte, requests] = ipState_.at(ip);
|
||||
if (transferedByte > maxFetches_ || requests > maxRequestCount_)
|
||||
{
|
||||
log_.warn()
|
||||
<< "Dosguard:Client surpassed the rate limit. ip = "
|
||||
<< ip << " Transfered Byte:" << transferedByte
|
||||
<< " Requests:" << requests;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto it = ipConnCount_.find(ip);
|
||||
if (it != ipConnCount_.end())
|
||||
{
|
||||
if (it->second > maxConnCount_)
|
||||
{
|
||||
log_.warn()
|
||||
<< "Dosguard:Client surpassed the rate limit. ip = "
|
||||
<< ip << " Concurrent connection:" << it->second;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increment connection count for the given ip address
|
||||
*
|
||||
* @param ip
|
||||
*/
|
||||
void
|
||||
increment(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelist_.contains(ip))
|
||||
return;
|
||||
std::unique_lock lck{mtx_};
|
||||
ipConnCount_[ip]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decrement connection count for the given ip address
|
||||
*
|
||||
* @param ip
|
||||
*/
|
||||
void
|
||||
decrement(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelist_.contains(ip))
|
||||
return;
|
||||
std::unique_lock lck{mtx_};
|
||||
assert(ipConnCount_[ip] > 0);
|
||||
ipConnCount_[ip]--;
|
||||
if (ipConnCount_[ip] == 0)
|
||||
ipConnCount_.erase(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds numObjects of usage for the given ip address.
|
||||
*
|
||||
* If the total sums up to a value equal or larger than maxFetches_
|
||||
* the operation is no longer allowed and false is returned; true is
|
||||
* returned otherwise.
|
||||
*
|
||||
* @param ip
|
||||
* @param numObjects
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
[[maybe_unused]] bool
|
||||
add(std::string const& ip, uint32_t numObjects) noexcept
|
||||
{
|
||||
if (whitelist_.contains(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::unique_lock lck(mtx_);
|
||||
ipState_[ip].transferedByte += numObjects;
|
||||
}
|
||||
|
||||
return isOk(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds one request for the given ip address.
|
||||
*
|
||||
* If the total sums up to a value equal or larger than maxRequestCount_
|
||||
* the operation is no longer allowed and false is returned; true is
|
||||
* returned otherwise.
|
||||
*
|
||||
* @param ip
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
[[maybe_unused]] bool
|
||||
request(std::string const& ip) noexcept
|
||||
{
|
||||
if (whitelist_.contains(ip))
|
||||
return true;
|
||||
|
||||
{
|
||||
std::unique_lock lck(mtx_);
|
||||
ipState_[ip].requestsCount++;
|
||||
}
|
||||
|
||||
return isOk(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Instantly clears all fetch counters added by @see add(std::string
|
||||
* const&, uint32_t)
|
||||
*/
|
||||
void
|
||||
clear()
|
||||
clear() noexcept override
|
||||
{
|
||||
std::unique_lock lck(mtx_);
|
||||
ipFetchCount_.clear();
|
||||
ipState_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_set<std::string> const
|
||||
[[nodiscard]] std::unordered_set<std::string> const
|
||||
getWhitelist(clio::Config const& config) const
|
||||
{
|
||||
using T = std::unordered_set<std::string> const;
|
||||
@@ -100,4 +246,72 @@ private:
|
||||
boost::transform_iterator(std::end(whitelist), transform)};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Sweep handler using a steady_timer and boost::asio::io_context.
|
||||
*/
|
||||
class IntervalSweepHandler
|
||||
{
|
||||
std::chrono::milliseconds sweepInterval_;
|
||||
std::reference_wrapper<boost::asio::io_context> ctx_;
|
||||
BaseDOSGuard* dosGuard_ = nullptr;
|
||||
|
||||
boost::asio::steady_timer timer_{ctx_.get()};
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new interval-based sweep handler
|
||||
*
|
||||
* @param config Clio config
|
||||
* @param ctx The boost::asio::io_context
|
||||
*/
|
||||
IntervalSweepHandler(
|
||||
clio::Config const& config,
|
||||
boost::asio::io_context& ctx)
|
||||
: sweepInterval_{std::max(
|
||||
1u,
|
||||
static_cast<uint32_t>(
|
||||
config.valueOr("dos_guard.sweep_interval", 10.0) * 1000.0))}
|
||||
, ctx_{std::ref(ctx)}
|
||||
{
|
||||
}
|
||||
|
||||
~IntervalSweepHandler()
|
||||
{
|
||||
timer_.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This setup member function is called by @ref BasicDOSGuard during
|
||||
* its initialization.
|
||||
*
|
||||
* @param guard Pointer to the dos guard
|
||||
*/
|
||||
void
|
||||
setup(BaseDOSGuard* guard)
|
||||
{
|
||||
assert(dosGuard_ == nullptr);
|
||||
dosGuard_ = guard;
|
||||
assert(dosGuard_ != nullptr);
|
||||
|
||||
createTimer();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
createTimer()
|
||||
{
|
||||
timer_.expires_after(sweepInterval_);
|
||||
timer_.async_wait([this](boost::system::error_code const& error) {
|
||||
if (error == boost::asio::error::operation_aborted)
|
||||
return;
|
||||
|
||||
dosGuard_->clear();
|
||||
createTimer();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
using DOSGuard = BasicDOSGuard<IntervalSweepHandler>;
|
||||
|
||||
} // namespace clio
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_REPORTING_HTTP_BASE_SESSION_H
|
||||
#define RIPPLE_REPORTING_HTTP_BASE_SESSION_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/asio/spawn.hpp>
|
||||
@@ -19,14 +37,17 @@
|
||||
#include <thread>
|
||||
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <log/Logger.h>
|
||||
#include <main/Build.h>
|
||||
#include <rpc/Counters.h>
|
||||
#include <rpc/RPC.h>
|
||||
#include <rpc/WorkQueue.h>
|
||||
#include <util/Profiler.h>
|
||||
#include <util/Taggable.h>
|
||||
#include <vector>
|
||||
#include <webserver/DOSGuard.h>
|
||||
|
||||
// TODO: consider removing those - visible to anyone including this header
|
||||
namespace http = boost::beast::http;
|
||||
namespace net = boost::asio;
|
||||
namespace ssl = boost::asio::ssl;
|
||||
@@ -95,13 +116,15 @@ class HttpBase : public util::Taggable
|
||||
std::shared_ptr<ETLLoadBalancer> balancer_;
|
||||
std::shared_ptr<ReportingETL const> etl_;
|
||||
util::TagDecoratorFactory const& tagFactory_;
|
||||
DOSGuard& dosGuard_;
|
||||
clio::DOSGuard& dosGuard_;
|
||||
RPC::Counters& counters_;
|
||||
WorkQueue& workQueue_;
|
||||
send_lambda lambda_;
|
||||
|
||||
protected:
|
||||
clio::Logger perfLog_{"Performance"};
|
||||
boost::beast::flat_buffer buffer_;
|
||||
bool upgraded_ = false;
|
||||
|
||||
bool
|
||||
dead()
|
||||
@@ -135,8 +158,7 @@ protected:
|
||||
if (!ec_ && ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
ec_ = ec;
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< tag() << __func__ << ": " << what << ": " << ec.message();
|
||||
perfLog_.info() << tag() << ": " << what << ": " << ec.message();
|
||||
boost::beast::get_lowest_layer(derived().stream())
|
||||
.socket()
|
||||
.close(ec);
|
||||
@@ -151,7 +173,7 @@ public:
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer buffer)
|
||||
@@ -168,11 +190,18 @@ public:
|
||||
, lambda_(*this)
|
||||
, buffer_(std::move(buffer))
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << tag() << "http session created";
|
||||
perfLog_.debug() << tag() << "http session created";
|
||||
}
|
||||
|
||||
virtual ~HttpBase()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug) << tag() << "http session closed";
|
||||
perfLog_.debug() << tag() << "http session closed";
|
||||
}
|
||||
|
||||
clio::DOSGuard&
|
||||
dosGuard()
|
||||
{
|
||||
return dosGuard_;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -209,14 +238,37 @@ public:
|
||||
if (ec)
|
||||
return httpFail(ec, "read");
|
||||
|
||||
auto ip = derived().ip();
|
||||
|
||||
if (!ip)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto const httpResponse = [&](http::status status,
|
||||
std::string content_type,
|
||||
std::string message) {
|
||||
http::response<http::string_body> res{status, req_.version()};
|
||||
res.set(
|
||||
http::field::server,
|
||||
"clio-server-" + Build::getClioVersionString());
|
||||
res.set(http::field::content_type, content_type);
|
||||
res.keep_alive(req_.keep_alive());
|
||||
res.body() = std::string(message);
|
||||
res.prepare_payload();
|
||||
return res;
|
||||
};
|
||||
|
||||
if (boost::beast::websocket::is_upgrade(req_))
|
||||
{
|
||||
upgraded_ = true;
|
||||
// Disable the timeout.
|
||||
// The websocket::stream uses its own timeout settings.
|
||||
boost::beast::get_lowest_layer(derived().stream()).expires_never();
|
||||
return make_websocket_session(
|
||||
ioc_,
|
||||
derived().release_stream(),
|
||||
derived().ip(),
|
||||
std::move(req_),
|
||||
std::move(buffer_),
|
||||
backend_,
|
||||
@@ -229,14 +281,19 @@ public:
|
||||
workQueue_);
|
||||
}
|
||||
|
||||
auto ip = derived().ip();
|
||||
// to avoid overwhelm work queue, the request limit check should be
|
||||
// before posting to queue the web socket creation will be guarded via
|
||||
// connection limit
|
||||
if (!dosGuard_.request(ip.value()))
|
||||
{
|
||||
return lambda_(httpResponse(
|
||||
http::status::service_unavailable,
|
||||
"text/plain",
|
||||
"Server is overloaded"));
|
||||
}
|
||||
|
||||
if (!ip)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << tag() << "http::" << __func__
|
||||
<< " received request from ip = " << *ip
|
||||
<< " - posting to WorkQueue";
|
||||
perfLog_.debug() << tag() << "Received request from ip = " << *ip
|
||||
<< " - posting to WorkQueue";
|
||||
|
||||
auto session = derived().shared_from_this();
|
||||
|
||||
@@ -256,23 +313,18 @@ public:
|
||||
dosGuard_,
|
||||
counters_,
|
||||
*ip,
|
||||
session);
|
||||
session,
|
||||
perfLog_);
|
||||
},
|
||||
dosGuard_.isWhiteListed(*ip)))
|
||||
{
|
||||
// Non-whitelist connection rejected due to full connection
|
||||
// queue
|
||||
http::response<http::string_body> res{
|
||||
http::status::ok, req_.version()};
|
||||
res.set(
|
||||
http::field::server,
|
||||
"clio-server-" + Build::getClioVersionString());
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.keep_alive(req_.keep_alive());
|
||||
res.body() = boost::json::serialize(
|
||||
RPC::makeError(RPC::RippledError::rpcTOO_BUSY));
|
||||
res.prepare_payload();
|
||||
lambda_(std::move(res));
|
||||
lambda_(httpResponse(
|
||||
http::status::ok,
|
||||
"application/json",
|
||||
boost::json::serialize(
|
||||
RPC::makeError(RPC::RippledError::rpcTOO_BUSY))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,10 +370,11 @@ handle_request(
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
std::string const& ip,
|
||||
std::shared_ptr<Session> http)
|
||||
std::shared_ptr<Session> http,
|
||||
clio::Logger& perfLog)
|
||||
{
|
||||
auto const httpResponse = [&req](
|
||||
http::status status,
|
||||
@@ -348,17 +401,11 @@ handle_request(
|
||||
return send(httpResponse(
|
||||
http::status::bad_request, "text/html", "Expected a POST request"));
|
||||
|
||||
if (!dosGuard.isOk(ip))
|
||||
return send(httpResponse(
|
||||
http::status::service_unavailable,
|
||||
"text/plain",
|
||||
"Server is overloaded"));
|
||||
|
||||
try
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< http->tag()
|
||||
<< "http received request from work queue: " << req.body();
|
||||
perfLog.debug() << http->tag()
|
||||
<< "http received request from work queue: "
|
||||
<< req.body();
|
||||
|
||||
boost::json::object request;
|
||||
std::string responseStr = "";
|
||||
@@ -405,14 +452,11 @@ handle_request(
|
||||
boost::json::serialize(
|
||||
RPC::makeError(RPC::RippledError::rpcBAD_SYNTAX))));
|
||||
|
||||
boost::json::object response{{"result", boost::json::object{}}};
|
||||
boost::json::object& result = response["result"].as_object();
|
||||
boost::json::object response;
|
||||
auto [v, timeDiff] =
|
||||
util::timed([&]() { return RPC::buildResponse(*context); });
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
auto v = RPC::buildResponse(*context);
|
||||
auto end = std::chrono::system_clock::now();
|
||||
auto us =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(end - start);
|
||||
auto us = std::chrono::duration<int, std::milli>(timeDiff);
|
||||
RPC::logDuration(*context, us);
|
||||
|
||||
if (auto status = std::get_if<RPC::Status>(&v))
|
||||
@@ -420,10 +464,10 @@ handle_request(
|
||||
counters.rpcErrored(context->method);
|
||||
auto error = RPC::makeError(*status);
|
||||
error["request"] = request;
|
||||
result = error;
|
||||
response["result"] = error;
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << http->tag() << __func__
|
||||
<< " Encountered error: " << responseStr;
|
||||
perfLog.debug()
|
||||
<< http->tag() << "Encountered error: " << responseStr;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -431,10 +475,15 @@ handle_request(
|
||||
// requests as successful.
|
||||
|
||||
counters.rpcComplete(context->method, us);
|
||||
result = std::get<boost::json::object>(v);
|
||||
|
||||
auto result = std::get<boost::json::object>(v);
|
||||
if (result.contains("result") && result.at("result").is_object())
|
||||
result = result.at("result").as_object();
|
||||
|
||||
if (!result.contains("error"))
|
||||
result["status"] = "success";
|
||||
|
||||
response["result"] = result;
|
||||
}
|
||||
|
||||
boost::json::array warnings;
|
||||
@@ -457,8 +506,7 @@ handle_request(
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< http->tag() << __func__ << " Caught exception : " << e.what();
|
||||
perfLog.error() << http->tag() << "Caught exception : " << e.what();
|
||||
return send(httpResponse(
|
||||
http::status::internal_server_error,
|
||||
"application/json",
|
||||
@@ -466,5 +514,3 @@ handle_request(
|
||||
RPC::makeError(RPC::RippledError::rpcINTERNAL))));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // RIPPLE_REPORTING_HTTP_BASE_SESSION_H
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_REPORTING_HTTP_SESSION_H
|
||||
#define RIPPLE_REPORTING_HTTP_SESSION_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <webserver/HttpBase.h>
|
||||
|
||||
@@ -13,6 +31,7 @@ class HttpSession : public HttpBase<HttpSession>,
|
||||
public std::enable_shared_from_this<HttpSession>
|
||||
{
|
||||
boost::beast::tcp_stream stream_;
|
||||
std::optional<std::string> ip_;
|
||||
|
||||
public:
|
||||
// Take ownership of the socket
|
||||
@@ -24,7 +43,7 @@ public:
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer buffer)
|
||||
@@ -41,6 +60,21 @@ public:
|
||||
std::move(buffer))
|
||||
, stream_(std::move(socket))
|
||||
{
|
||||
try
|
||||
{
|
||||
ip_ = stream_.socket().remote_endpoint().address().to_string();
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
}
|
||||
if (ip_)
|
||||
HttpBase::dosGuard().increment(*ip_);
|
||||
}
|
||||
|
||||
~HttpSession()
|
||||
{
|
||||
if (ip_ and not upgraded_)
|
||||
HttpBase::dosGuard().decrement(*ip_);
|
||||
}
|
||||
|
||||
boost::beast::tcp_stream&
|
||||
@@ -57,14 +91,7 @@ public:
|
||||
std::optional<std::string>
|
||||
ip()
|
||||
{
|
||||
try
|
||||
{
|
||||
return stream_.socket().remote_endpoint().address().to_string();
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return ip_;
|
||||
}
|
||||
|
||||
// Start the asynchronous operation
|
||||
@@ -91,5 +118,3 @@ public:
|
||||
// At this point the connection is closed gracefully
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RIPPLE_REPORTING_HTTP_SESSION_H
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
#ifndef LISTENER_H
|
||||
#define LISTENER_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <log/Logger.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <util/Taggable.h>
|
||||
#include <webserver/HttpSession.h>
|
||||
@@ -12,6 +27,10 @@
|
||||
#include <webserver/SslHttpSession.h>
|
||||
#include <webserver/SslWsSession.h>
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class SubscriptionManager;
|
||||
@@ -23,6 +42,7 @@ class Detector
|
||||
using std::enable_shared_from_this<
|
||||
Detector<PlainSession, SslSession>>::shared_from_this;
|
||||
|
||||
clio::Logger log_{"WebServer"};
|
||||
boost::asio::io_context& ioc_;
|
||||
boost::beast::tcp_stream stream_;
|
||||
std::optional<std::reference_wrapper<ssl::context>> ctx_;
|
||||
@@ -31,7 +51,7 @@ class Detector
|
||||
std::shared_ptr<ETLLoadBalancer> balancer_;
|
||||
std::shared_ptr<ReportingETL const> etl_;
|
||||
util::TagDecoratorFactory const& tagFactory_;
|
||||
DOSGuard& dosGuard_;
|
||||
clio::DOSGuard& dosGuard_;
|
||||
RPC::Counters& counters_;
|
||||
WorkQueue& queue_;
|
||||
boost::beast::flat_buffer buffer_;
|
||||
@@ -46,7 +66,7 @@ public:
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue)
|
||||
: ioc_(ioc)
|
||||
@@ -69,8 +89,7 @@ public:
|
||||
if (ec == net::ssl::error::stream_truncated)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< "Detector failed: " << message << ec.message() << std::endl;
|
||||
log_.info() << "Detector failed (" << message << "): " << ec.message();
|
||||
}
|
||||
|
||||
// Launch the detector
|
||||
@@ -137,6 +156,7 @@ void
|
||||
make_websocket_session(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::beast::tcp_stream stream,
|
||||
std::optional<std::string> const& ip,
|
||||
http::request<http::string_body> req,
|
||||
boost::beast::flat_buffer buffer,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
@@ -144,13 +164,14 @@ make_websocket_session(
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue)
|
||||
{
|
||||
std::make_shared<WsUpgrader>(
|
||||
ioc,
|
||||
std::move(stream),
|
||||
ip,
|
||||
backend,
|
||||
subscriptions,
|
||||
balancer,
|
||||
@@ -168,6 +189,7 @@ void
|
||||
make_websocket_session(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream> stream,
|
||||
std::optional<std::string> const& ip,
|
||||
http::request<http::string_body> req,
|
||||
boost::beast::flat_buffer buffer,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
@@ -175,13 +197,14 @@ make_websocket_session(
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue)
|
||||
{
|
||||
std::make_shared<SslWsUpgrader>(
|
||||
ioc,
|
||||
std::move(stream),
|
||||
ip,
|
||||
backend,
|
||||
subscriptions,
|
||||
balancer,
|
||||
@@ -202,6 +225,7 @@ class Listener
|
||||
using std::enable_shared_from_this<
|
||||
Listener<PlainSession, SslSession>>::shared_from_this;
|
||||
|
||||
clio::Logger log_{"WebServer"};
|
||||
boost::asio::io_context& ioc_;
|
||||
std::optional<std::reference_wrapper<ssl::context>> ctx_;
|
||||
tcp::acceptor acceptor_;
|
||||
@@ -210,7 +234,7 @@ class Listener
|
||||
std::shared_ptr<ETLLoadBalancer> balancer_;
|
||||
std::shared_ptr<ReportingETL const> etl_;
|
||||
util::TagDecoratorFactory tagFactory_;
|
||||
DOSGuard& dosGuard_;
|
||||
clio::DOSGuard& dosGuard_;
|
||||
WorkQueue queue_;
|
||||
RPC::Counters counters_;
|
||||
|
||||
@@ -226,7 +250,7 @@ public:
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory tagFactory,
|
||||
DOSGuard& dosGuard)
|
||||
clio::DOSGuard& dosGuard)
|
||||
: ioc_(ioc)
|
||||
, ctx_(ctx)
|
||||
, acceptor_(net::make_strand(ioc))
|
||||
@@ -255,9 +279,8 @@ public:
|
||||
acceptor_.bind(endpoint, ec);
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Failed to bind to endpoint: " << endpoint
|
||||
<< ". message: " << ec.message();
|
||||
log_.error() << "Failed to bind to endpoint: " << endpoint
|
||||
<< ". message: " << ec.message();
|
||||
throw std::runtime_error("Failed to bind to specified endpoint");
|
||||
}
|
||||
|
||||
@@ -265,9 +288,8 @@ public:
|
||||
acceptor_.listen(net::socket_base::max_listen_connections, ec);
|
||||
if (ec)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< "Failed to listen at endpoint: " << endpoint
|
||||
<< ". message: " << ec.message();
|
||||
log_.error() << "Failed to listen at endpoint: " << endpoint
|
||||
<< ". message: " << ec.message();
|
||||
throw std::runtime_error("Failed to listen at specified endpoint");
|
||||
}
|
||||
}
|
||||
@@ -334,8 +356,9 @@ make_HttpServer(
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
DOSGuard& dosGuard)
|
||||
clio::DOSGuard& dosGuard)
|
||||
{
|
||||
static clio::Logger log{"WebServer"};
|
||||
if (!config.contains("server"))
|
||||
return nullptr;
|
||||
|
||||
@@ -348,8 +371,8 @@ make_HttpServer(
|
||||
auto const maxQueueSize =
|
||||
serverConfig.valueOr<uint32_t>("max_queue_size", 0); // 0 is no limit
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << __func__ << " Number of workers = " << numThreads
|
||||
<< ". Max queue size = " << maxQueueSize;
|
||||
log.info() << "Number of workers = " << numThreads
|
||||
<< ". Max queue size = " << maxQueueSize;
|
||||
|
||||
auto server = std::make_shared<HttpServer>(
|
||||
ioc,
|
||||
@@ -368,5 +391,3 @@ make_HttpServer(
|
||||
return server;
|
||||
}
|
||||
} // namespace Server
|
||||
|
||||
#endif // LISTENER_H
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_REPORTING_WS_SESSION_H
|
||||
#define RIPPLE_REPORTING_WS_SESSION_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
@@ -32,17 +50,19 @@ public:
|
||||
explicit PlainWsSession(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::asio::ip::tcp::socket&& socket,
|
||||
std::optional<std::string> ip,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& buffer)
|
||||
: WsSession(
|
||||
ioc,
|
||||
ip,
|
||||
backend,
|
||||
subscriptions,
|
||||
balancer,
|
||||
@@ -65,19 +85,7 @@ public:
|
||||
std::optional<std::string>
|
||||
ip()
|
||||
{
|
||||
try
|
||||
{
|
||||
return ws()
|
||||
.next_layer()
|
||||
.socket()
|
||||
.remote_endpoint()
|
||||
.address()
|
||||
.to_string();
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return ip_;
|
||||
}
|
||||
|
||||
~PlainWsSession() = default;
|
||||
@@ -94,21 +102,23 @@ class WsUpgrader : public std::enable_shared_from_this<WsUpgrader>
|
||||
std::shared_ptr<ETLLoadBalancer> balancer_;
|
||||
std::shared_ptr<ReportingETL const> etl_;
|
||||
util::TagDecoratorFactory const& tagFactory_;
|
||||
DOSGuard& dosGuard_;
|
||||
clio::DOSGuard& dosGuard_;
|
||||
RPC::Counters& counters_;
|
||||
WorkQueue& queue_;
|
||||
http::request<http::string_body> req_;
|
||||
std::optional<std::string> ip_;
|
||||
|
||||
public:
|
||||
WsUpgrader(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::asio::ip::tcp::socket&& socket,
|
||||
std::optional<std::string> ip,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& b)
|
||||
@@ -123,17 +133,19 @@ public:
|
||||
, dosGuard_(dosGuard)
|
||||
, counters_(counters)
|
||||
, queue_(queue)
|
||||
, ip_(ip)
|
||||
{
|
||||
}
|
||||
WsUpgrader(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::beast::tcp_stream&& stream,
|
||||
std::optional<std::string> ip,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& b,
|
||||
@@ -150,6 +162,7 @@ public:
|
||||
, counters_(counters)
|
||||
, queue_(queue)
|
||||
, req_(std::move(req))
|
||||
, ip_(ip)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -198,6 +211,7 @@ private:
|
||||
std::make_shared<PlainWsSession>(
|
||||
ioc_,
|
||||
http_.release_socket(),
|
||||
ip_,
|
||||
backend_,
|
||||
subscriptions_,
|
||||
balancer_,
|
||||
@@ -210,5 +224,3 @@ private:
|
||||
->run(std::move(req_));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RIPPLE_REPORTING_WS_SESSION_H
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2021 Ripple Labs Inc.
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef REPORTING_SSL_H
|
||||
#define REPORTING_SSL_H
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
@@ -62,5 +61,3 @@ parse_certs(const char* certFilename, const char* keyFilename)
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
#endif // REPORTING_SSL_H
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_REPORTING_HTTPS_SESSION_H
|
||||
#define RIPPLE_REPORTING_HTTPS_SESSION_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <webserver/HttpBase.h>
|
||||
|
||||
@@ -13,6 +31,7 @@ class SslHttpSession : public HttpBase<SslHttpSession>,
|
||||
public std::enable_shared_from_this<SslHttpSession>
|
||||
{
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream> stream_;
|
||||
std::optional<std::string> ip_;
|
||||
|
||||
public:
|
||||
// Take ownership of the socket
|
||||
@@ -25,7 +44,7 @@ public:
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer buffer)
|
||||
@@ -42,6 +61,25 @@ public:
|
||||
std::move(buffer))
|
||||
, stream_(std::move(socket), ctx)
|
||||
{
|
||||
try
|
||||
{
|
||||
ip_ = stream_.next_layer()
|
||||
.socket()
|
||||
.remote_endpoint()
|
||||
.address()
|
||||
.to_string();
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
}
|
||||
if (ip_)
|
||||
HttpBase::dosGuard().increment(*ip_);
|
||||
}
|
||||
|
||||
~SslHttpSession()
|
||||
{
|
||||
if (ip_ and not upgraded_)
|
||||
HttpBase::dosGuard().decrement(*ip_);
|
||||
}
|
||||
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream>&
|
||||
@@ -58,18 +96,7 @@ public:
|
||||
std::optional<std::string>
|
||||
ip()
|
||||
{
|
||||
try
|
||||
{
|
||||
return stream_.next_layer()
|
||||
.socket()
|
||||
.remote_endpoint()
|
||||
.address()
|
||||
.to_string();
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return ip_;
|
||||
}
|
||||
|
||||
// Start the asynchronous operation
|
||||
@@ -126,5 +153,3 @@ public:
|
||||
// At this point the connection is closed gracefully
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RIPPLE_REPORTING_HTTPS_SESSION_H
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
#ifndef RIPPLE_REPORTING_SSL_WS_SESSION_H
|
||||
#define RIPPLE_REPORTING_SSL_WS_SESSION_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <boost/asio/dispatch.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
@@ -30,17 +48,19 @@ public:
|
||||
explicit SslWsSession(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream>&& stream,
|
||||
std::optional<std::string> ip,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& b)
|
||||
: WsSession(
|
||||
ioc,
|
||||
ip,
|
||||
backend,
|
||||
subscriptions,
|
||||
balancer,
|
||||
@@ -63,20 +83,7 @@ public:
|
||||
std::optional<std::string>
|
||||
ip()
|
||||
{
|
||||
try
|
||||
{
|
||||
return ws()
|
||||
.next_layer()
|
||||
.next_layer()
|
||||
.socket()
|
||||
.remote_endpoint()
|
||||
.address()
|
||||
.to_string();
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
return ip_;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,12 +93,13 @@ class SslWsUpgrader : public std::enable_shared_from_this<SslWsUpgrader>
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream> https_;
|
||||
boost::optional<http::request_parser<http::string_body>> parser_;
|
||||
boost::beast::flat_buffer buffer_;
|
||||
std::optional<std::string> ip_;
|
||||
std::shared_ptr<BackendInterface const> backend_;
|
||||
std::shared_ptr<SubscriptionManager> subscriptions_;
|
||||
std::shared_ptr<ETLLoadBalancer> balancer_;
|
||||
std::shared_ptr<ReportingETL const> etl_;
|
||||
util::TagDecoratorFactory const& tagFactory_;
|
||||
DOSGuard& dosGuard_;
|
||||
clio::DOSGuard& dosGuard_;
|
||||
RPC::Counters& counters_;
|
||||
WorkQueue& queue_;
|
||||
http::request<http::string_body> req_;
|
||||
@@ -99,6 +107,7 @@ class SslWsUpgrader : public std::enable_shared_from_this<SslWsUpgrader>
|
||||
public:
|
||||
SslWsUpgrader(
|
||||
boost::asio::io_context& ioc,
|
||||
std::optional<std::string> ip,
|
||||
boost::asio::ip::tcp::socket&& socket,
|
||||
ssl::context& ctx,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
@@ -106,13 +115,14 @@ public:
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& b)
|
||||
: ioc_(ioc)
|
||||
, https_(std::move(socket), ctx)
|
||||
, buffer_(std::move(b))
|
||||
, ip_(ip)
|
||||
, backend_(backend)
|
||||
, subscriptions_(subscriptions)
|
||||
, balancer_(balancer)
|
||||
@@ -126,12 +136,13 @@ public:
|
||||
SslWsUpgrader(
|
||||
boost::asio::io_context& ioc,
|
||||
boost::beast::ssl_stream<boost::beast::tcp_stream> stream,
|
||||
std::optional<std::string> ip,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& b,
|
||||
@@ -139,6 +150,7 @@ public:
|
||||
: ioc_(ioc)
|
||||
, https_(std::move(stream))
|
||||
, buffer_(std::move(b))
|
||||
, ip_(ip)
|
||||
, backend_(backend)
|
||||
, subscriptions_(subscriptions)
|
||||
, balancer_(balancer)
|
||||
@@ -211,6 +223,7 @@ private:
|
||||
std::make_shared<SslWsSession>(
|
||||
ioc_,
|
||||
std::move(https_),
|
||||
ip_,
|
||||
backend_,
|
||||
subscriptions_,
|
||||
balancer_,
|
||||
@@ -223,5 +236,3 @@ private:
|
||||
->run(std::move(req_));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RIPPLE_REPORTING_SSL_WS_SESSION_H
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
#ifndef RIPPLE_REPORTING_WS_BASE_SESSION_H
|
||||
#define RIPPLE_REPORTING_WS_BASE_SESSION_H
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <etl/ETLSource.h>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/Counters.h>
|
||||
#include <rpc/RPC.h>
|
||||
#include <rpc/WorkQueue.h>
|
||||
#include <subscriptions/Message.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <util/Profiler.h>
|
||||
#include <util/Taggable.h>
|
||||
#include <webserver/DOSGuard.h>
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/websocket.hpp>
|
||||
@@ -7,17 +38,7 @@
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <etl/ETLSource.h>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <rpc/Counters.h>
|
||||
#include <rpc/RPC.h>
|
||||
#include <rpc/WorkQueue.h>
|
||||
#include <subscriptions/Message.h>
|
||||
#include <subscriptions/SubscriptionManager.h>
|
||||
#include <util/Taggable.h>
|
||||
#include <webserver/DOSGuard.h>
|
||||
|
||||
// TODO: Consider removing these. Visible to anyone including this header.
|
||||
namespace http = boost::beast::http;
|
||||
namespace net = boost::asio;
|
||||
namespace ssl = boost::asio::ssl;
|
||||
@@ -27,8 +48,8 @@ using tcp = boost::asio::ip::tcp;
|
||||
inline void
|
||||
logError(boost::beast::error_code ec, char const* what)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< __func__ << " : " << what << ": " << ec.message() << "\n";
|
||||
static clio::Logger log{"WebServer"};
|
||||
log.debug() << what << ": " << ec.message() << "\n";
|
||||
}
|
||||
|
||||
inline boost::json::object
|
||||
@@ -38,7 +59,6 @@ getDefaultWsResponse(boost::json::value const& id)
|
||||
if (!id.is_null())
|
||||
defaultResp["id"] = id;
|
||||
|
||||
defaultResp["result"] = boost::json::object_kind;
|
||||
defaultResp["status"] = "success";
|
||||
defaultResp["type"] = "response";
|
||||
|
||||
@@ -48,6 +68,8 @@ getDefaultWsResponse(boost::json::value const& id)
|
||||
class WsBase : public util::Taggable
|
||||
{
|
||||
protected:
|
||||
clio::Logger log_{"WebServer"};
|
||||
clio::Logger perfLog_{"Performance"};
|
||||
boost::system::error_code ec_;
|
||||
|
||||
public:
|
||||
@@ -100,7 +122,7 @@ class WsSession : public WsBase,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer_;
|
||||
std::shared_ptr<ReportingETL const> etl_;
|
||||
util::TagDecoratorFactory const& tagFactory_;
|
||||
DOSGuard& dosGuard_;
|
||||
clio::DOSGuard& dosGuard_;
|
||||
RPC::Counters& counters_;
|
||||
WorkQueue& queue_;
|
||||
std::mutex mtx_;
|
||||
@@ -108,14 +130,16 @@ class WsSession : public WsBase,
|
||||
bool sending_ = false;
|
||||
std::queue<std::shared_ptr<Message>> messages_;
|
||||
|
||||
protected:
|
||||
std::optional<std::string> ip_;
|
||||
|
||||
void
|
||||
wsFail(boost::beast::error_code ec, char const* what)
|
||||
{
|
||||
if (!ec_ && ec != boost::asio::error::operation_aborted)
|
||||
{
|
||||
ec_ = ec;
|
||||
BOOST_LOG_TRIVIAL(info)
|
||||
<< tag() << __func__ << ": " << what << ": " << ec.message();
|
||||
perfLog_.info() << tag() << ": " << what << ": " << ec.message();
|
||||
boost::beast::get_lowest_layer(derived().ws()).socket().close(ec);
|
||||
|
||||
if (auto manager = subscriptions_.lock(); manager)
|
||||
@@ -126,12 +150,13 @@ class WsSession : public WsBase,
|
||||
public:
|
||||
explicit WsSession(
|
||||
boost::asio::io_context& ioc,
|
||||
std::optional<std::string> ip,
|
||||
std::shared_ptr<BackendInterface const> backend,
|
||||
std::shared_ptr<SubscriptionManager> subscriptions,
|
||||
std::shared_ptr<ETLLoadBalancer> balancer,
|
||||
std::shared_ptr<ReportingETL const> etl,
|
||||
util::TagDecoratorFactory const& tagFactory,
|
||||
DOSGuard& dosGuard,
|
||||
clio::DOSGuard& dosGuard,
|
||||
RPC::Counters& counters,
|
||||
WorkQueue& queue,
|
||||
boost::beast::flat_buffer&& buffer)
|
||||
@@ -146,12 +171,16 @@ public:
|
||||
, dosGuard_(dosGuard)
|
||||
, counters_(counters)
|
||||
, queue_(queue)
|
||||
, ip_(ip)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << tag() << "session created";
|
||||
perfLog_.info() << tag() << "session created";
|
||||
}
|
||||
|
||||
virtual ~WsSession()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << tag() << "session closed";
|
||||
perfLog_.info() << tag() << "session closed";
|
||||
if (ip_)
|
||||
dosGuard_.decrement(*ip_);
|
||||
}
|
||||
|
||||
// Access the derived class, this is part of
|
||||
@@ -244,7 +273,7 @@ public:
|
||||
if (ec)
|
||||
return wsFail(ec, "accept");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << tag() << "accepting new connection";
|
||||
perfLog_.info() << tag() << "accepting new connection";
|
||||
|
||||
// Read a message
|
||||
do_read();
|
||||
@@ -287,7 +316,7 @@ public:
|
||||
|
||||
try
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
perfLog_.debug()
|
||||
<< tag() << "ws received request from work queue : " << request;
|
||||
|
||||
auto range = backend_->fetchLedgerRange();
|
||||
@@ -309,18 +338,16 @@ public:
|
||||
|
||||
if (!context)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(warning)
|
||||
<< tag() << " could not create RPC context";
|
||||
perfLog_.warn() << tag() << "Could not create RPC context";
|
||||
return sendError(RPC::RippledError::rpcBAD_SYNTAX);
|
||||
}
|
||||
|
||||
response = getDefaultWsResponse(id);
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
auto v = RPC::buildResponse(*context);
|
||||
auto end = std::chrono::system_clock::now();
|
||||
auto us = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
end - start);
|
||||
auto [v, timeDiff] =
|
||||
util::timed([&]() { return RPC::buildResponse(*context); });
|
||||
|
||||
auto us = std::chrono::duration<int, std::milli>(timeDiff);
|
||||
logDuration(*context, us);
|
||||
|
||||
if (auto status = std::get_if<RPC::Status>(&v))
|
||||
@@ -339,13 +366,23 @@ public:
|
||||
{
|
||||
counters_.rpcComplete(context->method, us);
|
||||
|
||||
response["result"] = std::get<boost::json::object>(v);
|
||||
auto const& result = std::get<boost::json::object>(v);
|
||||
auto const isForwarded = result.contains("forwarded") &&
|
||||
result.at("forwarded").is_bool() &&
|
||||
result.at("forwarded").as_bool();
|
||||
|
||||
// if the result is forwarded - just use it as is
|
||||
// but keep all default fields in the response too.
|
||||
if (isForwarded)
|
||||
for (auto const& [k, v] : result)
|
||||
response.insert_or_assign(k, v);
|
||||
else
|
||||
response["result"] = result;
|
||||
}
|
||||
}
|
||||
catch (std::exception const& e)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(error)
|
||||
<< tag() << __func__ << " caught exception : " << e.what();
|
||||
perfLog_.error() << tag() << "Caught exception : " << e.what();
|
||||
|
||||
return sendError(RPC::RippledError::rpcINTERNAL);
|
||||
}
|
||||
@@ -385,8 +422,7 @@ public:
|
||||
if (!ip)
|
||||
return;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << tag() << "ws::" << __func__
|
||||
<< " received request from ip = " << *ip;
|
||||
perfLog_.info() << tag() << "Received request from ip = " << *ip;
|
||||
|
||||
auto sendError = [this, ip](
|
||||
auto error,
|
||||
@@ -399,7 +435,7 @@ public:
|
||||
e["request"] = request;
|
||||
|
||||
auto responseStr = boost::json::serialize(e);
|
||||
BOOST_LOG_TRIVIAL(trace) << __func__ << " : " << responseStr;
|
||||
log_.trace() << responseStr;
|
||||
dosGuard_.add(*ip, responseStr.size());
|
||||
send(std::move(responseStr));
|
||||
};
|
||||
@@ -416,21 +452,23 @@ public:
|
||||
}(std::move(msg));
|
||||
|
||||
boost::json::object request;
|
||||
if (!raw.is_object())
|
||||
return sendError(
|
||||
RPC::RippledError::rpcINVALID_PARAMS, nullptr, request);
|
||||
request = raw.as_object();
|
||||
|
||||
auto id = request.contains("id") ? request.at("id") : nullptr;
|
||||
|
||||
if (!dosGuard_.isOk(*ip))
|
||||
// dosGuard served request++ and check ip address
|
||||
// dosGuard should check before any request, even invalid request
|
||||
if (!dosGuard_.request(*ip))
|
||||
{
|
||||
sendError(RPC::RippledError::rpcSLOW_DOWN, id, request);
|
||||
sendError(RPC::RippledError::rpcSLOW_DOWN, nullptr, request);
|
||||
}
|
||||
else if (!raw.is_object())
|
||||
{
|
||||
// handle invalid request and async read again
|
||||
sendError(RPC::RippledError::rpcINVALID_PARAMS, nullptr, request);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(debug)
|
||||
<< tag() << __func__ << " adding to work queue";
|
||||
request = raw.as_object();
|
||||
|
||||
auto id = request.contains("id") ? request.at("id") : nullptr;
|
||||
perfLog_.debug() << tag() << "Adding to work queue";
|
||||
|
||||
if (!queue_.postCoro(
|
||||
[shared_this = shared_from_this(),
|
||||
@@ -445,5 +483,3 @@ public:
|
||||
do_read();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RIPPLE_REPORTING_WS_BASE_SESSION_H
|
||||
|
||||
@@ -1,17 +1,42 @@
|
||||
#include <algorithm>
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <backend/BackendFactory.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <backend/DBHelpers.h>
|
||||
#include <config/Config.h>
|
||||
#include <etl/ReportingETL.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <log/Logger.h>
|
||||
#include <rpc/RPCHelpers.h>
|
||||
#include <util/Fixtures.h>
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <backend/BackendFactory.h>
|
||||
#include <backend/BackendInterface.h>
|
||||
#include <config/Config.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(BackendTest, Basic)
|
||||
#include <algorithm>
|
||||
|
||||
class BackendTest : public NoLoggerFixture
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(BackendTest, Basic)
|
||||
{
|
||||
boost::asio::io_context ioc;
|
||||
std::optional<boost::asio::io_context::work> work;
|
||||
@@ -21,7 +46,7 @@ TEST(BackendTest, Basic)
|
||||
boost::asio::spawn(
|
||||
ioc, [&done, &work, &ioc](boost::asio::yield_context yield) {
|
||||
boost::log::core::get()->set_filter(
|
||||
boost::log::trivial::severity >= boost::log::trivial::warning);
|
||||
clio::log_severity >= clio::Severity::WRN);
|
||||
std::string keyspace = "clio_test_" +
|
||||
std::to_string(std::chrono::system_clock::now()
|
||||
.time_since_epoch()
|
||||
@@ -41,7 +66,6 @@ TEST(BackendTest, Basic)
|
||||
std::vector<boost::json::object> configs = {cassandraConfig};
|
||||
for (auto& config : configs)
|
||||
{
|
||||
std::cout << keyspace << std::endl;
|
||||
auto backend = Backend::make_Backend(ioc, clio::Config{config});
|
||||
|
||||
std::string rawHeader =
|
||||
@@ -107,10 +131,8 @@ TEST(BackendTest, Basic)
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "fetching ledger by sequence" << std::endl;
|
||||
auto retLgr =
|
||||
backend->fetchLedgerBySequence(lgrInfo.seq, yield);
|
||||
std::cout << "fetched ledger by sequence" << std::endl;
|
||||
ASSERT_TRUE(retLgr.has_value());
|
||||
EXPECT_EQ(retLgr->seq, lgrInfo.seq);
|
||||
EXPECT_EQ(
|
||||
@@ -147,10 +169,8 @@ TEST(BackendTest, Basic)
|
||||
EXPECT_EQ(seq, lgrInfoNext.seq);
|
||||
}
|
||||
{
|
||||
std::cout << "fetching ledger by sequence" << std::endl;
|
||||
auto retLgr =
|
||||
backend->fetchLedgerBySequence(lgrInfoNext.seq, yield);
|
||||
std::cout << "fetched ledger by sequence" << std::endl;
|
||||
EXPECT_TRUE(retLgr.has_value());
|
||||
EXPECT_EQ(retLgr->seq, lgrInfoNext.seq);
|
||||
EXPECT_EQ(
|
||||
@@ -802,9 +822,6 @@ TEST(BackendTest, Basic)
|
||||
auto objs,
|
||||
auto accountTx,
|
||||
auto state) {
|
||||
std::cout
|
||||
<< "writing ledger = " << std::to_string(lgrInfo.seq)
|
||||
<< std::endl;
|
||||
backend->startWrites();
|
||||
|
||||
backend->writeLedger(
|
||||
@@ -978,11 +995,6 @@ TEST(BackendTest, Basic)
|
||||
uint32_t limit = 10;
|
||||
page = backend->fetchLedgerPage(
|
||||
page.cursor, seq, limit, false, yield);
|
||||
std::cout << "fetched a page " << page.objects.size()
|
||||
<< std::endl;
|
||||
if (page.cursor)
|
||||
std::cout << ripple::strHex(*page.cursor)
|
||||
<< std::endl;
|
||||
// if (page.cursor)
|
||||
// EXPECT_EQ(page.objects.size(), limit);
|
||||
retObjs.insert(
|
||||
@@ -1007,8 +1019,7 @@ TEST(BackendTest, Basic)
|
||||
}
|
||||
}
|
||||
if (found != (obj.second.size() != 0))
|
||||
std::cout << ripple::strHex(obj.first) << std::endl;
|
||||
ASSERT_EQ(found, obj.second.size() != 0);
|
||||
ASSERT_EQ(found, obj.second.size() != 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1148,15 +1159,12 @@ TEST(BackendTest, Basic)
|
||||
|
||||
for (auto [seq, diff] : state)
|
||||
{
|
||||
std::cout << "flatteneing" << std::endl;
|
||||
auto flat = flatten(seq);
|
||||
std::cout << "flattened" << std::endl;
|
||||
checkLedger(
|
||||
lgrInfos[seq],
|
||||
allTxns[seq],
|
||||
flat,
|
||||
flattenAccountTx(seq));
|
||||
std::cout << "checked" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1168,11 +1176,11 @@ TEST(BackendTest, Basic)
|
||||
EXPECT_EQ(done, true);
|
||||
}
|
||||
|
||||
TEST(Backend, cache)
|
||||
TEST_F(BackendTest, cache)
|
||||
{
|
||||
using namespace Backend;
|
||||
boost::log::core::get()->set_filter(
|
||||
boost::log::trivial::severity >= boost::log::trivial::warning);
|
||||
clio::log_severity >= clio::Severity::WRN);
|
||||
SimpleCache cache;
|
||||
ASSERT_FALSE(cache.isFull());
|
||||
cache.setFull();
|
||||
@@ -1410,11 +1418,11 @@ TEST(Backend, cache)
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Backend, cacheBackground)
|
||||
TEST_F(BackendTest, cacheBackground)
|
||||
{
|
||||
using namespace Backend;
|
||||
boost::log::core::get()->set_filter(
|
||||
boost::log::trivial::severity >= boost::log::trivial::warning);
|
||||
clio::log_severity >= clio::Severity::WRN);
|
||||
SimpleCache cache;
|
||||
ASSERT_FALSE(cache.isFull());
|
||||
ASSERT_EQ(cache.size(), 0);
|
||||
@@ -1814,7 +1822,7 @@ TEST(Backend, cacheBackground)
|
||||
ASSERT_EQ(idx, allObjs.size());
|
||||
}
|
||||
|
||||
TEST(Backend, cacheIntegration)
|
||||
TEST_F(BackendTest, cacheIntegration)
|
||||
{
|
||||
boost::asio::io_context ioc;
|
||||
std::optional<boost::asio::io_context::work> work;
|
||||
@@ -1824,7 +1832,7 @@ TEST(Backend, cacheIntegration)
|
||||
boost::asio::spawn(
|
||||
ioc, [&ioc, &done, &work](boost::asio::yield_context yield) {
|
||||
boost::log::core::get()->set_filter(
|
||||
boost::log::trivial::severity >= boost::log::trivial::warning);
|
||||
clio::log_severity >= clio::Severity::WRN);
|
||||
std::string keyspace = "clio_test_" +
|
||||
std::to_string(std::chrono::system_clock::now()
|
||||
.time_since_epoch()
|
||||
@@ -1844,7 +1852,6 @@ TEST(Backend, cacheIntegration)
|
||||
std::vector<boost::json::object> configs = {cassandraConfig};
|
||||
for (auto& config : configs)
|
||||
{
|
||||
std::cout << keyspace << std::endl;
|
||||
auto backend = Backend::make_Backend(ioc, clio::Config{config});
|
||||
backend->cache().setFull();
|
||||
|
||||
@@ -1927,10 +1934,8 @@ TEST(Backend, cacheIntegration)
|
||||
}
|
||||
|
||||
{
|
||||
std::cout << "fetching ledger by sequence" << std::endl;
|
||||
auto retLgr =
|
||||
backend->fetchLedgerBySequence(lgrInfo.seq, yield);
|
||||
std::cout << "fetched ledger by sequence" << std::endl;
|
||||
ASSERT_TRUE(retLgr.has_value());
|
||||
EXPECT_EQ(retLgr->seq, lgrInfo.seq);
|
||||
EXPECT_EQ(
|
||||
@@ -1967,10 +1972,8 @@ TEST(Backend, cacheIntegration)
|
||||
EXPECT_EQ(seq, lgrInfoNext.seq);
|
||||
}
|
||||
{
|
||||
std::cout << "fetching ledger by sequence" << std::endl;
|
||||
auto retLgr =
|
||||
backend->fetchLedgerBySequence(lgrInfoNext.seq, yield);
|
||||
std::cout << "fetched ledger by sequence" << std::endl;
|
||||
EXPECT_TRUE(retLgr.has_value());
|
||||
EXPECT_EQ(retLgr->seq, lgrInfoNext.seq);
|
||||
EXPECT_EQ(
|
||||
@@ -2226,8 +2229,6 @@ TEST(Backend, cacheIntegration)
|
||||
return lgrInfo;
|
||||
};
|
||||
auto writeLedger = [&](auto lgrInfo, auto objs, auto state) {
|
||||
std::cout << "writing ledger = "
|
||||
<< std::to_string(lgrInfo.seq);
|
||||
backend->startWrites();
|
||||
|
||||
backend->writeLedger(
|
||||
@@ -2344,11 +2345,6 @@ TEST(Backend, cacheIntegration)
|
||||
uint32_t limit = 10;
|
||||
page = backend->fetchLedgerPage(
|
||||
page.cursor, seq, limit, false, yield);
|
||||
std::cout << "fetched a page " << page.objects.size()
|
||||
<< std::endl;
|
||||
if (page.cursor)
|
||||
std::cout << ripple::strHex(*page.cursor)
|
||||
<< std::endl;
|
||||
// if (page.cursor)
|
||||
// EXPECT_EQ(page.objects.size(), limit);
|
||||
retObjs.insert(
|
||||
@@ -2372,8 +2368,7 @@ TEST(Backend, cacheIntegration)
|
||||
}
|
||||
}
|
||||
if (found != (obj.second.size() != 0))
|
||||
std::cout << ripple::strHex(obj.first) << std::endl;
|
||||
ASSERT_EQ(found, obj.second.size() != 0);
|
||||
ASSERT_EQ(found, obj.second.size() != 0);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2437,11 +2432,8 @@ TEST(Backend, cacheIntegration)
|
||||
|
||||
for (auto [seq, diff] : state)
|
||||
{
|
||||
std::cout << "flatteneing" << std::endl;
|
||||
auto flat = flatten(seq);
|
||||
std::cout << "flattened" << std::endl;
|
||||
checkLedger(lgrInfos[seq], flat);
|
||||
std::cout << "checked" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <config/Config.h>
|
||||
#include <util/Fixtures.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <boost/log/core.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
@@ -34,17 +52,10 @@ constexpr static auto JSONData = R"JSON(
|
||||
}
|
||||
)JSON";
|
||||
|
||||
class ConfigTest : public ::testing::Test
|
||||
class ConfigTest : public NoLoggerFixture
|
||||
{
|
||||
protected:
|
||||
Config cfg{json::parse(JSONData)};
|
||||
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
// disable logging in test
|
||||
core::get()->set_logging_enabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ConfigTest, SanityCheck)
|
||||
177
unittests/DOSGuard.cpp
Normal file
177
unittests/DOSGuard.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <util/Fixtures.h>
|
||||
|
||||
#include <config/Config.h>
|
||||
#include <webserver/DOSGuard.h>
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace testing;
|
||||
using namespace clio;
|
||||
using namespace std;
|
||||
namespace json = boost::json;
|
||||
|
||||
namespace {
|
||||
constexpr static auto JSONData = R"JSON(
|
||||
{
|
||||
"dos_guard": {
|
||||
"max_fetches": 100,
|
||||
"sweep_interval": 1,
|
||||
"max_connections": 2,
|
||||
"max_requests": 3,
|
||||
"whitelist": ["127.0.0.1"]
|
||||
}
|
||||
}
|
||||
)JSON";
|
||||
|
||||
constexpr static auto JSONData2 = R"JSON(
|
||||
{
|
||||
"dos_guard": {
|
||||
"max_fetches": 100,
|
||||
"sweep_interval": 0.1,
|
||||
"max_connections": 2,
|
||||
"whitelist": ["127.0.0.1"]
|
||||
}
|
||||
}
|
||||
)JSON";
|
||||
|
||||
constexpr static auto IP = "127.0.0.2";
|
||||
|
||||
class FakeSweepHandler
|
||||
{
|
||||
private:
|
||||
using guard_type = BasicDOSGuard<FakeSweepHandler>;
|
||||
guard_type* dosGuard_;
|
||||
|
||||
public:
|
||||
void
|
||||
setup(guard_type* guard)
|
||||
{
|
||||
dosGuard_ = guard;
|
||||
}
|
||||
|
||||
void
|
||||
sweep()
|
||||
{
|
||||
dosGuard_->clear();
|
||||
}
|
||||
};
|
||||
}; // namespace
|
||||
|
||||
class DOSGuardTest : public NoLoggerFixture
|
||||
{
|
||||
protected:
|
||||
Config cfg{json::parse(JSONData)};
|
||||
FakeSweepHandler sweepHandler;
|
||||
BasicDOSGuard<FakeSweepHandler> guard{cfg, sweepHandler};
|
||||
};
|
||||
|
||||
TEST_F(DOSGuardTest, Whitelisting)
|
||||
{
|
||||
EXPECT_TRUE(guard.isWhiteListed("127.0.0.1"));
|
||||
EXPECT_FALSE(guard.isWhiteListed(IP));
|
||||
}
|
||||
|
||||
TEST_F(DOSGuardTest, ConnectionCount)
|
||||
{
|
||||
EXPECT_TRUE(guard.isOk(IP));
|
||||
guard.increment(IP); // one connection
|
||||
EXPECT_TRUE(guard.isOk(IP));
|
||||
guard.increment(IP); // two connections
|
||||
EXPECT_TRUE(guard.isOk(IP));
|
||||
guard.increment(IP); // > two connections, can't connect more
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
|
||||
guard.decrement(IP);
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can connect again
|
||||
}
|
||||
|
||||
TEST_F(DOSGuardTest, FetchCount)
|
||||
{
|
||||
EXPECT_TRUE(guard.add(IP, 50)); // half of allowence
|
||||
EXPECT_TRUE(guard.add(IP, 50)); // now fully charged
|
||||
EXPECT_FALSE(guard.add(IP, 1)); // can't add even 1 anymore
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
|
||||
guard.clear(); // force clear the above fetch count
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can fetch again
|
||||
}
|
||||
|
||||
TEST_F(DOSGuardTest, ClearFetchCountOnTimer)
|
||||
{
|
||||
EXPECT_TRUE(guard.add(IP, 50)); // half of allowence
|
||||
EXPECT_TRUE(guard.add(IP, 50)); // now fully charged
|
||||
EXPECT_FALSE(guard.add(IP, 1)); // can't add even 1 anymore
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
|
||||
sweepHandler.sweep(); // pretend sweep called from timer
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can fetch again
|
||||
}
|
||||
|
||||
TEST_F(DOSGuardTest, RequestLimit)
|
||||
{
|
||||
EXPECT_TRUE(guard.request(IP));
|
||||
EXPECT_TRUE(guard.request(IP));
|
||||
EXPECT_TRUE(guard.request(IP));
|
||||
EXPECT_TRUE(guard.isOk(IP));
|
||||
EXPECT_FALSE(guard.request(IP));
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
guard.clear();
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can request again
|
||||
}
|
||||
|
||||
TEST_F(DOSGuardTest, RequestLimitOnTimer)
|
||||
{
|
||||
EXPECT_TRUE(guard.request(IP));
|
||||
EXPECT_TRUE(guard.request(IP));
|
||||
EXPECT_TRUE(guard.request(IP));
|
||||
EXPECT_TRUE(guard.isOk(IP));
|
||||
EXPECT_FALSE(guard.request(IP));
|
||||
EXPECT_FALSE(guard.isOk(IP));
|
||||
sweepHandler.sweep();
|
||||
EXPECT_TRUE(guard.isOk(IP)); // can request again
|
||||
}
|
||||
|
||||
template <typename SweepHandler>
|
||||
struct BasicDOSGuardMock : public BaseDOSGuard
|
||||
{
|
||||
BasicDOSGuardMock(SweepHandler& handler)
|
||||
{
|
||||
handler.setup(this);
|
||||
}
|
||||
|
||||
MOCK_METHOD(void, clear, (), (noexcept, override));
|
||||
};
|
||||
|
||||
class DOSGuardIntervalSweepHandlerTest : public SyncAsioContextTest
|
||||
{
|
||||
protected:
|
||||
Config cfg{json::parse(JSONData2)};
|
||||
IntervalSweepHandler sweepHandler{cfg, ctx};
|
||||
BasicDOSGuardMock<IntervalSweepHandler> guard{sweepHandler};
|
||||
};
|
||||
|
||||
TEST_F(DOSGuardIntervalSweepHandlerTest, SweepAfterInterval)
|
||||
{
|
||||
EXPECT_CALL(guard, clear()).Times(Exactly(2));
|
||||
ctx.run_for(std::chrono::milliseconds(210));
|
||||
}
|
||||
68
unittests/Logger.cpp
Normal file
68
unittests/Logger.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <util/Fixtures.h>
|
||||
using namespace clio;
|
||||
|
||||
// Used as a fixture for tests with enabled logging
|
||||
class LoggerTest : public LoggerFixture
|
||||
{
|
||||
};
|
||||
|
||||
// Used as a fixture for tests with disabled logging
|
||||
class NoLoggerTest : public NoLoggerFixture
|
||||
{
|
||||
};
|
||||
|
||||
TEST_F(LoggerTest, Basic)
|
||||
{
|
||||
Logger log{"General"};
|
||||
log.info() << "Info line logged";
|
||||
checkEqual("General:NFO Info line logged");
|
||||
|
||||
LogService::debug() << "Debug line with numbers " << 12345;
|
||||
checkEqual("General:DBG Debug line with numbers 12345");
|
||||
|
||||
LogService::warn() << "Warning is logged";
|
||||
checkEqual("General:WRN Warning is logged");
|
||||
}
|
||||
|
||||
TEST_F(LoggerTest, Filtering)
|
||||
{
|
||||
Logger log{"General"};
|
||||
log.trace() << "Should not be logged";
|
||||
checkEmpty();
|
||||
|
||||
log.warn() << "Warning is logged";
|
||||
checkEqual("General:WRN Warning is logged");
|
||||
|
||||
Logger tlog{"Trace"};
|
||||
tlog.trace() << "Trace line logged for 'Trace' component";
|
||||
checkEqual("Trace:TRC Trace line logged for 'Trace' component");
|
||||
}
|
||||
|
||||
TEST_F(NoLoggerTest, Basic)
|
||||
{
|
||||
Logger log{"Trace"};
|
||||
log.trace() << "Nothing";
|
||||
checkEmpty();
|
||||
|
||||
LogService::fatal() << "Still nothing";
|
||||
checkEmpty();
|
||||
}
|
||||
108
unittests/ProfilerTest.cpp
Normal file
108
unittests/ProfilerTest.cpp
Normal file
@@ -0,0 +1,108 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <thread>
|
||||
#include <util/Profiler.h>
|
||||
|
||||
using namespace util;
|
||||
TEST(TimedTest, HasReturnValue)
|
||||
{
|
||||
auto [ret, time] = timed([]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
return 8;
|
||||
});
|
||||
|
||||
ASSERT_EQ(ret, 8);
|
||||
ASSERT_NE(time, 0);
|
||||
}
|
||||
|
||||
TEST(TimedTest, ReturnVoid)
|
||||
{
|
||||
auto time = timed(
|
||||
[]() { std::this_thread::sleep_for(std::chrono::milliseconds(5)); });
|
||||
|
||||
ASSERT_NE(time, 0);
|
||||
}
|
||||
|
||||
struct FunctorTest
|
||||
{
|
||||
void
|
||||
operator()() const
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
};
|
||||
|
||||
TEST(TimedTest, Functor)
|
||||
{
|
||||
auto time = timed(FunctorTest());
|
||||
|
||||
ASSERT_NE(time, 0);
|
||||
}
|
||||
|
||||
TEST(TimedTest, MovedLambda)
|
||||
{
|
||||
auto f = []() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
return 8;
|
||||
};
|
||||
auto [ret, time] = timed(std::move(f));
|
||||
|
||||
ASSERT_EQ(ret, 8);
|
||||
ASSERT_NE(time, 0);
|
||||
}
|
||||
|
||||
TEST(TimedTest, ChangeToNs)
|
||||
{
|
||||
auto f = []() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
return 8;
|
||||
};
|
||||
auto [ret, time] = timed<std::chrono::nanoseconds>(std::move(f));
|
||||
ASSERT_EQ(ret, 8);
|
||||
ASSERT_GE(time, 5 * 1000000);
|
||||
}
|
||||
|
||||
TEST(TimedTest, NestedLambda)
|
||||
{
|
||||
double timeNested;
|
||||
auto f = [&]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
timeNested = timed([]() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
});
|
||||
return 8;
|
||||
};
|
||||
auto [ret, time] = timed<std::chrono::nanoseconds>(std::move(f));
|
||||
ASSERT_EQ(ret, 8);
|
||||
ASSERT_GE(timeNested, 5);
|
||||
ASSERT_GE(time, 10 * 1000000);
|
||||
}
|
||||
|
||||
TEST(TimedTest, FloatSec)
|
||||
{
|
||||
auto f = []() {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
return 8;
|
||||
};
|
||||
auto [ret, time] = timed<std::chrono::duration<double>>(std::move(f));
|
||||
ASSERT_EQ(ret, 8);
|
||||
ASSERT_GE(time, 0);
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <rpc/RPC.h>
|
||||
|
||||
#include <boost/json.hpp>
|
||||
|
||||
156
unittests/util/Fixtures.h
Normal file
156
unittests/util/Fixtures.h
Normal file
@@ -0,0 +1,156 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2022, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ios>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <log/Logger.h>
|
||||
|
||||
/**
|
||||
* @brief Fixture with LogService support.
|
||||
*/
|
||||
class LoggerFixture : public ::testing::Test
|
||||
{
|
||||
/**
|
||||
* @brief A simple string buffer that can be used to mock std::cout for
|
||||
* console logging.
|
||||
*/
|
||||
class FakeBuffer final : public std::stringbuf
|
||||
{
|
||||
public:
|
||||
std::string
|
||||
getStrAndReset()
|
||||
{
|
||||
auto value = str();
|
||||
str("");
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
FakeBuffer buffer_;
|
||||
std::ostream stream_ = std::ostream{&buffer_};
|
||||
|
||||
protected:
|
||||
// Simulates the `LogService::init(config)` call
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
static std::once_flag once_;
|
||||
std::call_once(once_, [] {
|
||||
boost::log::add_common_attributes();
|
||||
boost::log::register_simple_formatter_factory<clio::Severity, char>(
|
||||
"Severity");
|
||||
});
|
||||
|
||||
namespace src = boost::log::sources;
|
||||
namespace keywords = boost::log::keywords;
|
||||
namespace sinks = boost::log::sinks;
|
||||
namespace expr = boost::log::expressions;
|
||||
auto core = boost::log::core::get();
|
||||
|
||||
core->remove_all_sinks();
|
||||
boost::log::add_console_log(
|
||||
stream_, keywords::format = "%Channel%:%Severity% %Message%");
|
||||
auto min_severity = expr::channel_severity_filter(
|
||||
clio::log_channel, clio::log_severity);
|
||||
min_severity["General"] = clio::Severity::DBG;
|
||||
min_severity["Trace"] = clio::Severity::TRC;
|
||||
core->set_filter(min_severity);
|
||||
core->set_logging_enabled(true);
|
||||
}
|
||||
|
||||
void
|
||||
checkEqual(std::string expected)
|
||||
{
|
||||
auto value = buffer_.getStrAndReset();
|
||||
ASSERT_EQ(value, expected + '\n');
|
||||
}
|
||||
|
||||
void
|
||||
checkEmpty()
|
||||
{
|
||||
ASSERT_TRUE(buffer_.getStrAndReset().empty());
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fixture with LogService support but completely disabled logging.
|
||||
*
|
||||
* This is meant to be used as a base for other fixtures.
|
||||
*/
|
||||
class NoLoggerFixture : public LoggerFixture
|
||||
{
|
||||
protected:
|
||||
void
|
||||
SetUp() override
|
||||
{
|
||||
LoggerFixture::SetUp();
|
||||
boost::log::core::get()->set_logging_enabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fixture with an embedded boost::asio context running on a thread
|
||||
*
|
||||
* This is meant to be used as a base for other fixtures.
|
||||
*/
|
||||
struct AsyncAsioContextTest : public NoLoggerFixture
|
||||
{
|
||||
AsyncAsioContextTest()
|
||||
{
|
||||
work.emplace(ctx); // make sure ctx does not stop on its own
|
||||
}
|
||||
|
||||
~AsyncAsioContextTest()
|
||||
{
|
||||
work.reset();
|
||||
ctx.stop();
|
||||
runner.join();
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::io_context ctx;
|
||||
|
||||
private:
|
||||
std::optional<boost::asio::io_service::work> work;
|
||||
std::thread runner{[this] { ctx.run(); }};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Fixture with an embedded boost::asio context that is not running by
|
||||
* default but can be progressed on the calling thread
|
||||
*
|
||||
* Use `run_for(duration)` etc. directly on `ctx`.
|
||||
* This is meant to be used as a base for other fixtures.
|
||||
*/
|
||||
struct SyncAsioContextTest : public NoLoggerFixture
|
||||
{
|
||||
SyncAsioContextTest()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::asio::io_context ctx;
|
||||
};
|
||||
Reference in New Issue
Block a user