mirror of
https://github.com/YGGverse/aquatic.git
synced 2026-03-31 17:55:36 +00:00
Add aquatic_load_tester: multi-run multi-implementation load tests
- Work in progress - Only UDP is currently implemented so far - Also includes some changes to other crates, notably deriving serde Serialize for Config structs and making udp_load_test a lib and a binary
This commit is contained in:
parent
c7997d5aed
commit
afc3deb656
18 changed files with 1666 additions and 312 deletions
431
Cargo.lock
generated
431
Cargo.lock
generated
|
|
@ -62,12 +62,54 @@ version = "0.1.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
|
|
@ -96,7 +138,7 @@ dependencies = [
|
|||
"duplicate",
|
||||
"git-testament",
|
||||
"glommio",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
"hex",
|
||||
"hwloc",
|
||||
"indexmap 2.1.0",
|
||||
|
|
@ -108,7 +150,7 @@ dependencies = [
|
|||
"rustls-pemfile",
|
||||
"serde",
|
||||
"simple_logger",
|
||||
"toml",
|
||||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -159,7 +201,7 @@ dependencies = [
|
|||
"futures-lite",
|
||||
"futures-rustls",
|
||||
"glommio",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
"log",
|
||||
"mimalloc",
|
||||
"quickcheck",
|
||||
|
|
@ -190,6 +232,24 @@ dependencies = [
|
|||
"urlencoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aquatic_load_tester"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"aquatic_udp",
|
||||
"aquatic_udp_load_test",
|
||||
"clap 4.4.8",
|
||||
"indexmap 2.1.0",
|
||||
"itertools 0.12.0",
|
||||
"nonblock",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"tempfile",
|
||||
"toml 0.8.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aquatic_peer_id"
|
||||
version = "0.8.0"
|
||||
|
|
@ -210,7 +270,7 @@ dependencies = [
|
|||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"serde",
|
||||
"toml",
|
||||
"toml 0.5.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -236,7 +296,7 @@ dependencies = [
|
|||
"constant_time_eq",
|
||||
"crossbeam-channel",
|
||||
"getrandom",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
"hdrhistogram",
|
||||
"hex",
|
||||
"io-uring",
|
||||
|
|
@ -269,7 +329,7 @@ dependencies = [
|
|||
"aquatic_common",
|
||||
"aquatic_toml_config",
|
||||
"aquatic_udp_protocol",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
"mimalloc",
|
||||
"mio",
|
||||
"quickcheck",
|
||||
|
|
@ -309,7 +369,7 @@ dependencies = [
|
|||
"futures-lite",
|
||||
"futures-rustls",
|
||||
"glommio",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
"httparse",
|
||||
"indexmap 2.1.0",
|
||||
"log",
|
||||
|
|
@ -361,7 +421,7 @@ version = "0.8.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"criterion 0.5.1",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
"quickcheck",
|
||||
"quickcheck_macros",
|
||||
"serde",
|
||||
|
|
@ -433,6 +493,12 @@ dependencies = [
|
|||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.5"
|
||||
|
|
@ -598,21 +664,36 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.4.11"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
|
||||
checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.4.11"
|
||||
version = "4.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
|
||||
checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"clap_lex 0.6.0",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.39",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -631,11 +712,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.1.0"
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||
dependencies = [
|
||||
"is-terminal",
|
||||
"lazy_static",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
|
@ -699,7 +787,7 @@ dependencies = [
|
|||
"ciborium",
|
||||
"clap 3.2.25",
|
||||
"criterion-plot",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"oorandom",
|
||||
|
|
@ -722,10 +810,10 @@ dependencies = [
|
|||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap 4.4.11",
|
||||
"clap 4.4.8",
|
||||
"criterion-plot",
|
||||
"is-terminal",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
|
|
@ -746,7 +834,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -828,15 +916,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.5.0"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
|
||||
checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.10"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc"
|
||||
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
|
@ -920,12 +1008,12 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -993,9 +1081,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
|||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
|
||||
checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
|
@ -1139,9 +1227,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.1"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "git-testament"
|
||||
|
|
@ -1233,9 +1321,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
dependencies = [
|
||||
"ahash 0.8.6",
|
||||
"allocator-api2",
|
||||
|
|
@ -1244,11 +1332,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "hdrhistogram"
|
||||
version = "7.5.4"
|
||||
version = "7.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
|
||||
checksum = "a5b38e5c02b7c7be48c8dc5217c4f1634af2ea221caae2e024bffc7a7651c691"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.13.1",
|
||||
"byteorder",
|
||||
"crossbeam-channel",
|
||||
"flate2",
|
||||
|
|
@ -1296,9 +1384,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
|
|
@ -1357,9 +1445,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.5.0"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
|
|
@ -1382,7 +1470,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
"hashbrown 0.14.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1440,16 +1528,25 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.66"
|
||||
version = "0.3.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
|
||||
checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
|
@ -1558,9 +1655,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.12"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
|
@ -1646,7 +1743,7 @@ version = "0.12.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a4964177ddfdab1e3a2b37aec7cf320e14169abb0ed73999f558136409178d5"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.5",
|
||||
"hyper",
|
||||
"indexmap 1.9.3",
|
||||
"ipnet",
|
||||
|
|
@ -1713,9 +1810,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.10"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
|
||||
checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
|
|
@ -1777,6 +1874,15 @@ dependencies = [
|
|||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonblock"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51c7a4f22e5f2e2bd805d6ab56f1ae87eb1815673e1b452048896fb687a8a3d4"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.1.42"
|
||||
|
|
@ -1859,9 +1965,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.19.0"
|
||||
version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
|
|
@ -1921,9 +2027,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
|
|
@ -1993,9 +2099,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.6.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
|
||||
checksum = "3bccab0e7fd7cc19f820a1c8c91720af652d0c88dc9664dd72aef2614f04af3b"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
|
|
@ -2055,9 +2161,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.70"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
|
||||
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
|
@ -2228,9 +2334,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
|||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.7"
|
||||
version = "0.17.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
|
||||
checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"getrandom",
|
||||
|
|
@ -2257,22 +2363,22 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.28"
|
||||
version = "0.38.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
||||
checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno 0.3.8",
|
||||
"errno 0.3.7",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.21.10"
|
||||
version = "0.21.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
|
|
@ -2286,7 +2392,7 @@ version = "1.0.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2307,9 +2413,9 @@ checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
|||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
|
|
@ -2344,9 +2450,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
version = "1.0.192"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
|
@ -2372,9 +2478,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
version = "1.0.192"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2392,6 +2498,15 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
|
|
@ -2445,14 +2560,14 @@ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
|||
|
||||
[[package]]
|
||||
name = "simple_logger"
|
||||
version = "4.3.0"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da0ca6504625ee1aa5fda33913d2005eab98c7a42dd85f116ecce3ff54c9d3ef"
|
||||
checksum = "2230cd5c29b815c9b699fb610b49a5ed65588f3509d9f0108be3a885da629333"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"log",
|
||||
"time",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2478,9 +2593,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.7"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
|
||||
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
|
@ -2548,6 +2663,12 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
|
|
@ -2677,9 +2798,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.35.0"
|
||||
version = "1.34.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c"
|
||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"libc",
|
||||
|
|
@ -2698,6 +2819,40 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.21.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
|
||||
dependencies = [
|
||||
"indexmap 2.1.0",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
|
|
@ -2737,9 +2892,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
|
||||
|
||||
[[package]]
|
||||
name = "tungstenite"
|
||||
|
|
@ -2768,9 +2923,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.14"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
|
|
@ -2795,9 +2950,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
|
|
@ -2816,6 +2971,12 @@ version = "0.7.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "value-trait"
|
||||
version = "0.6.1"
|
||||
|
|
@ -2867,9 +3028,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
|
||||
checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
|
@ -2877,9 +3038,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
|
||||
checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
|
@ -2892,9 +3053,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
|
||||
checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -2902,9 +3063,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||
checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -2915,15 +3076,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.89"
|
||||
version = "0.2.88"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.66"
|
||||
version = "0.3.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
|
||||
checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
|
@ -2972,6 +3133,21 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
|
|
@ -3020,6 +3196,12 @@ dependencies = [
|
|||
"windows_x86_64_msvc 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3032,6 +3214,12 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3044,6 +3232,12 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3056,6 +3250,12 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3068,6 +3268,12 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3080,6 +3286,12 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3092,6 +3304,12 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
|
|
@ -3104,6 +3322,15 @@ version = "0.52.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
|
|
@ -3112,9 +3339,9 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.30"
|
||||
version = "0.7.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7"
|
||||
checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
|
|
@ -3122,9 +3349,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.30"
|
||||
version = "0.7.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
|
||||
checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ members = [
|
|||
"crates/http",
|
||||
"crates/http_load_test",
|
||||
"crates/http_protocol",
|
||||
"crates/load_tester",
|
||||
"crates/peer_id",
|
||||
"crates/toml_config",
|
||||
"crates/toml_config_derive",
|
||||
|
|
@ -35,6 +36,7 @@ aquatic_toml_config = { version = "0.8.0", path = "./crates/toml_config" }
|
|||
aquatic_toml_config_derive = { version = "0.8.0", path = "./crates/toml_config_derive" }
|
||||
aquatic_udp_protocol = { version = "0.8.0", path = "./crates/udp_protocol" }
|
||||
aquatic_udp = { version = "0.8.0", path = "./crates/udp" }
|
||||
aquatic_udp_load_test = { version = "0.8.0", path = "./crates/udp_load_test" }
|
||||
aquatic_ws_protocol = { version = "0.8.0", path = "./crates/ws_protocol" }
|
||||
aquatic_ws = { version = "0.8.0", path = "./crates/ws" }
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ impl AccessListMode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct AccessListConfig {
|
||||
pub mode: AccessListMode,
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ pub mod mod_name {
|
|||
use super::*;
|
||||
|
||||
/// Experimental cpu pinning
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
pub struct struct_name {
|
||||
pub active: bool,
|
||||
pub direction: CpuPinningDirection,
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ use std::{
|
|||
|
||||
use anyhow::Context;
|
||||
use privdrop::PrivDrop;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use aquatic_toml_config::TomlConfig;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct PrivilegeConfig {
|
||||
/// Chroot and switch group and user after binding to sockets
|
||||
|
|
|
|||
35
crates/load_tester/Cargo.toml
Normal file
35
crates/load_tester/Cargo.toml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
[package]
|
||||
name = "aquatic_load_tester"
|
||||
description = "Load test runner for aquatic BitTorrent tracker"
|
||||
keywords = ["peer-to-peer", "torrent", "bittorrent"]
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
repository.workspace = true
|
||||
rust-version.workspace = true
|
||||
readme.workspace = true
|
||||
|
||||
[[bin]]
|
||||
name = "aquatic_load_tester"
|
||||
|
||||
[features]
|
||||
default = ["udp"]
|
||||
udp = ["aquatic_udp", "aquatic_udp_load_test"]
|
||||
|
||||
[dependencies]
|
||||
aquatic_udp = { optional = true, workspace = true }
|
||||
aquatic_udp_load_test = { optional = true, workspace = true }
|
||||
|
||||
anyhow = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
indexmap = "2"
|
||||
itertools = "0.12"
|
||||
nonblock = "0.2"
|
||||
once_cell = "1"
|
||||
regex = "1"
|
||||
serde = "1"
|
||||
tempfile = "3"
|
||||
toml = "0.8"
|
||||
|
||||
[dev-dependencies]
|
||||
258
crates/load_tester/src/common.rs
Normal file
258
crates/load_tester/src/common.rs
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
use std::{fmt::Display, ops::Range, thread::available_parallelism};
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskSetCpuList(pub Vec<TaskSetCpuIndicator>);
|
||||
|
||||
impl TaskSetCpuList {
|
||||
pub fn as_cpu_list(&self) -> String {
|
||||
let indicator = self.0.iter().map(|indicator| match indicator {
|
||||
TaskSetCpuIndicator::Single(i) => i.to_string(),
|
||||
TaskSetCpuIndicator::Range(range) => {
|
||||
format!(
|
||||
"{}-{}",
|
||||
range.start,
|
||||
range.clone().into_iter().last().unwrap()
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
Itertools::intersperse_with(indicator, || ",".to_string())
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
mode: CpuMode,
|
||||
direction: CpuDirection,
|
||||
requested_cpus: usize,
|
||||
) -> anyhow::Result<Self> {
|
||||
let available_parallelism: usize = available_parallelism()?.into();
|
||||
|
||||
Ok(Self::new_with_available_parallelism(
|
||||
available_parallelism,
|
||||
mode,
|
||||
direction,
|
||||
requested_cpus,
|
||||
))
|
||||
}
|
||||
|
||||
fn new_with_available_parallelism(
|
||||
available_parallelism: usize,
|
||||
mode: CpuMode,
|
||||
direction: CpuDirection,
|
||||
requested_cpus: usize,
|
||||
) -> Self {
|
||||
match direction {
|
||||
CpuDirection::Asc => match mode {
|
||||
CpuMode::Split => {
|
||||
let middle = available_parallelism / 2;
|
||||
|
||||
let range_a = 0..(middle.min(requested_cpus));
|
||||
let range_b = middle..(available_parallelism.min(middle + requested_cpus));
|
||||
|
||||
Self(vec![
|
||||
range_a.try_into().unwrap(),
|
||||
range_b.try_into().unwrap(),
|
||||
])
|
||||
}
|
||||
CpuMode::All => {
|
||||
let range = 0..(available_parallelism.min(requested_cpus));
|
||||
|
||||
Self(vec![range.try_into().unwrap()])
|
||||
}
|
||||
},
|
||||
CpuDirection::Desc => match mode {
|
||||
CpuMode::Split => {
|
||||
let middle = available_parallelism / 2;
|
||||
|
||||
let range_a = middle.saturating_sub(requested_cpus)..middle;
|
||||
let range_b = available_parallelism
|
||||
.saturating_sub(requested_cpus)
|
||||
.max(middle)..available_parallelism;
|
||||
|
||||
Self(vec![
|
||||
range_a.try_into().unwrap(),
|
||||
range_b.try_into().unwrap(),
|
||||
])
|
||||
}
|
||||
CpuMode::All => {
|
||||
let range =
|
||||
available_parallelism.saturating_sub(requested_cpus)..available_parallelism;
|
||||
|
||||
Self(vec![range.try_into().unwrap()])
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<Range<usize>>> for TaskSetCpuList {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: Vec<Range<usize>>) -> Result<Self, Self::Error> {
|
||||
let mut output = Vec::new();
|
||||
|
||||
for range in value {
|
||||
output.push(range.try_into()?);
|
||||
}
|
||||
|
||||
Ok(Self(output))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum TaskSetCpuIndicator {
|
||||
Single(usize),
|
||||
Range(Range<usize>),
|
||||
}
|
||||
|
||||
impl TryFrom<Range<usize>> for TaskSetCpuIndicator {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: Range<usize>) -> Result<Self, Self::Error> {
|
||||
match value.len() {
|
||||
0 => Err("Empty ranges not supported".into()),
|
||||
1 => Ok(TaskSetCpuIndicator::Single(value.start)),
|
||||
_ => Ok(TaskSetCpuIndicator::Range(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, clap::ValueEnum)]
|
||||
pub enum CpuMode {
|
||||
Split,
|
||||
All,
|
||||
}
|
||||
|
||||
impl Display for CpuMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::All => f.write_str("all"),
|
||||
Self::Split => f.write_str("split"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CpuDirection {
|
||||
Asc,
|
||||
Desc,
|
||||
}
|
||||
|
||||
pub fn simple_load_test_runs(cpu_mode: CpuMode, workers: &[usize]) -> Vec<(usize, TaskSetCpuList)> {
|
||||
workers
|
||||
.into_iter()
|
||||
.copied()
|
||||
.map(|workers| {
|
||||
(
|
||||
workers,
|
||||
TaskSetCpuList::new(cpu_mode, CpuDirection::Desc, workers).unwrap(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_task_set_cpu_list_split_asc() {
|
||||
let f = TaskSetCpuList::new_with_available_parallelism;
|
||||
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Asc, 1).as_cpu_list(),
|
||||
"0,4"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Asc, 2).as_cpu_list(),
|
||||
"0-1,4-5"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Asc, 4).as_cpu_list(),
|
||||
"0-3,4-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Asc, 8).as_cpu_list(),
|
||||
"0-3,4-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Asc, 9).as_cpu_list(),
|
||||
"0-3,4-7"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task_set_cpu_list_split_desc() {
|
||||
let f = TaskSetCpuList::new_with_available_parallelism;
|
||||
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Desc, 1).as_cpu_list(),
|
||||
"3,7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Desc, 2).as_cpu_list(),
|
||||
"2-3,6-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Desc, 4).as_cpu_list(),
|
||||
"0-3,4-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Desc, 8).as_cpu_list(),
|
||||
"0-3,4-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::Split, CpuDirection::Desc, 9).as_cpu_list(),
|
||||
"0-3,4-7"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task_set_cpu_list_all_asc() {
|
||||
let f = TaskSetCpuList::new_with_available_parallelism;
|
||||
|
||||
assert_eq!(f(8, CpuMode::All, CpuDirection::Asc, 1).as_cpu_list(), "0");
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Asc, 2).as_cpu_list(),
|
||||
"0-1"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Asc, 4).as_cpu_list(),
|
||||
"0-3"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Asc, 8).as_cpu_list(),
|
||||
"0-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Asc, 9).as_cpu_list(),
|
||||
"0-7"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_task_set_cpu_list_all_desc() {
|
||||
let f = TaskSetCpuList::new_with_available_parallelism;
|
||||
|
||||
assert_eq!(f(8, CpuMode::All, CpuDirection::Desc, 1).as_cpu_list(), "7");
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Desc, 2).as_cpu_list(),
|
||||
"6-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Desc, 4).as_cpu_list(),
|
||||
"4-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Desc, 8).as_cpu_list(),
|
||||
"0-7"
|
||||
);
|
||||
assert_eq!(
|
||||
f(8, CpuMode::All, CpuDirection::Desc, 9).as_cpu_list(),
|
||||
"0-7"
|
||||
);
|
||||
}
|
||||
}
|
||||
28
crates/load_tester/src/main.rs
Normal file
28
crates/load_tester/src/main.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
pub mod common;
|
||||
pub mod protocols;
|
||||
pub mod run;
|
||||
pub mod set;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about)]
|
||||
struct Args {
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Command {
|
||||
#[cfg(feature = "udp")]
|
||||
Udp(protocols::udp::UdpCommand),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
match args.command {
|
||||
#[cfg(feature = "udp")]
|
||||
Command::Udp(command) => command.run().unwrap(),
|
||||
}
|
||||
}
|
||||
2
crates/load_tester/src/protocols/mod.rs
Normal file
2
crates/load_tester/src/protocols/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#[cfg(feature = "udp")]
|
||||
pub mod udp;
|
||||
236
crates/load_tester/src/protocols/udp.rs
Normal file
236
crates/load_tester/src/protocols/udp.rs
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
process::{Child, Command, Stdio},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use indexmap::{indexmap, IndexMap};
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::{
|
||||
common::{simple_load_test_runs, CpuMode, TaskSetCpuList},
|
||||
run::ProcessRunner,
|
||||
set::{run_sets, Server, SetConfig},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum UdpServer {
|
||||
Aquatic,
|
||||
OpenTracker,
|
||||
}
|
||||
|
||||
impl Server for UdpServer {
|
||||
fn name(&self) -> String {
|
||||
match self {
|
||||
Self::Aquatic => "aquatic_udp".into(),
|
||||
Self::OpenTracker => "opentracker".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct UdpCommand {
|
||||
#[arg(long, default_value_t = CpuMode::Split)]
|
||||
cpu_mode: CpuMode,
|
||||
#[arg(long, default_value = "./target/release-debug/aquatic_udp_load_test")]
|
||||
load_test: PathBuf,
|
||||
#[arg(long, default_value = "./target/release-debug/aquatic_udp")]
|
||||
aquatic: PathBuf,
|
||||
#[arg(long, default_value = "opentracker")]
|
||||
opentracker: PathBuf,
|
||||
}
|
||||
|
||||
impl UdpCommand {
|
||||
pub fn run(&self) -> anyhow::Result<()> {
|
||||
run_sets(self, self.cpu_mode, self.sets(), |workers| {
|
||||
Box::new(AquaticUdpLoadTestProcessConfig { workers })
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sets(&self) -> IndexMap<usize, SetConfig<UdpCommand, UdpServer>> {
|
||||
indexmap::indexmap! {
|
||||
1 => SetConfig {
|
||||
implementations: indexmap! {
|
||||
UdpServer::Aquatic => vec![
|
||||
Rc::new(AquaticUdpProcessConfig {
|
||||
socket_workers: 1,
|
||||
swarm_workers: 1,
|
||||
}) as Rc<dyn ProcessRunner<Command = UdpCommand>>,
|
||||
],
|
||||
/*
|
||||
UdpServer::OpenTracker => vec![
|
||||
Rc::new(OpenTrackerUdpProcessConfig {
|
||||
workers: 1,
|
||||
}) as Rc<dyn RunProcess<Command = UdpCommand>>,
|
||||
Rc::new(OpenTrackerUdpProcessConfig {
|
||||
workers: 2,
|
||||
}) as Rc<dyn RunProcess<Command = UdpCommand>>,
|
||||
],
|
||||
*/
|
||||
},
|
||||
load_test_runs: simple_load_test_runs(self.cpu_mode, &[1, 2, 4]),
|
||||
},
|
||||
2 => SetConfig {
|
||||
implementations: indexmap! {
|
||||
UdpServer::Aquatic => vec![
|
||||
Rc::new(AquaticUdpProcessConfig {
|
||||
socket_workers: 1,
|
||||
swarm_workers: 1,
|
||||
}) as Rc<dyn ProcessRunner<Command = UdpCommand>>,
|
||||
Rc::new(AquaticUdpProcessConfig {
|
||||
socket_workers: 2,
|
||||
swarm_workers: 1,
|
||||
}) as Rc<dyn ProcessRunner<Command = UdpCommand>>,
|
||||
],
|
||||
/*
|
||||
UdpServer::OpenTracker => vec![
|
||||
Rc::new(OpenTrackerUdpProcessConfig {
|
||||
workers: 2,
|
||||
}) as Rc<dyn RunProcess<Command = UdpCommand>>,
|
||||
Rc::new(OpenTrackerUdpProcessConfig {
|
||||
workers: 4,
|
||||
}) as Rc<dyn RunProcess<Command = UdpCommand>>,
|
||||
],
|
||||
*/
|
||||
},
|
||||
load_test_runs: simple_load_test_runs(self.cpu_mode, &[1, 2, 4]),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AquaticUdpProcessConfig {
|
||||
socket_workers: usize,
|
||||
swarm_workers: usize,
|
||||
}
|
||||
|
||||
impl ProcessRunner for AquaticUdpProcessConfig {
|
||||
type Command = UdpCommand;
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
command: &Self::Command,
|
||||
vcpus: &TaskSetCpuList,
|
||||
tmp_file: &mut NamedTempFile,
|
||||
) -> anyhow::Result<Child> {
|
||||
let mut c = aquatic_udp::config::Config::default();
|
||||
|
||||
c.socket_workers = self.socket_workers;
|
||||
c.swarm_workers = self.swarm_workers;
|
||||
|
||||
let c = toml::to_string_pretty(&c)?;
|
||||
|
||||
tmp_file.write_all(c.as_bytes())?;
|
||||
|
||||
Ok(Command::new("taskset")
|
||||
.arg("--cpu-list")
|
||||
.arg(vcpus.as_cpu_list())
|
||||
.arg(&command.aquatic)
|
||||
.arg("-c")
|
||||
.arg(tmp_file.path())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?)
|
||||
}
|
||||
|
||||
fn info(&self) -> String {
|
||||
format!(
|
||||
"socket workers: {}, swarm workers: {}",
|
||||
self.socket_workers, self.swarm_workers
|
||||
)
|
||||
}
|
||||
fn keys(&self) -> IndexMap<String, String> {
|
||||
indexmap! {
|
||||
"socket workers".to_string() => self.socket_workers.to_string(),
|
||||
"swarm workers".to_string() => self.swarm_workers.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpenTrackerUdpProcessConfig {
|
||||
workers: usize,
|
||||
}
|
||||
|
||||
impl ProcessRunner for OpenTrackerUdpProcessConfig {
|
||||
type Command = UdpCommand;
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
command: &Self::Command,
|
||||
vcpus: &TaskSetCpuList,
|
||||
tmp_file: &mut NamedTempFile,
|
||||
) -> anyhow::Result<Child> {
|
||||
writeln!(tmp_file, "{}", self.workers)?; // FIXME
|
||||
|
||||
Ok(Command::new("taskset")
|
||||
.arg("--cpu-list")
|
||||
.arg(vcpus.as_cpu_list())
|
||||
.arg(&command.opentracker)
|
||||
.arg("-f")
|
||||
.arg(tmp_file.path())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?)
|
||||
}
|
||||
|
||||
fn info(&self) -> String {
|
||||
format!("workers: {}", self.workers)
|
||||
}
|
||||
|
||||
fn keys(&self) -> IndexMap<String, String> {
|
||||
indexmap! {
|
||||
"workers".to_string() => self.workers.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AquaticUdpLoadTestProcessConfig {
|
||||
workers: usize,
|
||||
}
|
||||
|
||||
impl ProcessRunner for AquaticUdpLoadTestProcessConfig {
|
||||
type Command = UdpCommand;
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
command: &Self::Command,
|
||||
vcpus: &TaskSetCpuList,
|
||||
tmp_file: &mut NamedTempFile,
|
||||
) -> anyhow::Result<Child> {
|
||||
let mut c = aquatic_udp_load_test::config::Config::default();
|
||||
|
||||
c.workers = self.workers as u8;
|
||||
c.duration = 60;
|
||||
|
||||
let c = toml::to_string_pretty(&c)?;
|
||||
|
||||
tmp_file.write_all(c.as_bytes())?;
|
||||
|
||||
Ok(Command::new("taskset")
|
||||
.arg("--cpu-list")
|
||||
.arg(vcpus.as_cpu_list())
|
||||
.arg(&command.load_test)
|
||||
.arg("-c")
|
||||
.arg(tmp_file.path())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?)
|
||||
}
|
||||
|
||||
fn info(&self) -> String {
|
||||
format!("workers: {}", self.workers)
|
||||
}
|
||||
|
||||
fn keys(&self) -> IndexMap<String, String> {
|
||||
indexmap! {
|
||||
"workers".to_string() => self.workers.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
274
crates/load_tester/src/run.rs
Normal file
274
crates/load_tester/src/run.rs
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
use std::{
|
||||
process::{Child, Command},
|
||||
rc::Rc,
|
||||
str::FromStr,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use nonblock::NonBlockingReader;
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
use crate::common::TaskSetCpuList;
|
||||
|
||||
pub trait ProcessRunner: ::std::fmt::Debug {
|
||||
type Command;
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
command: &Self::Command,
|
||||
vcpus: &TaskSetCpuList,
|
||||
tmp_file: &mut NamedTempFile,
|
||||
) -> anyhow::Result<Child>;
|
||||
fn info(&self) -> String;
|
||||
fn keys(&self) -> IndexMap<String, String>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RunConfig<C> {
|
||||
pub server_runner: Rc<dyn ProcessRunner<Command = C>>,
|
||||
pub server_vcpus: TaskSetCpuList,
|
||||
pub load_test_runner: Box<dyn ProcessRunner<Command = C>>,
|
||||
pub load_test_vcpus: TaskSetCpuList,
|
||||
}
|
||||
|
||||
impl<C> RunConfig<C> {
|
||||
pub fn run(self, command: &C) -> Result<RunResults<C>, RunResults<C>> {
|
||||
let mut server_config_file = NamedTempFile::new().unwrap();
|
||||
let mut load_test_config_file = NamedTempFile::new().unwrap();
|
||||
|
||||
let server =
|
||||
match self
|
||||
.server_runner
|
||||
.run(command, &self.server_vcpus, &mut server_config_file)
|
||||
{
|
||||
Ok(handle) => ChildWrapper(handle),
|
||||
Err(err) => return Err(RunResults::new(self).set_error(err.into(), "run server")),
|
||||
};
|
||||
|
||||
::std::thread::sleep(Duration::from_secs(1));
|
||||
|
||||
let mut load_tester = match self.load_test_runner.run(
|
||||
command,
|
||||
&self.load_test_vcpus,
|
||||
&mut load_test_config_file,
|
||||
) {
|
||||
Ok(handle) => ChildWrapper(handle),
|
||||
Err(err) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error(err.into(), "run load test")
|
||||
.set_server(server))
|
||||
}
|
||||
};
|
||||
|
||||
::std::thread::sleep(Duration::from_secs(59));
|
||||
|
||||
let cpu_stats_res = Command::new("ps")
|
||||
.arg("-p")
|
||||
.arg(server.0.id().to_string())
|
||||
.arg("-o")
|
||||
.arg("%cpu,rss")
|
||||
.arg("--noheader")
|
||||
.output();
|
||||
|
||||
let server_process_stats = match cpu_stats_res {
|
||||
Ok(output) if output.status.success() => {
|
||||
ProcessStats::from_str(&String::from_utf8_lossy(&output.stdout)).unwrap()
|
||||
}
|
||||
Ok(_) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error_context("run ps")
|
||||
.set_server(server)
|
||||
.set_load_test(load_tester));
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error(err.into(), "run ps")
|
||||
.set_server(server)
|
||||
.set_load_test(load_tester));
|
||||
}
|
||||
};
|
||||
|
||||
::std::thread::sleep(Duration::from_secs(5));
|
||||
|
||||
let load_test_data = match load_tester.0.try_wait() {
|
||||
Ok(Some(status)) if status.success() => read_child_outputs(load_tester),
|
||||
Ok(Some(_)) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error_context("wait for load tester")
|
||||
.set_server(server)
|
||||
.set_load_test(load_tester))
|
||||
}
|
||||
Ok(None) => {
|
||||
if let Err(err) = load_tester.0.kill() {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error(err.into(), "kill load tester")
|
||||
.set_server(server)
|
||||
.set_load_test(load_tester));
|
||||
}
|
||||
|
||||
::std::thread::sleep(Duration::from_secs(1));
|
||||
|
||||
match load_tester.0.try_wait() {
|
||||
Ok(_) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error_context("load tester didn't finish in time")
|
||||
.set_load_test(load_tester))
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error(err.into(), "wait for load tester after kill")
|
||||
.set_server(server));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(RunResults::new(self)
|
||||
.set_error(err.into(), "wait for load tester")
|
||||
.set_server(server)
|
||||
.set_load_test(load_tester))
|
||||
}
|
||||
};
|
||||
|
||||
let mut results = RunResults::new(self);
|
||||
|
||||
results.server_process_stats = Some(server_process_stats);
|
||||
results.load_test_stdout = load_test_data.0;
|
||||
results.load_test_stderr = load_test_data.1;
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RunResults<C> {
|
||||
pub run_config: RunConfig<C>,
|
||||
pub server_process_stats: Option<ProcessStats>,
|
||||
pub server_stdout: Option<String>,
|
||||
pub server_stderr: Option<String>,
|
||||
pub load_test_stdout: Option<String>,
|
||||
pub load_test_stderr: Option<String>,
|
||||
pub error: Option<anyhow::Error>,
|
||||
pub error_context: Option<String>,
|
||||
}
|
||||
|
||||
impl<C> RunResults<C> {
|
||||
pub fn avg_responses(&self) -> Option<String> {
|
||||
static RE: Lazy<Regex> =
|
||||
Lazy::new(|| Regex::new(r"Average responses per second: ([0-9]+\.?[0-9]+)").unwrap());
|
||||
|
||||
self.load_test_stdout.as_ref().and_then(|stdout| {
|
||||
RE.captures_iter(&stdout).next().map(|c| {
|
||||
let (_, [avg_responses]) = c.extract();
|
||||
|
||||
avg_responses.to_string()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn new(run_config: RunConfig<C>) -> Self {
|
||||
Self {
|
||||
run_config,
|
||||
server_process_stats: Default::default(),
|
||||
server_stdout: Default::default(),
|
||||
server_stderr: Default::default(),
|
||||
load_test_stdout: Default::default(),
|
||||
load_test_stderr: Default::default(),
|
||||
error: Default::default(),
|
||||
error_context: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_server(mut self, server: ChildWrapper) -> Self {
|
||||
let (stdout, stderr) = read_child_outputs(server);
|
||||
|
||||
self.server_stdout = stdout;
|
||||
self.server_stderr = stderr;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn set_load_test(mut self, load_test: ChildWrapper) -> Self {
|
||||
let (stdout, stderr) = read_child_outputs(load_test);
|
||||
|
||||
self.load_test_stdout = stdout;
|
||||
self.load_test_stderr = stderr;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn set_error(mut self, error: anyhow::Error, context: &str) -> Self {
|
||||
self.error = Some(error);
|
||||
self.error_context = Some(context.to_string());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn set_error_context(mut self, context: &str) -> Self {
|
||||
self.error_context = Some(context.to_string());
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct ProcessStats {
|
||||
pub avg_cpu_utilization: f32,
|
||||
pub peak_rss_kb: f32,
|
||||
}
|
||||
|
||||
impl FromStr for ProcessStats {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut parts = s.trim().split_whitespace();
|
||||
|
||||
Ok(Self {
|
||||
avg_cpu_utilization: parts.next().ok_or(())?.parse().map_err(|_| ())?,
|
||||
peak_rss_kb: parts.next().ok_or(())?.parse().map_err(|_| ())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ChildWrapper(Child);
|
||||
|
||||
impl Drop for ChildWrapper {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.kill();
|
||||
|
||||
::std::thread::sleep(Duration::from_secs(1));
|
||||
|
||||
let _ = self.0.try_wait();
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<Child> for ChildWrapper {
|
||||
fn as_mut(&mut self) -> &mut Child {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn read_child_outputs(mut child: ChildWrapper) -> (Option<String>, Option<String>) {
|
||||
let stdout = child.0.stdout.take().map(|stdout| {
|
||||
let mut buf = String::new();
|
||||
|
||||
let mut reader = NonBlockingReader::from_fd(stdout).unwrap();
|
||||
|
||||
reader.read_available_to_string(&mut buf).unwrap();
|
||||
|
||||
buf
|
||||
});
|
||||
let stderr = child.0.stderr.take().map(|stderr| {
|
||||
let mut buf = String::new();
|
||||
|
||||
let mut reader = NonBlockingReader::from_fd(stderr).unwrap();
|
||||
|
||||
reader.read_available_to_string(&mut buf).unwrap();
|
||||
|
||||
buf
|
||||
});
|
||||
|
||||
(stdout, stderr)
|
||||
}
|
||||
291
crates/load_tester/src/set.rs
Normal file
291
crates/load_tester/src/set.rs
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
use std::rc::Rc;
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
common::{CpuDirection, CpuMode, TaskSetCpuList},
|
||||
run::{ProcessRunner, ProcessStats, RunConfig},
|
||||
};
|
||||
|
||||
pub trait Server: ::std::fmt::Debug + Copy + Clone + ::std::hash::Hash {
|
||||
fn name(&self) -> String;
|
||||
}
|
||||
|
||||
pub struct SetConfig<C, I> {
|
||||
pub implementations: IndexMap<I, Vec<Rc<dyn ProcessRunner<Command = C>>>>,
|
||||
pub load_test_runs: Vec<(usize, TaskSetCpuList)>,
|
||||
}
|
||||
|
||||
pub fn run_sets<C, F, I>(
|
||||
command: &C,
|
||||
cpu_mode: CpuMode,
|
||||
set_configs: IndexMap<usize, SetConfig<C, I>>,
|
||||
load_test_gen: F,
|
||||
) where
|
||||
C: ::std::fmt::Debug,
|
||||
I: Server,
|
||||
F: Fn(usize) -> Box<dyn ProcessRunner<Command = C>>,
|
||||
{
|
||||
println!("# Load test report");
|
||||
|
||||
let results = set_configs
|
||||
.into_iter()
|
||||
.map(|(server_core_count, set_config)| {
|
||||
let server_vcpus =
|
||||
TaskSetCpuList::new(cpu_mode, CpuDirection::Asc, server_core_count).unwrap();
|
||||
|
||||
println!(
|
||||
"## Tracker cores: {} (cpus: {})",
|
||||
server_core_count,
|
||||
server_vcpus.as_cpu_list()
|
||||
);
|
||||
|
||||
let server_results = set_config
|
||||
.implementations
|
||||
.into_iter()
|
||||
.map(|(implementation, server_runs)| {
|
||||
let server_run_results = server_runs
|
||||
.iter()
|
||||
.map(|server_run| {
|
||||
let load_test_run_results = set_config
|
||||
.load_test_runs
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(|(workers, load_test_vcpus)| {
|
||||
LoadTestRunResults::produce(
|
||||
command,
|
||||
&load_test_gen,
|
||||
implementation,
|
||||
&server_run,
|
||||
server_vcpus.clone(),
|
||||
workers,
|
||||
load_test_vcpus,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
ServerConfigurationResults {
|
||||
config_keys: server_run.keys(),
|
||||
load_tests: load_test_run_results,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
ImplementationResults {
|
||||
name: implementation.name(),
|
||||
configurations: server_run_results,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
ServerCoreCountResults {
|
||||
core_count: server_core_count,
|
||||
implementations: server_results,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
html_summary(&results);
|
||||
}
|
||||
|
||||
pub struct ServerCoreCountResults {
|
||||
core_count: usize,
|
||||
implementations: Vec<ImplementationResults>,
|
||||
}
|
||||
|
||||
pub struct ImplementationResults {
|
||||
name: String,
|
||||
configurations: Vec<ServerConfigurationResults>,
|
||||
}
|
||||
|
||||
impl ImplementationResults {
|
||||
fn best_result(&self) -> Option<LoadTestRunResultsSuccess> {
|
||||
self.configurations
|
||||
.iter()
|
||||
.filter_map(|c| c.best_result())
|
||||
.reduce(|acc, r| {
|
||||
if r.average_responses > acc.average_responses {
|
||||
r
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ServerConfigurationResults {
|
||||
config_keys: IndexMap<String, String>,
|
||||
load_tests: Vec<LoadTestRunResults>,
|
||||
// best_index: Option<usize>,
|
||||
}
|
||||
|
||||
impl ServerConfigurationResults {
|
||||
fn best_result(&self) -> Option<LoadTestRunResultsSuccess> {
|
||||
self.load_tests
|
||||
.iter()
|
||||
.filter_map(|r| match r {
|
||||
LoadTestRunResults::Success(r) => Some(r.clone()),
|
||||
LoadTestRunResults::Failure(_) => None,
|
||||
})
|
||||
.reduce(|acc, r| {
|
||||
if r.average_responses > acc.average_responses {
|
||||
r
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LoadTestRunResults {
|
||||
Success(LoadTestRunResultsSuccess),
|
||||
Failure(LoadTestRunResultsFailure),
|
||||
}
|
||||
|
||||
impl LoadTestRunResults {
|
||||
pub fn produce<C, F, I>(
|
||||
command: &C,
|
||||
load_test_gen: &F,
|
||||
implementation: I,
|
||||
server_process: &Rc<dyn ProcessRunner<Command = C>>,
|
||||
server_vcpus: TaskSetCpuList,
|
||||
workers: usize,
|
||||
load_test_vcpus: TaskSetCpuList,
|
||||
) -> Self
|
||||
where
|
||||
C: ::std::fmt::Debug,
|
||||
I: Server,
|
||||
F: Fn(usize) -> Box<dyn ProcessRunner<Command = C>>,
|
||||
{
|
||||
println!(
|
||||
"### {} run ({}) (load test workers: {}, cpus: {})",
|
||||
implementation.name(),
|
||||
server_process.info(),
|
||||
workers,
|
||||
load_test_vcpus.as_cpu_list()
|
||||
);
|
||||
|
||||
let load_test_runner = load_test_gen(workers);
|
||||
let load_test_keys = load_test_runner.keys();
|
||||
|
||||
let run_config = RunConfig {
|
||||
server_runner: server_process.clone(),
|
||||
server_vcpus: server_vcpus.clone(),
|
||||
load_test_runner,
|
||||
load_test_vcpus,
|
||||
};
|
||||
|
||||
match run_config.run(command) {
|
||||
Ok(results) => {
|
||||
let avg_responses = results.avg_responses().unwrap().parse::<f32>().unwrap();
|
||||
let server_process_stats = results.server_process_stats.unwrap();
|
||||
|
||||
println!("- Average responses per second: {}", avg_responses);
|
||||
println!(
|
||||
"- Average server CPU utilization: {}%",
|
||||
server_process_stats.avg_cpu_utilization,
|
||||
);
|
||||
println!("- Peak server RSS: {} kB", server_process_stats.peak_rss_kb);
|
||||
|
||||
LoadTestRunResults::Success(LoadTestRunResultsSuccess {
|
||||
config_keys: load_test_keys,
|
||||
average_responses: avg_responses,
|
||||
server_process_stats,
|
||||
})
|
||||
}
|
||||
Err(results) => {
|
||||
println!("\nRun failed:\n{:?}\n", results);
|
||||
|
||||
LoadTestRunResults::Failure(LoadTestRunResultsFailure {
|
||||
config_keys: load_test_keys,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LoadTestRunResultsSuccess {
|
||||
config_keys: IndexMap<String, String>,
|
||||
average_responses: f32,
|
||||
server_process_stats: ProcessStats,
|
||||
}
|
||||
|
||||
pub struct LoadTestRunResultsFailure {
|
||||
config_keys: IndexMap<String, String>,
|
||||
}
|
||||
|
||||
pub fn html_summary(results: &[ServerCoreCountResults]) {
|
||||
let mut all_implementation_names = IndexSet::new();
|
||||
|
||||
for core_count_results in results {
|
||||
all_implementation_names.extend(
|
||||
core_count_results
|
||||
.implementations
|
||||
.iter()
|
||||
.map(|r| r.name.clone()),
|
||||
);
|
||||
}
|
||||
|
||||
let mut data_rows = Vec::new();
|
||||
|
||||
for core_count_results in results {
|
||||
let best_results = core_count_results
|
||||
.implementations
|
||||
.iter()
|
||||
.map(|implementation| (implementation.name.clone(), implementation.best_result()))
|
||||
.collect::<IndexMap<_, _>>();
|
||||
|
||||
let best_results_for_all_implementations = all_implementation_names
|
||||
.iter()
|
||||
.map(|name| {
|
||||
best_results
|
||||
.get(name)
|
||||
.and_then(|r| r.as_ref().map(|r| r.average_responses))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let data_row = format!(
|
||||
"
|
||||
<tr>
|
||||
<th>{}</th>
|
||||
{}
|
||||
</tr>
|
||||
",
|
||||
core_count_results.core_count,
|
||||
best_results_for_all_implementations
|
||||
.into_iter()
|
||||
.map(|result| format!(
|
||||
"<td>{}</td>",
|
||||
result
|
||||
.map(|r| r.to_string())
|
||||
.unwrap_or_else(|| "-".to_string())
|
||||
))
|
||||
.join("\n"),
|
||||
);
|
||||
|
||||
data_rows.push(data_row);
|
||||
}
|
||||
|
||||
println!(
|
||||
"
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>CPU cores</th>
|
||||
{}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{}
|
||||
</tbody>
|
||||
</table>
|
||||
",
|
||||
all_implementation_names
|
||||
.iter()
|
||||
.map(|name| format!("<th>{name}</th>"))
|
||||
.join("\n"),
|
||||
data_rows.join("\n")
|
||||
)
|
||||
}
|
||||
|
|
@ -2,13 +2,13 @@ use std::{net::SocketAddr, path::PathBuf};
|
|||
|
||||
use aquatic_common::{access_list::AccessListConfig, privileges::PrivilegeConfig};
|
||||
use cfg_if::cfg_if;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use aquatic_common::cli::LogLevel;
|
||||
use aquatic_toml_config::TomlConfig;
|
||||
|
||||
/// aquatic_udp configuration
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
/// Number of socket worker. One per physical core is recommended.
|
||||
|
|
@ -78,7 +78,7 @@ impl aquatic_common::cli::Config for Config {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct NetworkConfig {
|
||||
/// Bind to this address
|
||||
|
|
@ -138,7 +138,7 @@ impl Default for NetworkConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct ProtocolConfig {
|
||||
/// Maximum number of torrents to allow in scrape request
|
||||
|
|
@ -159,7 +159,7 @@ impl Default for ProtocolConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct StatisticsConfig {
|
||||
/// Collect and print/write statistics this often (seconds)
|
||||
|
|
@ -231,7 +231,7 @@ impl Default for StatisticsConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct CleaningConfig {
|
||||
/// Clean torrents this often (seconds)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,9 @@ rust-version.workspace = true
|
|||
[features]
|
||||
cpu-pinning = ["aquatic_common/hwloc"]
|
||||
|
||||
[lib]
|
||||
name = "aquatic_udp_load_test"
|
||||
|
||||
[[bin]]
|
||||
name = "aquatic_udp_load_test"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::net::SocketAddr;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use aquatic_common::cli::LogLevel;
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
|
|
@ -8,7 +8,7 @@ use aquatic_common::cpu_pinning::desc::CpuPinningConfigDesc;
|
|||
use aquatic_toml_config::TomlConfig;
|
||||
|
||||
/// aquatic_udp_load_test configuration
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
/// Server address
|
||||
|
|
@ -42,7 +42,7 @@ impl Default for Config {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct NetworkConfig {
|
||||
/// True means bind to one localhost IP per socket.
|
||||
|
|
@ -56,8 +56,6 @@ pub struct NetworkConfig {
|
|||
pub first_port: u16,
|
||||
/// Socket worker poll timeout in microseconds
|
||||
pub poll_timeout: u64,
|
||||
/// Socket worker polling event number
|
||||
pub poll_event_capacity: usize,
|
||||
/// Size of socket recv buffer. Use 0 for OS default.
|
||||
///
|
||||
/// This setting can have a big impact on dropped packages. It might
|
||||
|
|
@ -79,13 +77,12 @@ impl Default for NetworkConfig {
|
|||
multiple_client_ipv4s: true,
|
||||
first_port: 45_000,
|
||||
poll_timeout: 276,
|
||||
poll_event_capacity: 2_877,
|
||||
recv_buffer: 6_000_000,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct RequestConfig {
|
||||
/// Number of torrents to simulate
|
||||
|
|
|
|||
191
crates/udp_load_test/src/lib.rs
Normal file
191
crates/udp_load_test/src/lib.rs
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
use std::thread::{self, Builder};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
use aquatic_common::cpu_pinning::{pin_current_if_configured_to, WorkerIndex};
|
||||
use rand_distr::Gamma;
|
||||
|
||||
pub mod common;
|
||||
pub mod config;
|
||||
pub mod utils;
|
||||
pub mod worker;
|
||||
|
||||
use common::*;
|
||||
use config::Config;
|
||||
use utils::*;
|
||||
use worker::*;
|
||||
|
||||
impl aquatic_common::cli::Config for Config {
|
||||
fn get_log_level(&self) -> Option<aquatic_common::cli::LogLevel> {
|
||||
Some(self.log_level)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(config: Config) -> ::anyhow::Result<()> {
|
||||
if config.requests.weight_announce
|
||||
+ config.requests.weight_connect
|
||||
+ config.requests.weight_scrape
|
||||
== 0
|
||||
{
|
||||
panic!("Error: at least one weight must be larger than zero.");
|
||||
}
|
||||
|
||||
println!("Starting client with config: {:#?}", config);
|
||||
|
||||
let mut info_hashes = Vec::with_capacity(config.requests.number_of_torrents);
|
||||
|
||||
for _ in 0..config.requests.number_of_torrents {
|
||||
info_hashes.push(generate_info_hash());
|
||||
}
|
||||
|
||||
let state = LoadTestState {
|
||||
info_hashes: Arc::new(info_hashes),
|
||||
statistics: Arc::new(Statistics::default()),
|
||||
};
|
||||
|
||||
let gamma = Gamma::new(
|
||||
config.requests.torrent_gamma_shape,
|
||||
config.requests.torrent_gamma_scale,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Start workers
|
||||
|
||||
for i in 0..config.workers {
|
||||
let port = config.network.first_port + (i as u16);
|
||||
|
||||
let ip = if config.server_address.is_ipv6() {
|
||||
Ipv6Addr::LOCALHOST.into()
|
||||
} else {
|
||||
if config.network.multiple_client_ipv4s {
|
||||
Ipv4Addr::new(127, 0, 0, 1 + i).into()
|
||||
} else {
|
||||
Ipv4Addr::LOCALHOST.into()
|
||||
}
|
||||
};
|
||||
|
||||
let addr = SocketAddr::new(ip, port);
|
||||
let config = config.clone();
|
||||
let state = state.clone();
|
||||
|
||||
Builder::new().name("load-test".into()).spawn(move || {
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
pin_current_if_configured_to(
|
||||
&config.cpu_pinning,
|
||||
config.workers as usize,
|
||||
0,
|
||||
WorkerIndex::SocketWorker(i as usize),
|
||||
);
|
||||
|
||||
run_worker_thread(state, gamma, &config, addr)
|
||||
})?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
pin_current_if_configured_to(
|
||||
&config.cpu_pinning,
|
||||
config.workers as usize,
|
||||
0,
|
||||
WorkerIndex::Util,
|
||||
);
|
||||
|
||||
monitor_statistics(state, &config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn monitor_statistics(state: LoadTestState, config: &Config) {
|
||||
let mut report_avg_connect: Vec<f64> = Vec::new();
|
||||
let mut report_avg_announce: Vec<f64> = Vec::new();
|
||||
let mut report_avg_scrape: Vec<f64> = Vec::new();
|
||||
let mut report_avg_error: Vec<f64> = Vec::new();
|
||||
|
||||
let interval = 5;
|
||||
|
||||
let start_time = Instant::now();
|
||||
let duration = Duration::from_secs(config.duration as u64);
|
||||
|
||||
let mut last = start_time;
|
||||
|
||||
let time_elapsed = loop {
|
||||
thread::sleep(Duration::from_secs(interval));
|
||||
|
||||
let requests = fetch_and_reset(&state.statistics.requests);
|
||||
let response_peers = fetch_and_reset(&state.statistics.response_peers);
|
||||
let responses_connect = fetch_and_reset(&state.statistics.responses_connect);
|
||||
let responses_announce = fetch_and_reset(&state.statistics.responses_announce);
|
||||
let responses_scrape = fetch_and_reset(&state.statistics.responses_scrape);
|
||||
let responses_error = fetch_and_reset(&state.statistics.responses_error);
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
let elapsed = (now - last).as_secs_f64();
|
||||
|
||||
last = now;
|
||||
|
||||
let peers_per_announce_response = response_peers / responses_announce;
|
||||
|
||||
let avg_requests = requests / elapsed;
|
||||
let avg_responses_connect = responses_connect / elapsed;
|
||||
let avg_responses_announce = responses_announce / elapsed;
|
||||
let avg_responses_scrape = responses_scrape / elapsed;
|
||||
let avg_responses_error = responses_error / elapsed;
|
||||
|
||||
let avg_responses = avg_responses_connect
|
||||
+ avg_responses_announce
|
||||
+ avg_responses_scrape
|
||||
+ avg_responses_error;
|
||||
|
||||
report_avg_connect.push(avg_responses_connect);
|
||||
report_avg_announce.push(avg_responses_announce);
|
||||
report_avg_scrape.push(avg_responses_scrape);
|
||||
report_avg_error.push(avg_responses_error);
|
||||
|
||||
println!();
|
||||
println!("Requests out: {:.2}/second", avg_requests);
|
||||
println!("Responses in: {:.2}/second", avg_responses);
|
||||
println!(" - Connect responses: {:.2}", avg_responses_connect);
|
||||
println!(" - Announce responses: {:.2}", avg_responses_announce);
|
||||
println!(" - Scrape responses: {:.2}", avg_responses_scrape);
|
||||
println!(" - Error responses: {:.2}", avg_responses_error);
|
||||
println!(
|
||||
"Peers per announce response: {:.2}",
|
||||
peers_per_announce_response
|
||||
);
|
||||
|
||||
let time_elapsed = start_time.elapsed();
|
||||
|
||||
if config.duration != 0 && time_elapsed >= duration {
|
||||
break time_elapsed;
|
||||
}
|
||||
};
|
||||
|
||||
let len = report_avg_connect.len() as f64;
|
||||
|
||||
let avg_connect: f64 = report_avg_connect.into_iter().sum::<f64>() / len;
|
||||
let avg_announce: f64 = report_avg_announce.into_iter().sum::<f64>() / len;
|
||||
let avg_scrape: f64 = report_avg_scrape.into_iter().sum::<f64>() / len;
|
||||
let avg_error: f64 = report_avg_error.into_iter().sum::<f64>() / len;
|
||||
|
||||
let avg_total = avg_connect + avg_announce + avg_scrape + avg_error;
|
||||
|
||||
println!();
|
||||
println!("# aquatic load test report");
|
||||
println!();
|
||||
println!("Test ran for {} seconds", time_elapsed.as_secs());
|
||||
println!("Average responses per second: {:.2}", avg_total);
|
||||
println!(" - Connect responses: {:.2}", avg_connect);
|
||||
println!(" - Announce responses: {:.2}", avg_announce);
|
||||
println!(" - Scrape responses: {:.2}", avg_scrape);
|
||||
println!(" - Error responses: {:.2}", avg_error);
|
||||
println!();
|
||||
println!("Config: {:#?}", config);
|
||||
println!();
|
||||
}
|
||||
|
||||
fn fetch_and_reset(atomic_usize: &AtomicUsize) -> f64 {
|
||||
atomic_usize.fetch_and(0, Ordering::Relaxed) as f64
|
||||
}
|
||||
|
|
@ -1,22 +1,4 @@
|
|||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
use std::thread::{self, Builder};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
use aquatic_common::cpu_pinning::{pin_current_if_configured_to, WorkerIndex};
|
||||
use rand_distr::Gamma;
|
||||
|
||||
mod common;
|
||||
mod config;
|
||||
mod utils;
|
||||
mod worker;
|
||||
|
||||
use common::*;
|
||||
use config::Config;
|
||||
use utils::*;
|
||||
use worker::*;
|
||||
use aquatic_udp_load_test::config::Config;
|
||||
|
||||
#[global_allocator]
|
||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
|
@ -25,179 +7,7 @@ pub fn main() {
|
|||
aquatic_common::cli::run_app_with_cli_and_config::<Config>(
|
||||
"aquatic_udp_load_test: BitTorrent load tester",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
run,
|
||||
aquatic_udp_load_test::run,
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
impl aquatic_common::cli::Config for Config {
|
||||
fn get_log_level(&self) -> Option<aquatic_common::cli::LogLevel> {
|
||||
Some(self.log_level)
|
||||
}
|
||||
}
|
||||
|
||||
fn run(config: Config) -> ::anyhow::Result<()> {
|
||||
if config.requests.weight_announce
|
||||
+ config.requests.weight_connect
|
||||
+ config.requests.weight_scrape
|
||||
== 0
|
||||
{
|
||||
panic!("Error: at least one weight must be larger than zero.");
|
||||
}
|
||||
|
||||
println!("Starting client with config: {:#?}", config);
|
||||
|
||||
let mut info_hashes = Vec::with_capacity(config.requests.number_of_torrents);
|
||||
|
||||
for _ in 0..config.requests.number_of_torrents {
|
||||
info_hashes.push(generate_info_hash());
|
||||
}
|
||||
|
||||
let state = LoadTestState {
|
||||
info_hashes: Arc::new(info_hashes),
|
||||
statistics: Arc::new(Statistics::default()),
|
||||
};
|
||||
|
||||
let gamma = Gamma::new(
|
||||
config.requests.torrent_gamma_shape,
|
||||
config.requests.torrent_gamma_scale,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Start workers
|
||||
|
||||
for i in 0..config.workers {
|
||||
let port = config.network.first_port + (i as u16);
|
||||
|
||||
let ip = if config.server_address.is_ipv6() {
|
||||
Ipv6Addr::LOCALHOST.into()
|
||||
} else {
|
||||
if config.network.multiple_client_ipv4s {
|
||||
Ipv4Addr::new(127, 0, 0, 1 + i).into()
|
||||
} else {
|
||||
Ipv4Addr::LOCALHOST.into()
|
||||
}
|
||||
};
|
||||
|
||||
let addr = SocketAddr::new(ip, port);
|
||||
let config = config.clone();
|
||||
let state = state.clone();
|
||||
|
||||
Builder::new().name("load-test".into()).spawn(move || {
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
pin_current_if_configured_to(
|
||||
&config.cpu_pinning,
|
||||
config.workers as usize,
|
||||
0,
|
||||
WorkerIndex::SocketWorker(i as usize),
|
||||
);
|
||||
|
||||
run_worker_thread(state, gamma, &config, addr)
|
||||
})?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "cpu-pinning")]
|
||||
pin_current_if_configured_to(
|
||||
&config.cpu_pinning,
|
||||
config.workers as usize,
|
||||
0,
|
||||
WorkerIndex::Util,
|
||||
);
|
||||
|
||||
monitor_statistics(state, &config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn monitor_statistics(state: LoadTestState, config: &Config) {
|
||||
let mut report_avg_connect: Vec<f64> = Vec::new();
|
||||
let mut report_avg_announce: Vec<f64> = Vec::new();
|
||||
let mut report_avg_scrape: Vec<f64> = Vec::new();
|
||||
let mut report_avg_error: Vec<f64> = Vec::new();
|
||||
|
||||
let interval = 5;
|
||||
|
||||
let start_time = Instant::now();
|
||||
let duration = Duration::from_secs(config.duration as u64);
|
||||
|
||||
let mut last = start_time;
|
||||
|
||||
let time_elapsed = loop {
|
||||
thread::sleep(Duration::from_secs(interval));
|
||||
|
||||
let requests = fetch_and_reset(&state.statistics.requests);
|
||||
let response_peers = fetch_and_reset(&state.statistics.response_peers);
|
||||
let responses_connect = fetch_and_reset(&state.statistics.responses_connect);
|
||||
let responses_announce = fetch_and_reset(&state.statistics.responses_announce);
|
||||
let responses_scrape = fetch_and_reset(&state.statistics.responses_scrape);
|
||||
let responses_error = fetch_and_reset(&state.statistics.responses_error);
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
let elapsed = (now - last).as_secs_f64();
|
||||
|
||||
last = now;
|
||||
|
||||
let peers_per_announce_response = response_peers / responses_announce;
|
||||
|
||||
let avg_requests = requests / elapsed;
|
||||
let avg_responses_connect = responses_connect / elapsed;
|
||||
let avg_responses_announce = responses_announce / elapsed;
|
||||
let avg_responses_scrape = responses_scrape / elapsed;
|
||||
let avg_responses_error = responses_error / elapsed;
|
||||
|
||||
let avg_responses = avg_responses_connect
|
||||
+ avg_responses_announce
|
||||
+ avg_responses_scrape
|
||||
+ avg_responses_error;
|
||||
|
||||
report_avg_connect.push(avg_responses_connect);
|
||||
report_avg_announce.push(avg_responses_announce);
|
||||
report_avg_scrape.push(avg_responses_scrape);
|
||||
report_avg_error.push(avg_responses_error);
|
||||
|
||||
println!();
|
||||
println!("Requests out: {:.2}/second", avg_requests);
|
||||
println!("Responses in: {:.2}/second", avg_responses);
|
||||
println!(" - Connect responses: {:.2}", avg_responses_connect);
|
||||
println!(" - Announce responses: {:.2}", avg_responses_announce);
|
||||
println!(" - Scrape responses: {:.2}", avg_responses_scrape);
|
||||
println!(" - Error responses: {:.2}", avg_responses_error);
|
||||
println!(
|
||||
"Peers per announce response: {:.2}",
|
||||
peers_per_announce_response
|
||||
);
|
||||
|
||||
let time_elapsed = start_time.elapsed();
|
||||
|
||||
if config.duration != 0 && time_elapsed >= duration {
|
||||
break time_elapsed;
|
||||
}
|
||||
};
|
||||
|
||||
let len = report_avg_connect.len() as f64;
|
||||
|
||||
let avg_connect: f64 = report_avg_connect.into_iter().sum::<f64>() / len;
|
||||
let avg_announce: f64 = report_avg_announce.into_iter().sum::<f64>() / len;
|
||||
let avg_scrape: f64 = report_avg_scrape.into_iter().sum::<f64>() / len;
|
||||
let avg_error: f64 = report_avg_error.into_iter().sum::<f64>() / len;
|
||||
|
||||
let avg_total = avg_connect + avg_announce + avg_scrape + avg_error;
|
||||
|
||||
println!();
|
||||
println!("# aquatic load test report");
|
||||
println!();
|
||||
println!("Test ran for {} seconds", time_elapsed.as_secs());
|
||||
println!("Average responses per second: {:.2}", avg_total);
|
||||
println!(" - Connect responses: {:.2}", avg_connect);
|
||||
println!(" - Announce responses: {:.2}", avg_announce);
|
||||
println!(" - Scrape responses: {:.2}", avg_scrape);
|
||||
println!(" - Error responses: {:.2}", avg_error);
|
||||
println!();
|
||||
println!("Config: {:#?}", config);
|
||||
println!();
|
||||
}
|
||||
|
||||
fn fetch_and_reset(atomic_usize: &AtomicUsize) -> f64 {
|
||||
atomic_usize.fetch_and(0, Ordering::Relaxed) as f64
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ pub fn run_worker_thread(
|
|||
.register(&mut socket, token, interests)
|
||||
.unwrap();
|
||||
|
||||
let mut events = Events::with_capacity(config.network.poll_event_capacity);
|
||||
let mut events = Events::with_capacity(1);
|
||||
|
||||
let mut statistics = SocketWorkerLocalStatistics::default();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue