From f13c3e9907313f09583721e7982f55ee3b72df85 Mon Sep 17 00:00:00 2001 From: Lee Bousfield Date: Wed, 24 Jan 2018 22:15:08 -0700 Subject: [PATCH] Add load tester * Add Connection: close header to RPC * Add load tester --- .gitignore | 3 + .travis.yml | 2 +- ci/build-travis.sh | 12 + ci/test.sh | 6 +- docker/ci/Dockerfile | 3 +- load-tester/Cargo.lock | 724 +++++++++++++++++++++++++++++++++ load-tester/Cargo.toml | 16 + load-tester/src/launch_node.rs | 125 ++++++ load-tester/src/main.rs | 534 ++++++++++++++++++++++++ load-tester/src/rpc.rs | 95 +++++ rai/node/rpc.cpp | 1 + 11 files changed, 1518 insertions(+), 3 deletions(-) create mode 100644 load-tester/Cargo.lock create mode 100644 load-tester/Cargo.toml create mode 100644 load-tester/src/launch_node.rs create mode 100644 load-tester/src/main.rs create mode 100644 load-tester/src/rpc.rs diff --git a/.gitignore b/.gitignore index 69a224db..b1661320 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ # Doxygen built HTML /doc-build +# Rust generated files +/load-tester/target + # Executables *.exe *.out diff --git a/.travis.yml b/.travis.yml index 1bc01f88..681ab2b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ compiler: before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo mkdir -p /etc/docker && echo '{"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' | sudo tee /etc/docker/daemon.json && sudo service docker restart; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update && brew install qt5 && brew cask install xquartz && brew upgrade boost; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew update && brew install qt5 && brew cask install xquartz && brew upgrade boost && brew install rust; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then docker pull lukealonso/raiblocks-ci:latest; fi script: diff --git a/ci/build-travis.sh b/ci/build-travis.sh index a81f3404..032c5422 100755 --- a/ci/build-travis.sh +++ b/ci/build-travis.sh @@ -47,4 +47,16 @@ if [[ "$OSTYPE" == "darwin"* ]]; then else TRUE_CMD=true fi + +if ! cargo --version &>/dev/null; then + # We'll update the docker image once this PR gets merged. + # If you're reading this comment on master, contact @PlasmaPower + apt-get update && apt-get install -yq cargo +fi + +pushd load-tester +cargo build --release +popd +cp ./load-tester/target/release/raiblocks-load-tester ./build/load_test + ./ci/test.sh ./build || ${TRUE_CMD} diff --git a/ci/test.sh b/ci/test.sh index 426541ac..b24b1de1 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -47,9 +47,13 @@ run_tests() { xvfb_run_ ./qt_test qt_test_res=${?} + ./load_test ./rai_node + load_test_res=${?} + echo "Core Test return code: ${core_test_res}" echo "QT Test return code: ${qt_test_res}" - return $((${core_test_res} + ${qt_test_res})) + echo "Load Test return code: ${qt_test_res}" + return $((${core_test_res} + ${qt_test_res} + ${load_test_res})) } cd ${build_dir} diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index 0593d846..9eb59e85 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -26,6 +26,7 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get install -yq \ qt5-default \ valgrind \ - xorg xvfb xauth xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic + xorg xvfb xauth xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic \ + cargo RUN rm -rf /tmp/* diff --git a/load-tester/Cargo.lock b/load-tester/Cargo.lock new file mode 100644 index 00000000..0f969eaf --- /dev/null +++ b/load-tester/Cargo.lock @@ -0,0 +1,724 @@ +[[package]] +name = "ansi_term" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "atty" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "base64" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytes" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "error-chain" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures-cpupool" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "httparse" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.11.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "iovec" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "lazycell" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "log" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mime" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "miow" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "socket2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "net2" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num_cpus" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "raiblocks-load-tester" +version = "0.1.0" +dependencies = [ + "clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.15 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-process 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.3.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "relay" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "safemem" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "scoped-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive_internals" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "slab" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "slab" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "smallvec" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "socket2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "take" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-core" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-io" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-process" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-signal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-proto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-service" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tokio-signal" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicase" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "version_check" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[metadata] +"checksum ansi_term 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3568b48b7cefa6b8ce125f9bb4989e52fbcc29ebea88df04cc7c5f12f70455" +"checksum atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8352656fd42c30a0c3c89d26dea01e3b77c0ab2af18230835c15e2e13cd51859" +"checksum backtrace 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbbf59b1c43eefa8c3ede390fcc36820b4999f7914104015be25025e0d62af2" +"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" +"checksum base64 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "229d032f1a99302697f10b27167ae6d03d49d032e6a8e2550e8d3fc13356d2b4" +"checksum bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3c30d3802dfb7281680d6285f2ccdaa8c2d8fee41f93805dba5c4cf50dc23cf" +"checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" +"checksum bytes 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b7db437d718977f6dc9b2e3fd6fc343c02ac6b899b73fdd2179163447bd9ce9" +"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" +"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4151c5790817c7d21bbdc6c3530811f798172915f93258244948b93ba19604a6" +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" +"checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" +"checksum httparse 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c2f407128745b78abc95c0ffbe4e5d37427fdc0d45470710cfef8c44522a2e37" +"checksum hyper 0.11.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4d6105c5eeb03068b10ff34475a0d166964f98e7b9777cc34b342a225af9b87c" +"checksum iovec 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6e8b9c2247fcf6c6a1151f1156932be5606c9fd6f55a2d7f9fc1cb29386b2f7" +"checksum itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8324a32baf01e2ae060e9de58ed0bc2320c9a2833491ee36cd3b4c414de4db8c" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" +"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" +"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" +"checksum log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89f010e843f2b1a31dbd316b3b8d443758bc634bed37aabade59c686d644e0a2" +"checksum mime 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e2e00e17be181010a91dbfefb01660b17311059dc8c7f48b9017677721e732bd" +"checksum mio 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "75f72a93f046f1517e3cfddc0a096eb756a2ba727d36edc8227dee769a50a9b0" +"checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +"checksum mio-uds 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1731a873077147b626d89cc6c2a0db6288d607496c5d10c0cfcf3adc697ec673" +"checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9224c91f82b3c47cf53dcf78dfaa20d6888fbcc5d272d5f2fcdf8a697f3c987d" +"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" +"checksum num-traits 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "9936036cc70fe4a8b2d338ab665900323290efb03983c86cbe235ae800ad8017" +"checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum relay 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f301bafeb60867c85170031bdb2fcf24c8041f33aee09e7b116a58d4e9f781c5" +"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" +"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f" +"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" +"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" +"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" +"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" +"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb" +"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" +"checksum slab 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fdeff4cd9ecff59ec7e3744cbca73dfe5ac35c2aedb2cfba8a1c715a18912e9d" +"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" +"checksum socket2 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a76b792959eba82f021c9028c8ecb6396f085268d6d46af2ed96a829cc758d7c" +"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "a15375f1df02096fb3317256ce2cee6a1f42fc84ea5ad5fc8c421cfe40c73098" +"checksum tokio-core 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "52b4e32d8edbf29501aabb3570f027c6ceb00ccef6538f4bddba0200503e74e8" +"checksum tokio-io 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "514aae203178929dbf03318ad7c683126672d4d96eccb77b29603d33c9e25743" +"checksum tokio-process 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2e76e0cd21a4ae5362697e85f98aa5d26c88f09ce9fc367b57c0643ba0b022c2" +"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" +"checksum tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "24da22d077e0f15f55162bdbdc661228c1581892f52074fb242678d015b45162" +"checksum tokio-signal 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "57c4031b97651d28c87a0a071e1c2809d70609d3120ce285b302eb7d52c96906" +"checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" +"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" +"checksum version_check 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6b772017e347561807c1aa192438c5fd74242a670a6cffacc40f2defd1dc069d" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" diff --git a/load-tester/Cargo.toml b/load-tester/Cargo.toml new file mode 100644 index 00000000..86a908c8 --- /dev/null +++ b/load-tester/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "raiblocks-load-tester" +version = "0.1.0" + +[dependencies] +clap = "2.29.2" +error-chain = "0.11.0" +serde_json = "1.0.9" +serde = "1.0.27" +hyper = "0.11.15" +tokio-core = "0.1.12" +futures = "0.1.17" +rand = "0.4.2" +serde_derive = "1.0.27" +tokio-process = "0.1.5" +time = "0.1.39" diff --git a/load-tester/src/launch_node.rs b/load-tester/src/launch_node.rs new file mode 100644 index 00000000..a07da1a6 --- /dev/null +++ b/load-tester/src/launch_node.rs @@ -0,0 +1,125 @@ +use std::io; +use std::fs; +use std::fs::File; +use std::path::Path; +use std::process::Command; + +use serde_json; + +use futures::Future; +use hyper::client::Connect; + +use tokio_core::reactor::Handle; +use tokio_process::{Child, CommandExt}; + +use serde_json::Value; + +use errors::*; + +use rpc::RpcClient; + +const RPC_PORT_START: u64 = 55000; +const PEERING_PORT_START: u64 = 54000; + +pub fn launch_node( + rai_node: &Path, + tmp_dir: &Path, + handle: Handle, + i: u64, +) -> Result<(Child, RpcClient)> { + let data_dir = tmp_dir.join(format!("RaiBlocks_load_test_{}", i)); + match fs::create_dir(&data_dir) { + Ok(_) => {} + Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => { + let _ = fs::remove_file(data_dir.join("data.ldb")); + } + r => r.chain_err(|| "failed to create rai_node data directory")?, + } + let rpc_port = RPC_PORT_START + i; + let peering_port = PEERING_PORT_START + i; + let config = json!({ + "version": "2", + "rpc_enable": "true", + "rpc": { + "address": "::1", + "port": rpc_port.to_string(), + "enable_control": "true", + "frontier_request_limit": "16384", + "chain_request_limit": "16384", + }, + "node": { + "version": "8", + "peering_port": peering_port.to_string(), + "bootstrap_fraction_numerator": "1", + "receive_minimum": "1000000000000000000000000", + "logging": { + "version": "2", + "ledger": "false", + "ledger_duplicate": "false", + "vote": "false", + "network": "true", + "network_message": "false", + "network_publish": "false", + "network_packet": "false", + "network_keepalive": "false", + "node_lifetime_tracing": "false", + "insufficient_work": "true", + "log_rpc": "true", + "bulk_pull": "false", + "work_generation_time": "true", + "log_to_cerr": "false", + "max_size": "16777216", + }, + "work_peers": "", + "preconfigured_peers": "", + "preconfigured_representatives": [ + "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo" + ], + "inactive_supply": "0", + "password_fanout": "1024", + "io_threads": "8", + "work_threads": "8", + "enable_voting": "true", + "bootstrap_connections": "4", + "callback_address": "", + "callback_port": "0", + "callback_target": "", + "lmdb_max_dbs": "128", + }, + "opencl_enable": "false", + "opencl": { + "platform": "0", + "device": "0", + "threads": "1048576", + } + }); + let config_writer = + File::create(data_dir.join("config.json")).chain_err(|| "failed to create config.json")?; + serde_json::to_writer_pretty(config_writer, &config) + .chain_err(|| "failed to write config.json")?; + let child = Command::new(rai_node) + .arg("--data_path") + .arg(&data_dir) + .arg("--daemon") + .spawn_async(&handle) + .chain_err(|| "failed to spawn rai_node")?; + let rpc_client = RpcClient::new( + handle, + format!("http://[::1]:{}/", rpc_port).parse().unwrap(), + ); + Ok((child, rpc_client)) +} + +pub fn connect_node( + node: &RpcClient, + i: u64, +) -> Box> { + Box::new( + node.call::<_, Value>(&json!({ + "action": "keepalive", + "address": "::1", + "port": PEERING_PORT_START + i, + })).then(|x| x.chain_err(|| "failed to call rai_node RPC")) + .map(|_| ()), + ) as _ +} diff --git a/load-tester/src/main.rs b/load-tester/src/main.rs new file mode 100644 index 00000000..758c4ecd --- /dev/null +++ b/load-tester/src/main.rs @@ -0,0 +1,534 @@ +#![recursion_limit = "128"] + +use std::process; +use std::path::PathBuf; +use std::thread; +use std::time::{Duration, Instant}; +use std::collections::HashMap; +use std::iter; +use std::env; + +extern crate clap; +use clap::Arg; + +#[macro_use] +extern crate error_chain; + +extern crate futures; +use futures::{stream, Future, Stream}; + +extern crate serde; +#[macro_use] +extern crate serde_derive; +#[macro_use] +extern crate serde_json; +use serde_json::Value; + +extern crate tokio_core; +use tokio_core::reactor::Core; + +extern crate tokio_process; + +extern crate hyper; + +extern crate rand; +use rand::Rng; + +extern crate time; + +mod errors { + error_chain!{} +} +use errors::*; + +mod rpc; +use rpc::{RpcClient, RpcError}; + +mod launch_node; + +struct Parameters { + node_count: u16, + node_path: PathBuf, + tmp_dir: PathBuf, + send_count: usize, + dest_count: usize, + simultaneous_process_calls: usize, + catch_up_timeout: u64, + generate_receives: bool, + precompute_blocks: bool, + output_stats: bool, +} + +// found in secure.cpp +const GENESIS_ACCOUNT: &str = "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo"; +const GENESIS_PRIVKEY: &str = "34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4"; + +fn run(params: Parameters) -> Result<()> { + let output_stats = params.output_stats; + macro_rules! tstat { + ($( $x:tt )*) => { + if output_stats { + let now = time::now().to_timespec(); + println!("{}.{:09},{}", now.sec, now.nsec, format_args!( $( $x )* )); + } + } + } + let mut tokio_core = Core::new().chain_err(|| "failed to create tokio Core")?; + let mut children = Vec::with_capacity(params.node_count as _); + let mut nodes: Vec> = Vec::with_capacity(params.node_count as _); + for i in 0..params.node_count { + let (child, rpc_client) = launch_node::launch_node( + ¶ms.node_path, + ¶ms.tmp_dir, + tokio_core.handle(), + i as _, + )?; + children.push(child); + nodes.push(rpc_client); + } + if nodes.is_empty() { + bail!("no nodes spun up"); + } + eprintln!("Waiting for nodes to spin up..."); + thread::sleep(Duration::from_secs(7)); + eprintln!("Connecting nodes..."); + let primary_node = nodes.first().unwrap(); + for (a, node) in nodes.iter().enumerate() { + for b in 0..nodes.len() { + if a != b { + tokio_core.run(launch_node::connect_node(node, b as _))?; + } + } + } + thread::sleep(Duration::from_secs(5)); + eprintln!("Beginning tests"); + tstat!("start"); + #[derive(Debug, Deserialize, PartialEq, Eq)] + struct AccountInfo { + frontier: String, + balance: String, + block_count: String, + } + tstat!("genesis_info,start"); + let genesis_initial = primary_node.call(&json!({ + "action": "account_info", + "account": GENESIS_ACCOUNT, + })); + let genesis_initial: AccountInfo = tokio_core + .run(genesis_initial) + .chain_err(|| "failed to get genesis account info")?; + tstat!("genesis_info,done"); + let genesis_initial_balance = genesis_initial.balance; + #[derive(Deserialize)] + #[allow(dead_code)] + struct Account { + account: String, + private: String, + public: String, + } + tstat!("key_create,start"); + let dest_accts = stream::iter_ok(0..params.dest_count) + .map(|_| { + primary_node.call(&json!({ + "action": "key_create", + })) + }) + .buffer_unordered(10) // execute 10 `key_create`s simultaniously + .inspect(|_| { + tstat!("key_create,progress"); + }) + .collect(); + tstat!("key_create,done"); + let dest_accts: Vec = tokio_core + .run(dest_accts) + .chain_err(|| "failed to generate destination accounts")?; + if dest_accts.is_empty() { + bail!("no destination accounts generated"); + } + let mut frontiers = HashMap::new(); + #[derive(Deserialize)] + struct BlockInfo { + hash: String, + block: String, + } + let mut rng: rand::XorShiftRng = rand::thread_rng().gen(); + if params.precompute_blocks { + frontiers.insert(GENESIS_ACCOUNT, genesis_initial.frontier); + let mut last_percent = 0; + eprint!("Creating blocks: 00%"); + tstat!("block_create,start"); + let mut blocks: Vec = Vec::new(); + for i in 0..params.send_count { + let dest_acct = &rng.choose(&dest_accts).unwrap(); + // since only `balance - amount` matters, each block spends 1 raw + let send_future = primary_node.call(&json!({ + "action": "block_create", + "type": "send", + "key": GENESIS_PRIVKEY, + "balance": genesis_initial_balance, + "amount": i + 1, + "destination": dest_acct.account, + "previous": frontiers[GENESIS_ACCOUNT], + })); + // needs to be synchronous because of frontier ordering + let send: BlockInfo = tokio_core + .run(send_future) + .chain_err(|| "failed to create send block")?; + tstat!("block_create,progress,send"); + blocks.push(send.block); + frontiers.insert(GENESIS_ACCOUNT, send.hash.clone()); + if params.generate_receives { + let recv_future = if let Some(frontier) = frontiers.get(dest_acct.account.as_str()) + { + primary_node.call(&json!({ + "action": "block_create", + "type": "receive", + "key": dest_acct.private, + "previous": frontier, + "source": send.hash, + })) + } else { + primary_node.call(&json!({ + "action": "block_create", + "type": "open", + "key": dest_acct.private, + "source": send.hash, + "representative": GENESIS_ACCOUNT, + })) + }; + let recv: BlockInfo = tokio_core + .run(recv_future) + .chain_err(|| "failed to create receive block")?; + tstat!("block_create,progress,receive"); + frontiers.insert(&dest_acct.account, recv.hash); + blocks.push(recv.block); + } + let new_percent = (100 * i) / params.send_count; + if last_percent == new_percent { + continue; + } + last_percent = new_percent; + eprint!("\rCreating blocks: {:02}%", new_percent); + } + eprintln!("\rCreated blocks "); + tstat!("block_create,done"); + let mut process_calls_completed = 0; + last_percent = 0; + let n_blocks = blocks.len(); + eprint!("Primary node processing blocks: 00%"); + tstat!("process,start"); + let process = stream::iter_ok(blocks.iter()) + .map(|block| { + primary_node.call::<_, Value>(&json!({ + "action": "process", + "block": block, + })) + }) + .buffer_unordered(params.simultaneous_process_calls) + .then(|r| match r { + Ok(_) => Ok(()), + Err(RpcError::RpcError(Value::String(ref s))) + if params.simultaneous_process_calls != 1 && s.starts_with("Gap") => + { + Ok(()) + } + Err(err) => Err(err), + }) + .inspect(|_| { + process_calls_completed += 1; + let new_percent = (100 * process_calls_completed) / n_blocks; + if last_percent == new_percent { + return; + } + last_percent = new_percent; + eprint!("\rPrimary node processing blocks: {:02}%", new_percent); + tstat!("process,progress"); + }); + tokio_core + .run(process.fold((), |_, _| Ok(()))) + .chain_err(|| "failed to process blocks")?; + tstat!("process,done"); + eprintln!("\rPrimary node processed blocks "); + } else { + #[derive(Deserialize)] + struct WalletInfo { + wallet: String, + } + let wallet = primary_node.call(&json!({ + "action": "wallet_create", + })); + let wallet: WalletInfo = tokio_core + .run(wallet) + .chain_err(|| "failed to create wallet")?; + let wallet = wallet.wallet; + tstat!("wallet_add_key,start"); + let add_genesis = primary_node.call::<_, Value>(&json!({ + "action": "wallet_add", + "wallet": wallet, + "key": GENESIS_PRIVKEY, + })); + tokio_core + .run(add_genesis) + .chain_err(|| "failed to add genesis key to wallet")?; + tstat!("wallet_add_key,progress"); + let add_keys = stream::iter_ok(dest_accts.iter()) + .map(|acct| { + primary_node.call::<_, Value>(&json!({ + "action": "wallet_add", + "wallet": wallet, + "key": acct.private, + "work": false, // We always manually call `receive` + })) + }) + .buffer_unordered(10) + .inspect(|_| tstat!("wallet_add_key,progress")); + tokio_core + .run(add_keys.fold((), |_, _| Ok(()))) + .chain_err(|| "failed to add keys to wallet")?; + tstat!("wallet_add_key,done"); + let mut send_calls_completed = 0; + let mut last_percent = 0; + #[derive(Deserialize)] + struct TransactionInfo { + block: String, + } + eprint!("\rPrimary node processing transactions: 00%"); + tstat!("transaction,start"); + let wallet = wallet.as_str(); + let dest_accts = dest_accts.as_slice(); + let params = ¶ms; + let stream = stream::iter_ok(0..params.send_count) + .map(move |_| { + let dest_acct = rng.choose(dest_accts).unwrap(); + let send_future = primary_node + .call::<_, TransactionInfo>(&json!({ + "action": "send", + "wallet": wallet, + "source": GENESIS_ACCOUNT, + "destination": dest_acct.account, + "amount": "1", + })) + .inspect(move |_| { + tstat!("transaction,progress,send"); + }); + if params.generate_receives { + Box::new(send_future.and_then(move |send| { + primary_node + .call::<_, TransactionInfo>(&json!({ + "action": "receive", + "wallet": wallet, + "account": dest_acct.account, + "block": send.block, + })) + .inspect(move |_| { + tstat!("transaction,progress,receive"); + }) + })) as Box> + } else { + Box::new(send_future) as Box> + } + }) + .buffer_unordered(params.simultaneous_process_calls) + .inspect(|_| { + send_calls_completed += 1; + let new_percent = (100 * send_calls_completed) / params.send_count; + if last_percent == new_percent { + return; + } + last_percent = new_percent; + eprint!( + "\rPrimary node processing transactions: {:02}%", + new_percent + ); + }); + tokio_core + .run(stream.fold((), |_, _| Ok(()))) + .chain_err(|| "failed to process transactions")?; + eprintln!("\rPrimary node processed transactions "); + tstat!("transaction,progress,done"); + } + let broadcasted_at = Instant::now(); + eprintln!("Waiting for nodes to catch up..."); + let timeout = Duration::from_secs(params.catch_up_timeout); + let mut known_account_info = HashMap::new(); + tstat!("check,start"); + for node in &nodes { + for (&acct, frontier) in frontiers.iter() { + // We only know frontiers if `precompute_blocks` is enabled. + loop { + if Instant::now() - broadcasted_at > timeout { + bail!("timed out while waiting for nodes to catch up"); + } + let acct_info = node.call::<_, AccountInfo>(&json!({ + "action": "account_info", + "account": acct, + })); + let acct_info = tokio_core + .run(acct_info) + .chain_err(|| "failed to check genesis account info")?; + if &acct_info.frontier == frontier { + break; + } + thread::sleep(Duration::from_secs(1)); + } + } + if !frontiers.is_empty() { + tstat!("check,progress"); + } + if node as *const _ == primary_node as *const _ { + for acct in dest_accts + .iter() + .map(|a| a.account.as_str()) + .chain(iter::once(GENESIS_ACCOUNT)) + { + let acct_info = node.call(&json!({ + "action": "account_info", + "account": acct, + })); + let acct_info: AccountInfo = tokio_core.run(acct_info).chain_err(|| { + format!("failed to check account {} info on primary node", acct) + })?; + known_account_info.insert(acct, acct_info); + } + } else { + for (&acct, acct_info) in known_account_info.iter() { + loop { + if Instant::now() - broadcasted_at > timeout { + bail!("timed out while waiting for nodes to catch up"); + } + let node_acct_info = node.call(&json!({ + "action": "account_info", + "account": acct, + })); + let node_acct_info = tokio_core.run(node_acct_info); + match node_acct_info { + Err(RpcError::RpcError(ref s)) if s == "Account not found" => {} + Ok(node_acct_info) => if acct_info == &node_acct_info { + break; + }, + r => { + r.chain_err(|| { + format!("failed to check account {} info on secondary node", acct) + })?; + } + } + thread::sleep(Duration::from_secs(1)); + } + } + } + tstat!("check,progress"); + } + tstat!("check,done"); + eprintln!("Done!"); + tstat!("done"); + Ok(()) +} + +fn main() { + let matches = clap::App::new("raiblocks-load-tester") + .version(env!("CARGO_PKG_VERSION")) + .arg( + Arg::with_name("node_count") + .short("n") + .long("node_count") + .value_name("N") + .default_value("10") + .help("The number of nodes to spin up"), + ) + .arg( + Arg::with_name("tmp_dir") + .long("tmp-dir") + .value_name("PATH") + .help("The path to a temporary directory for rai_node data"), + ) + .arg( + Arg::with_name("node_path") + .value_name("PATH") + .required(true) + .help("The path to the rai_node to test"), + ) + .arg( + Arg::with_name("send_count") + .short("s") + .short("send-count") + .value_name("N") + .default_value("2000") + .help("How many send blocks to generate"), + ) + .arg( + Arg::with_name("destination_count") + .short("destination-count") + .value_name("N") + .default_value("2") + .help("How many destination accounts to choose between"), + ) + .arg( + Arg::with_name("simultaneous_process_calls") + .long("simultaneous-process-calls") + .value_name("N") + .default_value("20") + .help("How many `process` or `send` calls to send at a given time"), + ) + .arg( + Arg::with_name("catch_up_timeout") + .long("catch-up-timeout") + .value_name("SECONDS") + .default_value("120") + .help("The maximum number of seconds to wait for nodes to catch up"), + ) + .arg( + Arg::with_name("no_receives") + .long("no-receives") + .help("Do not generate receives for the generated sends"), + ) + .arg( + Arg::with_name("precompute_blocks") + .long("precompute-blocks") + .help("Use the `block_create` and `process` endpoints to precompute blocks"), + ) + .arg( + Arg::with_name("stats") + .long("stats") + .help("Output stats on how long steps take in CSV format"), + ) + .get_matches(); + macro_rules! num_arg { + ($arg:expr) => { + match matches.value_of($arg).unwrap().parse() { + Ok(n) => n, + Err(err) => { + eprintln!("Failed to parse {}: {}", $arg, err); + process::exit(2); + } + } + }; + } + let params = Parameters { + node_count: num_arg!("node_count"), + node_path: matches.value_of("node_path").unwrap().into(), + tmp_dir: matches + .value_of("tmp_dir") + .or(env::var("TMPDIR").ok().as_ref().map(|x| x.as_str())) + .unwrap_or("/tmp") + .into(), + send_count: num_arg!("send_count"), + dest_count: num_arg!("destination_count"), + simultaneous_process_calls: num_arg!("simultaneous_process_calls"), + catch_up_timeout: num_arg!("catch_up_timeout"), + generate_receives: !matches.is_present("no_receives"), + precompute_blocks: matches.is_present("precompute_blocks"), + output_stats: matches.is_present("stats"), + }; + if let Err(ref e) = run(params) { + eprintln!("Error: {}", e); + for e in e.iter().skip(1) { + eprintln!(" caused by: {}", e); + } + + if let Some(backtrace) = e.backtrace() { + eprintln!("\nBacktrace:\n{:?}", backtrace); + } + + process::exit(1); + } +} diff --git a/load-tester/src/rpc.rs b/load-tester/src/rpc.rs new file mode 100644 index 00000000..fde5cce0 --- /dev/null +++ b/load-tester/src/rpc.rs @@ -0,0 +1,95 @@ +use std::fmt; + +use futures::{Future, Stream}; +use futures::future; + +use tokio_core::reactor::Handle; + +use hyper::{self, Request, Uri}; +use hyper::client::{Connect, HttpConnector}; +use hyper::header::{ContentLength, ContentType}; + +use serde::{Deserialize, Serialize}; + +use serde_json::{self, Value}; + +#[derive(Debug)] +pub enum RpcError { + Http(hyper::Error), + Json(serde_json::Error), + /// An error returned from the RPC + RpcError(Value), +} + +impl fmt::Display for RpcError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + RpcError::Http(ref err) => fmt::Display::fmt(err, f), + RpcError::Json(ref err) => fmt::Display::fmt(err, f), + RpcError::RpcError(ref val) => if let Some(s) = val.as_str() { + fmt::Display::fmt(s, f) + } else { + fmt::Display::fmt(val, f) + }, + } + } +} + +impl ::std::error::Error for RpcError { + fn description(&self) -> &str { + match *self { + RpcError::Http(_) => "HTTP error", + RpcError::Json(_) => "JSON parsing error", + RpcError::RpcError(_) => "RPC returned error", + } + } +} + +pub struct RpcClient { + http_client: hyper::Client, + uri: Uri, +} + +impl RpcClient { + pub fn new(tokio_handle: Handle, rpc_uri: Uri) -> RpcClient { + RpcClient::from_hyper_client(hyper::Client::new(&tokio_handle), rpc_uri) + } +} + +impl RpcClient { + pub fn from_hyper_client(hyper_client: hyper::Client, rpc_uri: Uri) -> RpcClient { + RpcClient { + http_client: hyper_client, + uri: rpc_uri, + } + } + + pub fn call<'a, I: Serialize, O: Deserialize<'a> + 'static>( + &self, + request: &'a I, + ) -> Box> { + let json = match serde_json::to_vec(request).map_err(RpcError::Json) { + Ok(json) => json, + Err(err) => return Box::new(future::err(err)) as _, + }; + let mut req = Request::new(hyper::Method::Post, self.uri.clone()); + req.headers_mut().set(ContentType::json()); + req.headers_mut().set(ContentLength(json.len() as u64)); + req.set_body(json); + Box::new( + self.http_client + .request(req) + .and_then(|res| res.body().concat2()) + .map_err(RpcError::Http) + .and_then(|slice| serde_json::from_slice(&slice).map_err(RpcError::Json)) + .and_then(|mut json: Value| { + if let Some(obj) = json.as_object_mut() { + if let Some(err) = obj.remove("error") { + return Err(RpcError::RpcError(err)); + } + } + O::deserialize(json).map_err(RpcError::Json) + }), + ) as _ + } +} diff --git a/rai/node/rpc.cpp b/rai/node/rpc.cpp index 843f846f..82af20da 100644 --- a/rai/node/rpc.cpp +++ b/rai/node/rpc.cpp @@ -4127,6 +4127,7 @@ void rai::rpc_connection::parse_connection () this_l->res.set ("Content-Type", "application/json"); this_l->res.set ("Access-Control-Allow-Origin", "*"); this_l->res.set ("Access-Control-Allow-Headers", "Accept, Accept-Language, Content-Language, Content-Type"); + this_l->res.set ("Connection", "close"); this_l->res.result (boost::beast::http::status::ok); this_l->res.body () = body; this_l->res.version (version);