From d6b8fb67676a38c0ca633a1bcb7d1b59fb7ba472 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 29 May 2024 18:39:51 -0400 Subject: [PATCH 01/41] early benchmarks for adding members to empty group --- Cargo.lock | 132 ++++++++++++++++++++++++++++++++ xmtp_mls/Cargo.toml | 8 ++ xmtp_mls/benches/group_limit.rs | 71 +++++++++++++++++ xmtp_mls/src/builder.rs | 62 +-------------- xmtp_mls/src/utils/mod.rs | 2 +- xmtp_mls/src/utils/test.rs | 63 ++++++++++++++- 6 files changed, 278 insertions(+), 60 deletions(-) create mode 100644 xmtp_mls/benches/group_limit.rs diff --git a/Cargo.lock b/Cargo.lock index 2d3acb7ad..257e1e27c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "ansi_term" version = "0.12.1" @@ -516,6 +522,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.98" @@ -571,6 +583,33 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.4.4" @@ -759,6 +798,44 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "futures", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -1949,6 +2026,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -3014,6 +3101,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -3550,6 +3643,34 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -5033,6 +5154,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -6245,6 +6376,7 @@ dependencies = [ "async-barrier", "async-trait", "chrono", + "criterion", "ctor", "diesel", "diesel_migrations", diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index bbd2e5959..358467329 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -13,6 +13,7 @@ default = ["types", "native"] grpc = ["xmtp_proto/grpc"] native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] types = [] +test-utils = ["xmtp_api_grpc"] [dependencies] aes = "0.8.4" @@ -52,6 +53,7 @@ xmtp_cryptography = { workspace = true } xmtp_id = { path = "../xmtp_id" } xmtp_proto = { workspace = true, features = ["proto_full", "convert"] } xmtp_v2 = { path = "../xmtp_v2" } +xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } [dev-dependencies] ctor.workspace = true @@ -66,3 +68,9 @@ xmtp_api_grpc = { path = "../xmtp_api_grpc" } xmtp_id = { path = "../xmtp_id", features = ["test-utils"] } async-barrier = "1.1" anyhow.workspace = true +criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } + +[[bench]] +name = "group_limit" +harness = false +required_features = ["test-utils"] diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs new file mode 100644 index 000000000..e9cf1a345 --- /dev/null +++ b/xmtp_mls/benches/group_limit.rs @@ -0,0 +1,71 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use ethers::{ + signers::{LocalWallet, Signer}, + types::Address, +}; +use tokio::runtime::{Builder, Runtime}; +use xmtp_cryptography::utils::rng; +use xmtp_mls::{builder::ClientBuilder, groups::MlsGroup, utils::test::TestClient}; + +async fn create_group() -> Address { + let wallet = LocalWallet::new(&mut rng()); + let _ = ClientBuilder::new_test_client(&wallet).await; + wallet.address() +} + +async fn create_groups(n: usize) -> Vec
{ + let mut addresses = Vec::with_capacity(n); + for _ in 0..n { + addresses.push(create_group().await); + } + addresses +} + +fn with_groups(num_groups: usize, fun: F) -> T +where + F: FnOnce(&TestClient, &MlsGroup, Vec, Runtime) -> T, +{ + let runtime = Builder::new_multi_thread() + .worker_threads(4) + .enable_time() + .enable_io() + .thread_name("xmtp-bencher") + .build() + .unwrap(); + + let (client, group, addresses) = runtime.block_on(async { + let addresses: Vec = create_groups(num_groups) + .await + .into_iter() + .map(hex::encode) + .collect(); + let wallet = LocalWallet::new(&mut rng()); + let client = ClientBuilder::new_test_client(&wallet).await; + + let group = client.create_group(None).unwrap(); + (client, group, addresses) + }); + + fun(&client, &group, addresses, runtime) +} + +fn add_100_members(c: &mut Criterion) { + with_groups(100, |client, group, addresses, runtime| { + c.bench_function("add 100 members", |b| { + b.to_async(&runtime) + .iter(|| group.add_members(client, addresses.clone())) + }); + }) +} + +fn add_1000_members(c: &mut Criterion) { + with_groups(1000, |client, group, addresses, runtime| { + c.bench_function("add 1000 members", |b| { + b.to_async(&runtime) + .iter(|| group.add_members(client, addresses.clone())) + }); + }) +} + +criterion_group!(group_limit, add_100_members, add_1000_members); +criterion_main!(group_limit); diff --git a/xmtp_mls/src/builder.rs b/xmtp_mls/src/builder.rs index acf6c938a..bf49e0266 100644 --- a/xmtp_mls/src/builder.rs +++ b/xmtp_mls/src/builder.rs @@ -111,67 +111,16 @@ where #[cfg(test)] mod tests { - use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; use xmtp_cryptography::utils::generate_local_wallet; - use xmtp_id::associations::{generate_inbox_id, RecoverableEcdsaSignature}; + use xmtp_id::associations::generate_inbox_id; use super::{ClientBuilder, IdentityStrategy}; use crate::{ storage::{EncryptedMessageStore, StorageOption}, - utils::test::tmp_path, - Client, InboxOwner, + utils::test::{register_client, tmp_path}, + InboxOwner, }; - async fn get_local_grpc_client() -> GrpcClient { - GrpcClient::create("http://localhost:5556".to_string(), false) - .await - .unwrap() - } - - async fn register_client(client: &Client, owner: &impl InboxOwner) { - let mut signature_request = client.context.signature_request().unwrap(); - let signature_text = signature_request.signature_text(); - signature_request - .add_signature(Box::new(RecoverableEcdsaSignature::new( - signature_text.clone(), - owner.sign(&signature_text).unwrap().into(), - ))) - .await - .unwrap(); - - client.register_identity(signature_request).await.unwrap(); - } - - impl ClientBuilder { - pub async fn local_grpc(self) -> Self { - self.api_client(get_local_grpc_client().await) - } - - fn temp_store(self) -> Self { - let tmpdb = tmp_path(); - self.store( - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(), - ) - } - - pub async fn new_test_client(owner: &impl InboxOwner) -> Client { - let client = Self::new(IdentityStrategy::CreateIfNotFound( - owner.get_address(), - None, - )) - .temp_store() - .local_grpc() - .await - .build() - .await - .unwrap(); - - register_client(&client, owner).await; - - client - } - } - #[tokio::test] async fn builder_test() { let wallet = generate_local_wallet(); @@ -196,10 +145,7 @@ mod tests { .await .unwrap(); - assert_eq!( - client.inbox_id(), - generate_inbox_id(&account_address.to_string(), &0) - ); + assert_eq!(client.inbox_id(), generate_inbox_id(&account_address, &0)); } #[tokio::test] diff --git a/xmtp_mls/src/utils/mod.rs b/xmtp_mls/src/utils/mod.rs index adbc0312b..0f66974fc 100644 --- a/xmtp_mls/src/utils/mod.rs +++ b/xmtp_mls/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod hash; pub mod id; -#[cfg(test)] +#[cfg(any(test, feature = "test-utils"))] pub mod test; pub mod time; diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 8e220b26e..1871733be 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -4,8 +4,18 @@ use rand::{ distributions::{Alphanumeric, DistString}, Rng, }; +use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; +use xmtp_id::associations::RecoverableEcdsaSignature; -use crate::types::Address; +use crate::{ + builder::ClientBuilder, + identity::IdentityStrategy, + storage::{EncryptedMessageStore, StorageOption}, + types::Address, + Client, InboxOwner, +}; + +pub type TestClient = Client; pub fn rand_string() -> String { Alphanumeric.sample_string(&mut rand::thread_rng(), 24) @@ -28,3 +38,54 @@ pub fn rand_time() -> i64 { let mut rng = rand::thread_rng(); rng.gen_range(0..1_000_000_000) } + +/// Get a GRPC Client pointed at the local instance of `xmtp-node-go` +pub async fn get_local_grpc_client() -> GrpcClient { + GrpcClient::create("http://localhost:5556".to_string(), false) + .await + .unwrap() +} + +impl ClientBuilder { + pub async fn local_grpc(self) -> Self { + self.api_client(get_local_grpc_client().await) + } + + pub fn temp_store(self) -> Self { + let tmpdb = tmp_path(); + self.store( + EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(), + ) + } + + pub async fn new_test_client(owner: &impl InboxOwner) -> Client { + let client = Self::new(IdentityStrategy::CreateIfNotFound( + owner.get_address(), + None, + )) + .temp_store() + .local_grpc() + .await + .build() + .await + .unwrap(); + + register_client(&client, owner).await; + + client + } +} + +pub async fn register_client(client: &Client, owner: &impl InboxOwner) { + let mut signature_request = client.context.signature_request().unwrap(); + let signature_text = signature_request.signature_text(); + signature_request + .add_signature(Box::new(RecoverableEcdsaSignature::new( + signature_text.clone(), + owner.sign(&signature_text).unwrap().into(), + ))) + .await + .unwrap(); + + client.register_identity(signature_request).await.unwrap(); +} From 15c65cfbfff1290e2454fb751c279ca72d3df05a Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 29 May 2024 18:40:58 -0400 Subject: [PATCH 02/41] emphasize to empty/new group --- xmtp_mls/benches/{group_limit.rs => group_limit_empty.rs} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename xmtp_mls/benches/{group_limit.rs => group_limit_empty.rs} (94%) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit_empty.rs similarity index 94% rename from xmtp_mls/benches/group_limit.rs rename to xmtp_mls/benches/group_limit_empty.rs index e9cf1a345..7cc18b75a 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -67,5 +67,5 @@ fn add_1000_members(c: &mut Criterion) { }) } -criterion_group!(group_limit, add_100_members, add_1000_members); -criterion_main!(group_limit); +criterion_group!(group_limit_empty, add_100_members, add_1000_members); +criterion_main!(group_limit_empty); From 8c8bf48c2a9b1c9a1695629d0384b9feb92e4f91 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 30 May 2024 12:57:41 -0400 Subject: [PATCH 03/41] combine all same benchmarks into one fn --- Cargo.lock | 1 + xmtp_mls/Cargo.toml | 11 +++-- xmtp_mls/benches/group_limit_empty.rs | 69 +++++++++++++++++---------- xmtp_mls/src/utils/test.rs | 2 +- 4 files changed, 53 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 257e1e27c..5899ff46f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6409,6 +6409,7 @@ dependencies = [ "toml 0.8.13", "tracing", "tracing-log", + "tracing-subscriber", "tracing-test", "xmtp_api_grpc", "xmtp_cryptography", diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 358467329..438220fd2 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -9,11 +9,10 @@ name = "update-schema" path = "src/bin/update-schema.rs" [features] -default = ["types", "native"] +default = ["native"] grpc = ["xmtp_proto/grpc"] native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] -types = [] -test-utils = ["xmtp_api_grpc"] +test-utils = ["xmtp_api_grpc", "tracing-subscriber"] [dependencies] aes = "0.8.4" @@ -53,7 +52,11 @@ xmtp_cryptography = { workspace = true } xmtp_id = { path = "../xmtp_id" } xmtp_proto = { workspace = true, features = ["proto_full", "convert"] } xmtp_v2 = { path = "../xmtp_v2" } + +# Test Utils xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } +tracing-subscriber = { version = "0.3", optional = true } + [dev-dependencies] ctor.workspace = true @@ -71,6 +74,6 @@ anyhow.workspace = true criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } [[bench]] -name = "group_limit" +name = "group_limit_empty" harness = false required_features = ["test-utils"] diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index 7cc18b75a..ba6c90ac3 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -1,30 +1,28 @@ -use criterion::{criterion_group, criterion_main, Criterion}; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use ethers::{ signers::{LocalWallet, Signer}, types::Address, }; +use std::collections::HashMap; use tokio::runtime::{Builder, Runtime}; use xmtp_cryptography::utils::rng; use xmtp_mls::{builder::ClientBuilder, groups::MlsGroup, utils::test::TestClient}; -async fn create_group() -> Address { +async fn create_identity() -> Address { let wallet = LocalWallet::new(&mut rng()); let _ = ClientBuilder::new_test_client(&wallet).await; wallet.address() } -async fn create_groups(n: usize) -> Vec
{ +async fn create_identities(n: usize) -> Vec
{ let mut addresses = Vec::with_capacity(n); for _ in 0..n { - addresses.push(create_group().await); + addresses.push(create_identity().await); } addresses } -fn with_groups(num_groups: usize, fun: F) -> T -where - F: FnOnce(&TestClient, &MlsGroup, Vec, Runtime) -> T, -{ +fn setup(num_groups: usize) -> (TestClient, MlsGroup, Vec, Runtime) { let runtime = Builder::new_multi_thread() .worker_threads(4) .enable_time() @@ -34,7 +32,7 @@ where .unwrap(); let (client, group, addresses) = runtime.block_on(async { - let addresses: Vec = create_groups(num_groups) + let addresses: Vec = create_identities(num_groups) .await .into_iter() .map(hex::encode) @@ -46,26 +44,47 @@ where (client, group, addresses) }); - fun(&client, &group, addresses, runtime) + (client, group, addresses, runtime) } -fn add_100_members(c: &mut Criterion) { - with_groups(100, |client, group, addresses, runtime| { - c.bench_function("add 100 members", |b| { - b.to_async(&runtime) - .iter(|| group.add_members(client, addresses.clone())) - }); - }) -} +fn add_to_empty_group(c: &mut Criterion) { + tracing_subscriber::fmt::init(); + + let mut benchmark_group = c.benchmark_group("add_to_empty_group"); + benchmark_group.sample_size(10); + + let total_identities = [10, 100, 250, 500, 1_000, 2_000]; /* 5_000, 10_000 + 20_000 + 40_000];*/ + + println!( + "Setting up {} identities", + total_identities.iter().sum::() + ); + let (client, group, addresses, runtime) = setup(total_identities.iter().sum()); + println!("setup finished"); -fn add_1000_members(c: &mut Criterion) { - with_groups(1000, |client, group, addresses, runtime| { - c.bench_function("add 1000 members", |b| { - b.to_async(&runtime) - .iter(|| group.add_members(client, addresses.clone())) + let mut addresses = addresses.into_iter(); + let mut map = HashMap::>::new(); + + for size in total_identities { + map.insert(size, addresses.by_ref().take(size).collect()); + } + + for size in total_identities.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let addrs = map.get(&size).unwrap(); + b.to_async(&runtime).iter(|| { + println!("Adding {} members", addrs.len()); + group.add_members(&client, addrs.clone()) + }); }); - }) + } + benchmark_group.finish(); } -criterion_group!(group_limit_empty, add_100_members, add_1000_members); +criterion_group!( + name = group_limit_empty; + config = Criterion::default().sample_size(10); + targets = add_to_empty_group +); criterion_main!(group_limit_empty); diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 1871733be..3af2345fb 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -1,4 +1,4 @@ -use std::env; +use std::{env, path::Path}; use rand::{ distributions::{Alphanumeric, DistString}, From 3bbf953669d35ecdfd21dde6f0bfff2e671767d3 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 30 May 2024 18:18:15 -0400 Subject: [PATCH 04/41] pregenerate identities in order to re-use them --- Cargo.lock | 61 ++++++++++++++++++ Cargo.toml | 2 +- xmtp_mls/Cargo.toml | 9 ++- xmtp_mls/benches/group_limit_empty.rs | 53 ++++++---------- xmtp_mls/src/utils/bench.rs | 90 +++++++++++++++++++++++++++ xmtp_mls/src/utils/mod.rs | 2 + xmtp_mls/src/utils/test.rs | 13 +++- 7 files changed, 191 insertions(+), 39 deletions(-) create mode 100644 xmtp_mls/src/utils/bench.rs diff --git a/Cargo.lock b/Cargo.lock index 5899ff46f..66dd507c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -729,6 +729,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys 0.52.0", +] + [[package]] name = "console_error_panic_hook" version = "0.1.7" @@ -1240,6 +1253,12 @@ dependencies = [ "log", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "encoding_rs" version = "0.8.34" @@ -1908,6 +1927,14 @@ dependencies = [ "byteorder", ] +[[package]] +name = "generate_identities" +version = "0.1.0" +dependencies = [ + "tokio", + "xmtp_mls", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -2473,6 +2500,20 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "rayon", + "unicode-width", +] + [[package]] name = "inout" version = "0.1.3" @@ -3086,6 +3127,12 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "object" version = "0.32.2" @@ -3694,6 +3741,12 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "powerfmt" version = "0.2.0" @@ -5677,6 +5730,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -6386,6 +6445,7 @@ dependencies = [ "flume", "futures", "hex", + "indicatif", "libsqlite3-sys", "log", "mockall", @@ -6396,6 +6456,7 @@ dependencies = [ "openmls_traits", "prost 0.12.6", "rand", + "rayon", "reqwest 0.12.4", "ring 0.17.8", "serde", diff --git a/Cargo.toml b/Cargo.toml index 07d44fc67..cbc9ec51d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ "xmtp_user_preferences", "xmtp_v2", "xmtp_mls", - "xmtp_id", + "xmtp_id", "bin/generate_identities", ] exclude = ["bindings_ffi", "bindings_wasm", "xmtp_api_grpc_gateway"] diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 438220fd2..b8c54e832 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -12,7 +12,8 @@ path = "src/bin/update-schema.rs" default = ["native"] grpc = ["xmtp_proto/grpc"] native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] -test-utils = ["xmtp_api_grpc", "tracing-subscriber"] +test-utils = ["xmtp_api_grpc"] +bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow", "rayon" ] [dependencies] aes = "0.8.4" @@ -56,7 +57,9 @@ xmtp_v2 = { path = "../xmtp_v2" } # Test Utils xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } tracing-subscriber = { version = "0.3", optional = true } - +indicatif = { version = "0.17", optional = true, features = ["rayon"] } +anyhow = { workspace = true, optional = true } +rayon = { version = "1.10", optional = true } [dev-dependencies] ctor.workspace = true @@ -76,4 +79,4 @@ criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } [[bench]] name = "group_limit_empty" harness = false -required_features = ["test-utils"] +required_features = ["bench"] diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index ba6c90ac3..2d422a6a3 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -1,28 +1,17 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use ethers::{ - signers::{LocalWallet, Signer}, - types::Address, -}; +use ethers::signers::LocalWallet; use std::collections::HashMap; use tokio::runtime::{Builder, Runtime}; use xmtp_cryptography::utils::rng; -use xmtp_mls::{builder::ClientBuilder, groups::MlsGroup, utils::test::TestClient}; - -async fn create_identity() -> Address { - let wallet = LocalWallet::new(&mut rng()); - let _ = ClientBuilder::new_test_client(&wallet).await; - wallet.address() -} - -async fn create_identities(n: usize) -> Vec
{ - let mut addresses = Vec::with_capacity(n); - for _ in 0..n { - addresses.push(create_identity().await); - } - addresses -} +use xmtp_mls::{ + builder::ClientBuilder, + utils::{ + bench::{create_identities_if_dont_exist, IDENTITIES}, + test::TestClient, + }, +}; -fn setup(num_groups: usize) -> (TestClient, MlsGroup, Vec, Runtime) { +fn setup() -> (TestClient, Vec, Runtime) { let runtime = Builder::new_multi_thread() .worker_threads(4) .enable_time() @@ -31,20 +20,15 @@ fn setup(num_groups: usize) -> (TestClient, MlsGroup, Vec, Runtime) { .build() .unwrap(); - let (client, group, addresses) = runtime.block_on(async { - let addresses: Vec = create_identities(num_groups) - .await - .into_iter() - .map(hex::encode) - .collect(); + let (client, addresses) = runtime.block_on(async { + let addresses: Vec = create_identities_if_dont_exist().await; let wallet = LocalWallet::new(&mut rng()); let client = ClientBuilder::new_test_client(&wallet).await; - let group = client.create_group(None).unwrap(); - (client, group, addresses) + (client, addresses) }); - (client, group, addresses, runtime) + (client, addresses, runtime) } fn add_to_empty_group(c: &mut Criterion) { @@ -53,29 +37,30 @@ fn add_to_empty_group(c: &mut Criterion) { let mut benchmark_group = c.benchmark_group("add_to_empty_group"); benchmark_group.sample_size(10); - let total_identities = [10, 100, 250, 500, 1_000, 2_000]; /* 5_000, 10_000 + 20_000 + 40_000];*/ + let total_identities = &IDENTITIES[0..8]; println!( "Setting up {} identities", total_identities.iter().sum::() ); - let (client, group, addresses, runtime) = setup(total_identities.iter().sum()); + let (client, addresses, runtime) = setup(); println!("setup finished"); let mut addresses = addresses.into_iter(); let mut map = HashMap::>::new(); for size in total_identities { - map.insert(size, addresses.by_ref().take(size).collect()); + map.insert(*size, addresses.by_ref().take(*size).collect()); } for size in total_identities.iter() { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let addrs = map.get(&size).unwrap(); - b.to_async(&runtime).iter(|| { + b.to_async(&runtime).iter(|| async { + let group = client.create_group(None).unwrap(); println!("Adding {} members", addrs.len()); - group.add_members(&client, addrs.clone()) + group.add_members(&client, addrs.clone()).await.unwrap(); }); }); } diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs new file mode 100644 index 000000000..adf8514ba --- /dev/null +++ b/xmtp_mls/src/utils/bench.rs @@ -0,0 +1,90 @@ +use crate::builder::ClientBuilder; +use anyhow::Error; +use ethers::{ + signers::{LocalWallet, Signer}, + types::Address, +}; +use indicatif::{ParallelProgressIterator, ProgressStyle}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use std::sync::mpsc::channel; +use tokio::runtime::Builder; +use xmtp_cryptography::utils::rng; + +pub const IDENTITIES: [usize; 14] = [ + 10, 50, 100, 200, 500, 1_000, 1_500, 2_000, 3_000, 5_000, 10_000, 20_000, 30_000, 40_000, +]; + +pub fn file_path() -> String { + format!("{}/identities.generated", env!("CARGO_MANIFEST_DIR")) +} + +pub fn write_identities(num_groups: usize) -> Vec { + let addresses: Vec = create_identities(num_groups) + .into_iter() + .map(hex::encode) + .collect(); + let json = serde_json::to_string(&addresses).unwrap(); + + std::fs::write(file_path(), json).unwrap(); + + addresses +} + +pub fn load_identities() -> Result, Error> { + let addresses = std::fs::read(file_path())?; + Ok(serde_json::from_slice(addresses.as_slice())?) +} + +fn create_identity() -> Address { + let runtime = Builder::new_current_thread() + .enable_time() + .enable_io() + .thread_name("xmtp-identity-gen") + .build() + .unwrap(); + + let wallet = LocalWallet::new(&mut rng()); + let _ = runtime.block_on(ClientBuilder::new_test_client(&wallet)); + wallet.address() +} + +fn create_identities(n: usize) -> Vec
{ + let mut addresses = Vec::with_capacity(n); + + let (tx, rx) = channel(); + + let style = + ProgressStyle::with_template("{bar} {pos}/{len} elapsed {elapsed} remaining {eta_precise}"); + (0..n) + .collect::>() + .par_iter() + .progress_count(n as u64) + .with_style(style.unwrap()) + .for_each(|_| { + tx.send(create_identity()).unwrap(); + }); + + while let Ok(addr) = rx.try_recv() { + addresses.push(addr); + } + + addresses +} + +pub async fn create_identities_if_dont_exist() -> Vec { + println!("Beware, this fills $TMPDIR with ~10GBs of identities"); + + if let Ok(identities) = load_identities() { + let wallet = LocalWallet::new(&mut rng()); + let client = ClientBuilder::new_test_client(&wallet).await; + if client.is_registered(&identities[0]).await { + return identities; + } + } + + let num_identities = IDENTITIES.iter().sum(); + println!("Writing {num_identities} identities... (this will take a while...)"); + let addresses = write_identities(num_identities); + println!("Wrote {num_identities} to {}", file_path()); + addresses +} diff --git a/xmtp_mls/src/utils/mod.rs b/xmtp_mls/src/utils/mod.rs index 0f66974fc..3b9ee78bd 100644 --- a/xmtp_mls/src/utils/mod.rs +++ b/xmtp_mls/src/utils/mod.rs @@ -1,3 +1,5 @@ +#[cfg(feature = "bench")] +pub mod bench; pub mod hash; pub mod id; #[cfg(any(test, feature = "test-utils"))] diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 3af2345fb..fe3b8a50c 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -1,4 +1,4 @@ -use std::{env, path::Path}; +use std::env; use rand::{ distributions::{Alphanumeric, DistString}, @@ -76,6 +76,17 @@ impl ClientBuilder { } } +impl Client { + pub async fn is_registered(&self, address: &String) -> bool { + let ids = self + .api_client + .get_inbox_ids(vec![address.clone()]) + .await + .unwrap(); + ids.contains_key(address) + } +} + pub async fn register_client(client: &Client, owner: &impl InboxOwner) { let mut signature_request = client.context.signature_request().unwrap(); let signature_text = signature_request.signature_text(); From 377c8729e024f26987e8285b233b221443768901 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 30 May 2024 18:18:59 -0400 Subject: [PATCH 05/41] add generated to gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 0b7063779..8f868dd49 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,6 @@ libxmtp-version.txt # Directories from v2 branch bindings_swift/ ecies_bindings_wasm/ + +# Locally Generated Stuff +*.generated From 8772c542681d9a68d887ebb41bf1000f497180f9 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 30 May 2024 20:59:39 -0400 Subject: [PATCH 06/41] pregenerate identitites. do not re-use the group --- xmtp_id/src/associations/association_log.rs | 2 +- xmtp_mls/benches/group_limit_empty.rs | 38 ++++++++++++++++--- xmtp_mls/src/configuration.rs | 2 +- xmtp_mls/src/utils/bench.rs | 41 ++++++++++++++++----- 4 files changed, 65 insertions(+), 18 deletions(-) diff --git a/xmtp_id/src/associations/association_log.rs b/xmtp_id/src/associations/association_log.rs index 578d45452..af751fd1d 100644 --- a/xmtp_id/src/associations/association_log.rs +++ b/xmtp_id/src/associations/association_log.rs @@ -187,7 +187,7 @@ impl IdentityAction for AddAssociation { let new_member = Member::new(new_member_address, Some(existing_entity_id)); - println!("Adding new entity to state {:?}", &new_member); + log::info!("Adding new entity to state {:?}", &new_member); Ok(existing_state.add(new_member)) } diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index 2d422a6a3..71043bc0b 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -37,14 +37,39 @@ fn add_to_empty_group(c: &mut Criterion) { let mut benchmark_group = c.benchmark_group("add_to_empty_group"); benchmark_group.sample_size(10); - let total_identities = &IDENTITIES[0..8]; + let total_identities = &IDENTITIES[0..4]; + let (client, addresses, runtime) = setup(); + + let mut addresses = addresses.into_iter(); + let mut map = HashMap::>::new(); + + for size in total_identities { + map.insert(*size, addresses.by_ref().take(*size).collect()); + } + + for size in total_identities.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let addrs = map.get(&size).unwrap(); + b.to_async(&runtime).iter(|| async { + let group = client.create_group(None).unwrap(); + println!("Adding {} members", addrs.len()); + group.add_members(&client, addrs.clone()).await.unwrap(); + }); + }); + } + benchmark_group.finish(); +} + +/* +fn remove_from_group(c: &mut Criterion) { + tracing_subscriber::fmt::init(); + + let mut benchmark_group = c.benchmark_group("add_to_empty_group"); + benchmark_group.sample_size(10); - println!( - "Setting up {} identities", - total_identities.iter().sum::() - ); + let total_identities = &IDENTITIES[0..3]; let (client, addresses, runtime) = setup(); - println!("setup finished"); let mut addresses = addresses.into_iter(); let mut map = HashMap::>::new(); @@ -66,6 +91,7 @@ fn add_to_empty_group(c: &mut Criterion) { } benchmark_group.finish(); } +*/ criterion_group!( name = group_limit_empty; diff --git a/xmtp_mls/src/configuration.rs b/xmtp_mls/src/configuration.rs index 3591377b4..f9f8f27d8 100644 --- a/xmtp_mls/src/configuration.rs +++ b/xmtp_mls/src/configuration.rs @@ -16,7 +16,7 @@ const NANOSECONDS_IN_HOUR: i64 = 3_600_000_000_000; pub const UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NANOSECONDS_IN_HOUR / 2; // 30 min -pub const MAX_GROUP_SIZE: u8 = 250; +pub const MAX_GROUP_SIZE: u16 = 40_000; pub const DELIMITER: char = '\x01'; diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index adf8514ba..1f4062ae3 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -1,5 +1,4 @@ use crate::builder::ClientBuilder; -use anyhow::Error; use ethers::{ signers::{LocalWallet, Signer}, types::Address, @@ -7,9 +6,18 @@ use ethers::{ use indicatif::{ParallelProgressIterator, ProgressStyle}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; use std::sync::mpsc::channel; +use thiserror::Error; use tokio::runtime::Builder; use xmtp_cryptography::utils::rng; +#[derive(Debug, Error)] +pub enum BenchError { + #[error(transparent)] + Serde(#[from] serde_json::Error), + #[error(transparent)] + Io(#[from] std::io::Error), +} + pub const IDENTITIES: [usize; 14] = [ 10, 50, 100, 200, 500, 1_000, 1_500, 2_000, 3_000, 5_000, 10_000, 20_000, 30_000, 40_000, ]; @@ -30,9 +38,12 @@ pub fn write_identities(num_groups: usize) -> Vec { addresses } -pub fn load_identities() -> Result, Error> { +pub fn load_identities() -> Result, BenchError> { let addresses = std::fs::read(file_path())?; - Ok(serde_json::from_slice(addresses.as_slice())?) + Ok(serde_json::from_slice::>(addresses.as_slice())? + .into_iter() + .map(|a| format!("0x{}", a)) + .collect()) } fn create_identity() -> Address { @@ -72,16 +83,26 @@ fn create_identities(n: usize) -> Vec
{ } pub async fn create_identities_if_dont_exist() -> Vec { - println!("Beware, this fills $TMPDIR with ~10GBs of identities"); - - if let Ok(identities) = load_identities() { - let wallet = LocalWallet::new(&mut rng()); - let client = ClientBuilder::new_test_client(&wallet).await; - if client.is_registered(&identities[0]).await { - return identities; + match load_identities() { + Ok(identities) => { + println!("Found file"); + let wallet = LocalWallet::new(&mut rng()); + let client = ClientBuilder::new_test_client(&wallet).await; + if client.is_registered(&identities[0]).await { + return identities; + } + } + Err(BenchError::Serde(e)) => { + panic!("{}", e.to_string()); } + _ => (), } + println!( + "Could not find any identitites to load, creating new identitites \n + Beware, this fills $TMPDIR with ~10GBs of identities" + ); + let num_identities = IDENTITIES.iter().sum(); println!("Writing {num_identities} identities... (this will take a while...)"); let addresses = write_identities(num_identities); From 6bc625b0d21706c676264a92b0f09888ba0a4fac Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 10:27:10 -0400 Subject: [PATCH 07/41] try threadlocal --- Cargo.lock | 1 + xmtp_mls/Cargo.toml | 3 +- xmtp_mls/benches/group_limit_empty.rs | 10 +++- xmtp_mls/src/api/mls.rs | 2 + xmtp_mls/src/utils/bench.rs | 79 +++++++++++++++------------ 5 files changed, 57 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66dd507c4..092d7c9f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6465,6 +6465,7 @@ dependencies = [ "smart-default", "tempfile", "thiserror", + "thread_local", "tls_codec 0.4.1", "tokio", "toml 0.8.13", diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index b8c54e832..67c858f9a 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -13,7 +13,7 @@ default = ["native"] grpc = ["xmtp_proto/grpc"] native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] test-utils = ["xmtp_api_grpc"] -bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow", "rayon" ] +bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow", "rayon", "thread_local" ] [dependencies] aes = "0.8.4" @@ -60,6 +60,7 @@ tracing-subscriber = { version = "0.3", optional = true } indicatif = { version = "0.17", optional = true, features = ["rayon"] } anyhow = { workspace = true, optional = true } rayon = { version = "1.10", optional = true } +thread_local = { version = "1.1", optional = true } [dev-dependencies] ctor.workspace = true diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index 71043bc0b..2d96392a6 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -6,7 +6,7 @@ use xmtp_cryptography::utils::rng; use xmtp_mls::{ builder::ClientBuilder, utils::{ - bench::{create_identities_if_dont_exist, IDENTITIES}, + bench::{create_identities_if_dont_exist, IDENTITY_SAMPLES}, test::TestClient, }, }; @@ -21,7 +21,11 @@ fn setup() -> (TestClient, Vec, Runtime) { .unwrap(); let (client, addresses) = runtime.block_on(async { - let addresses: Vec = create_identities_if_dont_exist().await; + let addresses: Vec = create_identities_if_dont_exist() + .await + .into_iter() + .map(|i| i.address) + .collect(); let wallet = LocalWallet::new(&mut rng()); let client = ClientBuilder::new_test_client(&wallet).await; @@ -37,7 +41,7 @@ fn add_to_empty_group(c: &mut Criterion) { let mut benchmark_group = c.benchmark_group("add_to_empty_group"); benchmark_group.sample_size(10); - let total_identities = &IDENTITIES[0..4]; + let total_identities = &IDENTITY_SAMPLES[0..4]; let (client, addresses, runtime) = setup(); let mut addresses = addresses.into_iter(); diff --git a/xmtp_mls/src/api/mls.rs b/xmtp_mls/src/api/mls.rs index ef2765875..02f6aaa2e 100644 --- a/xmtp_mls/src/api/mls.rs +++ b/xmtp_mls/src/api/mls.rs @@ -246,6 +246,8 @@ where &self, messages: Vec, ) -> Result<(), ApiError> { + log::debug!("Sending {} welcome messages", messages.len()); + retry_async!( self.retry_strategy, (async { diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index 1f4062ae3..eb990d68c 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -1,13 +1,12 @@ use crate::builder::ClientBuilder; -use ethers::{ - signers::{LocalWallet, Signer}, - types::Address, -}; +use ethers::signers::{LocalWallet, Signer}; use indicatif::{ParallelProgressIterator, ProgressStyle}; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use serde::{Deserialize, Serialize}; use std::sync::mpsc::channel; use thiserror::Error; -use tokio::runtime::Builder; +use thread_local::ThreadLocal; +use tokio::runtime::{Builder, Runtime}; use xmtp_cryptography::utils::rng; #[derive(Debug, Error)] @@ -18,61 +17,73 @@ pub enum BenchError { Io(#[from] std::io::Error), } -pub const IDENTITIES: [usize; 14] = [ - 10, 50, 100, 200, 500, 1_000, 1_500, 2_000, 3_000, 5_000, 10_000, 20_000, 30_000, 40_000, +pub const IDENTITY_SAMPLES: [usize; 6] = [ + 10, 50, 100, 200, 500, 1_000, /*1_500, 2_000, 3_000, 5_000, 10_000, 20_000, */ ]; pub fn file_path() -> String { format!("{}/identities.generated", env!("CARGO_MANIFEST_DIR")) } -pub fn write_identities(num_groups: usize) -> Vec { - let addresses: Vec = create_identities(num_groups) - .into_iter() - .map(hex::encode) - .collect(); - let json = serde_json::to_string(&addresses).unwrap(); +pub fn write_identities(num_groups: usize) -> Vec { + let identities: Vec = create_identities(num_groups).into_iter().collect(); + let json = serde_json::to_string(&identities).unwrap(); std::fs::write(file_path(), json).unwrap(); - addresses + identities +} + +pub fn load_identities() -> Result, BenchError> { + let identities = std::fs::read(file_path())?; + Ok(serde_json::from_slice(identities.as_slice())?) } -pub fn load_identities() -> Result, BenchError> { - let addresses = std::fs::read(file_path())?; - Ok(serde_json::from_slice::>(addresses.as_slice())? - .into_iter() - .map(|a| format!("0x{}", a)) - .collect()) +#[derive(Serialize, Deserialize)] +pub struct Identity { + pub inbox_id: String, + pub address: String, } -fn create_identity() -> Address { - let runtime = Builder::new_current_thread() - .enable_time() - .enable_io() - .thread_name("xmtp-identity-gen") - .build() - .unwrap(); +impl Identity { + pub fn new(inbox_id: String, address: String) -> Self { + Identity { inbox_id, address } + } +} +fn create_identity(runtime: &Runtime) -> Identity { let wallet = LocalWallet::new(&mut rng()); - let _ = runtime.block_on(ClientBuilder::new_test_client(&wallet)); - wallet.address() + let client = runtime.block_on(ClientBuilder::new_test_client(&wallet)); + + Identity::new(client.inbox_id(), format!("{:x}", wallet.address())) } -fn create_identities(n: usize) -> Vec
{ +fn create_identities(n: usize) -> Vec { let mut addresses = Vec::with_capacity(n); let (tx, rx) = channel(); let style = ProgressStyle::with_template("{bar} {pos}/{len} elapsed {elapsed} remaining {eta_precise}"); + + let get_runtime = || -> Runtime { + Builder::new_current_thread() + .enable_time() + .enable_io() + .thread_name("xmtp-identity-gen") + .build() + .unwrap() + }; + + let tl = ThreadLocal::new(); (0..n) .collect::>() .par_iter() .progress_count(n as u64) .with_style(style.unwrap()) .for_each(|_| { - tx.send(create_identity()).unwrap(); + let runtime = tl.get_or(get_runtime); + tx.send(create_identity(runtime)).unwrap(); }); while let Ok(addr) = rx.try_recv() { @@ -82,13 +93,13 @@ fn create_identities(n: usize) -> Vec
{ addresses } -pub async fn create_identities_if_dont_exist() -> Vec { +pub async fn create_identities_if_dont_exist() -> Vec { match load_identities() { Ok(identities) => { println!("Found file"); let wallet = LocalWallet::new(&mut rng()); let client = ClientBuilder::new_test_client(&wallet).await; - if client.is_registered(&identities[0]).await { + if client.is_registered(&identities[0].address).await { return identities; } } @@ -103,7 +114,7 @@ pub async fn create_identities_if_dont_exist() -> Vec { Beware, this fills $TMPDIR with ~10GBs of identities" ); - let num_identities = IDENTITIES.iter().sum(); + let num_identities = IDENTITY_SAMPLES.iter().sum(); println!("Writing {num_identities} identities... (this will take a while...)"); let addresses = write_identities(num_identities); println!("Wrote {num_identities} to {}", file_path()); From 3d6c5e19683f9ae2e4c69f52c3bf5ff4d7cbf406 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 13:29:13 -0400 Subject: [PATCH 08/41] tokio for pregeneration. debug gRPC limits --- Cargo.lock | 8 +-- xmtp_mls/Cargo.toml | 8 +-- xmtp_mls/benches/group_limit_empty.rs | 85 +++++++++++++++------------ xmtp_mls/src/groups/sync.rs | 2 - xmtp_mls/src/utils/bench.rs | 84 ++++++++++++-------------- xmtp_mls/src/utils/test.rs | 1 + 6 files changed, 92 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 092d7c9f9..183edd2a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1932,6 +1932,7 @@ name = "generate_identities" version = "0.1.0" dependencies = [ "tokio", + "tracing-subscriber", "xmtp_mls", ] @@ -2510,7 +2511,6 @@ dependencies = [ "instant", "number_prefix", "portable-atomic", - "rayon", "unicode-width", ] @@ -2783,9 +2783,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "openssl-sys", @@ -6456,7 +6456,6 @@ dependencies = [ "openmls_traits", "prost 0.12.6", "rand", - "rayon", "reqwest 0.12.4", "ring 0.17.8", "serde", @@ -6465,7 +6464,6 @@ dependencies = [ "smart-default", "tempfile", "thiserror", - "thread_local", "tls_codec 0.4.1", "tokio", "toml 0.8.13", diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 67c858f9a..a3ff82a0e 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -13,7 +13,7 @@ default = ["native"] grpc = ["xmtp_proto/grpc"] native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] test-utils = ["xmtp_api_grpc"] -bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow", "rayon", "thread_local" ] +bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow" ] [dependencies] aes = "0.8.4" @@ -31,7 +31,7 @@ ethers.workspace = true ethers-core.workspace = true futures.workspace = true hex.workspace = true -libsqlite3-sys = { version = "0.26.0", optional = true } +libsqlite3-sys = { version = "0.28.0", optional = true } log.workspace = true openmls = { workspace = true, features = ["test-utils"] } openmls_basic_credential = { workspace = true } @@ -57,10 +57,8 @@ xmtp_v2 = { path = "../xmtp_v2" } # Test Utils xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } tracing-subscriber = { version = "0.3", optional = true } -indicatif = { version = "0.17", optional = true, features = ["rayon"] } +indicatif = { version = "0.17", optional = true } anyhow = { workspace = true, optional = true } -rayon = { version = "1.10", optional = true } -thread_local = { version = "1.1", optional = true } [dev-dependencies] ctor.workspace = true diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index 2d96392a6..68444f89a 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -1,17 +1,31 @@ use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use ethers::signers::LocalWallet; use std::collections::HashMap; +use std::sync::Once; use tokio::runtime::{Builder, Runtime}; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; use xmtp_cryptography::utils::rng; use xmtp_mls::{ builder::ClientBuilder, utils::{ - bench::{create_identities_if_dont_exist, IDENTITY_SAMPLES}, + bench::{create_identities_if_dont_exist, Identity}, test::TestClient, }, }; -fn setup() -> (TestClient, Vec, Runtime) { +static INIT: Once = Once::new(); + +pub(crate) fn init_logging() { + INIT.call_once(|| { + let fmt = fmt::layer().compact(); + Registry::default() + .with(EnvFilter::from_default_env()) + .with(fmt) + .init() + }) +} + +fn setup() -> (TestClient, Vec, Runtime) { let runtime = Builder::new_multi_thread() .worker_threads(4) .enable_time() @@ -20,44 +34,40 @@ fn setup() -> (TestClient, Vec, Runtime) { .build() .unwrap(); - let (client, addresses) = runtime.block_on(async { - let addresses: Vec = create_identities_if_dont_exist() - .await - .into_iter() - .map(|i| i.address) - .collect(); + let (client, identities) = runtime.block_on(async { + let identities: Vec = create_identities_if_dont_exist().await; + let wallet = LocalWallet::new(&mut rng()); let client = ClientBuilder::new_test_client(&wallet).await; - (client, addresses) + (client, identities) }); - (client, addresses, runtime) + (client, identities, runtime) } fn add_to_empty_group(c: &mut Criterion) { - tracing_subscriber::fmt::init(); - + init_logging(); let mut benchmark_group = c.benchmark_group("add_to_empty_group"); benchmark_group.sample_size(10); - let total_identities = &IDENTITY_SAMPLES[0..4]; - let (client, addresses, runtime) = setup(); + let identity_samples = [ + 5, 10, 20, 40, 80, 100, 200, /* 400, 800, 1_000, 2_000, 4_000, 8_000, 10_000, 20_000,*/ + ]; + let (client, identities, runtime) = setup(); + let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); - let mut addresses = addresses.into_iter(); let mut map = HashMap::>::new(); - - for size in total_identities { - map.insert(*size, addresses.by_ref().take(*size).collect()); + for size in identity_samples { + map.insert(size, addresses.iter().take(size).cloned().collect()); } - for size in total_identities.iter() { + for size in identity_samples.iter() { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let addrs = map.get(&size).unwrap(); b.to_async(&runtime).iter(|| async { let group = client.create_group(None).unwrap(); - println!("Adding {} members", addrs.len()); group.add_members(&client, addrs.clone()).await.unwrap(); }); }); @@ -65,41 +75,40 @@ fn add_to_empty_group(c: &mut Criterion) { benchmark_group.finish(); } -/* -fn remove_from_group(c: &mut Criterion) { - tracing_subscriber::fmt::init(); - - let mut benchmark_group = c.benchmark_group("add_to_empty_group"); +fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("add_to_empty_group_by_inbox_id"); benchmark_group.sample_size(10); - let total_identities = &IDENTITIES[0..3]; - let (client, addresses, runtime) = setup(); + let identity_samples = [ + 5, 10, 20, 40, 80, 100, 200, /*400, 800, 1_000, 2_000, 4_000, 8_000, 10_000, 20_000, */ + ]; + let (client, identities, runtime) = setup(); + let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); - let mut addresses = addresses.into_iter(); let mut map = HashMap::>::new(); - - for size in total_identities { - map.insert(*size, addresses.by_ref().take(*size).collect()); + for size in identity_samples { + map.insert(size, inbox_ids.iter().take(size).cloned().collect()); } - for size in total_identities.iter() { + for size in identity_samples.iter() { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { - let addrs = map.get(&size).unwrap(); + let ids = map.get(&size).unwrap(); b.to_async(&runtime).iter(|| async { let group = client.create_group(None).unwrap(); - println!("Adding {} members", addrs.len()); - group.add_members(&client, addrs.clone()).await.unwrap(); + group + .add_members_by_inbox_id(&client, ids.clone()) + .await + .unwrap(); }); }); } benchmark_group.finish(); } -*/ criterion_group!( name = group_limit_empty; config = Criterion::default().sample_size(10); - targets = add_to_empty_group -); + targets = add_to_empty_group, add_to_empty_group_by_inbox_id); criterion_main!(group_limit_empty); diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index 6eaddb133..b339c9622 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -919,7 +919,6 @@ impl MlsGroup { action.welcome_message.as_slice(), installation.hpke_public_key.as_slice(), )?; - Ok(WelcomeMessageInput { version: Some(WelcomeMessageInputVersion::V1(WelcomeMessageInputV1 { installation_key, @@ -929,7 +928,6 @@ impl MlsGroup { }) }) .collect::, HpkeError>>()?; - client.api_client.send_welcome_messages(welcomes).await?; Ok(()) diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index eb990d68c..ade3e6156 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -1,12 +1,8 @@ use crate::builder::ClientBuilder; use ethers::signers::{LocalWallet, Signer}; -use indicatif::{ParallelProgressIterator, ProgressStyle}; -use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use indicatif::{ProgressBar, ProgressStyle}; use serde::{Deserialize, Serialize}; -use std::sync::mpsc::channel; use thiserror::Error; -use thread_local::ThreadLocal; -use tokio::runtime::{Builder, Runtime}; use xmtp_cryptography::utils::rng; #[derive(Debug, Error)] @@ -17,16 +13,14 @@ pub enum BenchError { Io(#[from] std::io::Error), } -pub const IDENTITY_SAMPLES: [usize; 6] = [ - 10, 50, 100, 200, 500, 1_000, /*1_500, 2_000, 3_000, 5_000, 10_000, 20_000, */ -]; +pub const MAX_IDENTITIES: usize = 20_000; pub fn file_path() -> String { format!("{}/identities.generated", env!("CARGO_MANIFEST_DIR")) } -pub fn write_identities(num_groups: usize) -> Vec { - let identities: Vec = create_identities(num_groups).into_iter().collect(); +pub async fn write_identities(num_groups: usize) -> Vec { + let identities: Vec = create_identities(num_groups).await.into_iter().collect(); let json = serde_json::to_string(&identities).unwrap(); std::fs::write(file_path(), json).unwrap(); @@ -51,52 +45,51 @@ impl Identity { } } -fn create_identity(runtime: &Runtime) -> Identity { +async fn create_identity() -> Identity { let wallet = LocalWallet::new(&mut rng()); - let client = runtime.block_on(ClientBuilder::new_test_client(&wallet)); + let client = ClientBuilder::new_test_client(&wallet).await; - Identity::new(client.inbox_id(), format!("{:x}", wallet.address())) + Identity::new(client.inbox_id(), format!("0x{:x}", wallet.address())) } -fn create_identities(n: usize) -> Vec { - let mut addresses = Vec::with_capacity(n); - - let (tx, rx) = channel(); +async fn create_identities(n: usize) -> Vec { + let mut identities = Vec::with_capacity(n); let style = ProgressStyle::with_template("{bar} {pos}/{len} elapsed {elapsed} remaining {eta_precise}"); - let get_runtime = || -> Runtime { - Builder::new_current_thread() - .enable_time() - .enable_io() - .thread_name("xmtp-identity-gen") - .build() - .unwrap() - }; - - let tl = ThreadLocal::new(); - (0..n) - .collect::>() - .par_iter() - .progress_count(n as u64) - .with_style(style.unwrap()) - .for_each(|_| { - let runtime = tl.get_or(get_runtime); - tx.send(create_identity(runtime)).unwrap(); - }); - - while let Ok(addr) = rx.try_recv() { - addresses.push(addr); + let mut set = tokio::task::JoinSet::new(); + let bar = ProgressBar::new(n as u64).with_style(style.unwrap()); + let mut handles = vec![]; + + for _ in 0..n { + let bar_pointer = bar.clone(); + handles.push(set.spawn(async move { + let identity = create_identity().await; + bar_pointer.inc(1); + identity + })); + + // going above 128 we hit "unable to open database errors" + // This may be related to open file limits + if set.len() == 128 { + if let Some(Ok(identity)) = set.join_next().await { + identities.push(identity); + } + } } - addresses + while let Some(Ok(identity)) = set.join_next().await { + identities.push(identity); + } + + identities } pub async fn create_identities_if_dont_exist() -> Vec { match load_identities() { Ok(identities) => { - println!("Found file"); + log::info!("Found generated identities, checking for existence on backend..."); let wallet = LocalWallet::new(&mut rng()); let client = ClientBuilder::new_test_client(&wallet).await; if client.is_registered(&identities[0].address).await { @@ -109,14 +102,13 @@ pub async fn create_identities_if_dont_exist() -> Vec { _ => (), } - println!( + log::info!( "Could not find any identitites to load, creating new identitites \n Beware, this fills $TMPDIR with ~10GBs of identities" ); - let num_identities = IDENTITY_SAMPLES.iter().sum(); - println!("Writing {num_identities} identities... (this will take a while...)"); - let addresses = write_identities(num_identities); - println!("Wrote {num_identities} to {}", file_path()); + println!("Writing {MAX_IDENTITIES} identities... (this will take a while...)"); + let addresses = write_identities(MAX_IDENTITIES).await; + println!("Wrote {MAX_IDENTITIES} to {}", file_path()); addresses } diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index fe3b8a50c..716625715 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -53,6 +53,7 @@ impl ClientBuilder { pub fn temp_store(self) -> Self { let tmpdb = tmp_path(); + log::debug!("database path {}", tmpdb); self.store( EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(), ) From a1b9a2de8a97ad4ac61d79258af8e48979c8dca7 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 13:37:10 -0400 Subject: [PATCH 09/41] naively chunk sending welcomes --- xmtp_mls/benches/group_limit_empty.rs | 2 +- xmtp_mls/src/api/mls.rs | 4 ++-- xmtp_mls/src/groups/sync.rs | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index 68444f89a..2bf762387 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -52,7 +52,7 @@ fn add_to_empty_group(c: &mut Criterion) { benchmark_group.sample_size(10); let identity_samples = [ - 5, 10, 20, 40, 80, 100, 200, /* 400, 800, 1_000, 2_000, 4_000, 8_000, 10_000, 20_000,*/ + 5, 10, 20, 40, 80, 100, 200, 400, 800, 1_000, 2_000, 4_000, 8_000, 10_000, 20_000, ]; let (client, identities, runtime) = setup(); let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); diff --git a/xmtp_mls/src/api/mls.rs b/xmtp_mls/src/api/mls.rs index 02f6aaa2e..5344a8501 100644 --- a/xmtp_mls/src/api/mls.rs +++ b/xmtp_mls/src/api/mls.rs @@ -244,7 +244,7 @@ where pub async fn send_welcome_messages( &self, - messages: Vec, + messages: &[WelcomeMessageInput], ) -> Result<(), ApiError> { log::debug!("Sending {} welcome messages", messages.len()); @@ -253,7 +253,7 @@ where (async { self.api_client .send_welcome_messages(SendWelcomeMessagesRequest { - messages: messages.clone(), + messages: messages.to_vec(), }) .await }) diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index b339c9622..748367066 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -928,7 +928,9 @@ impl MlsGroup { }) }) .collect::, HpkeError>>()?; - client.api_client.send_welcome_messages(welcomes).await?; + for welcomes in welcomes.chunks(200) { + client.api_client.send_welcome_messages(welcomes).await?; + } Ok(()) } From be0c12ef25650a0efa18275d7b84457c9792ba66 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 14:47:04 -0400 Subject: [PATCH 10/41] parallelize --- dev/docker/docker-compose.yml | 4 +--- xmtp_mls/benches/group_limit_empty.rs | 15 +++++---------- xmtp_mls/src/groups/sync.rs | 6 +++++- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/dev/docker/docker-compose.yml b/dev/docker/docker-compose.yml index 01dda9772..f346a0812 100644 --- a/dev/docker/docker-compose.yml +++ b/dev/docker/docker-compose.yml @@ -11,6 +11,7 @@ services: - --mls-store.db-connection-string=postgres://postgres:xmtp@mlsdb:5432/postgres?sslmode=disable - --mls-validation.grpc-address=validation:50051 - --api.enable-mls + - --api.max-msg-size=104857600 - --wait-for-db=30s # Disable authn until we have reliable support for generating auth tokens # - --api.authn.enable @@ -19,19 +20,16 @@ services: - 5556:5556 depends_on: - db - validation: image: ghcr.io/xmtp/mls-validation-service:main platform: linux/amd64 build: context: ../.. dockerfile: ./dev/validation_service/local.Dockerfile - db: image: postgres:13 environment: POSTGRES_PASSWORD: xmtp - mlsdb: image: postgres:13 environment: diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs index 2bf762387..90ebfeafa 100644 --- a/xmtp_mls/benches/group_limit_empty.rs +++ b/xmtp_mls/benches/group_limit_empty.rs @@ -14,6 +14,7 @@ use xmtp_mls::{ }; static INIT: Once = Once::new(); +pub const IDENTITY_SAMPLES: [usize; 12] = [5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800]; pub(crate) fn init_logging() { INIT.call_once(|| { @@ -51,18 +52,15 @@ fn add_to_empty_group(c: &mut Criterion) { let mut benchmark_group = c.benchmark_group("add_to_empty_group"); benchmark_group.sample_size(10); - let identity_samples = [ - 5, 10, 20, 40, 80, 100, 200, 400, 800, 1_000, 2_000, 4_000, 8_000, 10_000, 20_000, - ]; let (client, identities, runtime) = setup(); let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); let mut map = HashMap::>::new(); - for size in identity_samples { + for size in IDENTITY_SAMPLES { map.insert(size, addresses.iter().take(size).cloned().collect()); } - for size in identity_samples.iter() { + for size in IDENTITY_SAMPLES.iter() { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let addrs = map.get(&size).unwrap(); @@ -80,18 +78,15 @@ fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { let mut benchmark_group = c.benchmark_group("add_to_empty_group_by_inbox_id"); benchmark_group.sample_size(10); - let identity_samples = [ - 5, 10, 20, 40, 80, 100, 200, /*400, 800, 1_000, 2_000, 4_000, 8_000, 10_000, 20_000, */ - ]; let (client, identities, runtime) = setup(); let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); let mut map = HashMap::>::new(); - for size in identity_samples { + for size in IDENTITY_SAMPLES { map.insert(size, inbox_ids.iter().take(size).cloned().collect()); } - for size in identity_samples.iter() { + for size in IDENTITY_SAMPLES.iter() { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index 748367066..ec46466c2 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -34,6 +34,7 @@ use crate::{ xmtp_openmls_provider::XmtpOpenMlsProvider, Client, Delete, Fetch, Store, XmtpApi, }; +use futures::future::try_join_all; use log::debug; use openmls::{ credentials::BasicCredential, @@ -928,9 +929,12 @@ impl MlsGroup { }) }) .collect::, HpkeError>>()?; + + let mut futures = vec![]; for welcomes in welcomes.chunks(200) { - client.api_client.send_welcome_messages(welcomes).await?; + futures.push(client.api_client.send_welcome_messages(welcomes)); } + try_join_all(futures).await?; Ok(()) } From b8639c66d3984ea2243ae89d9871000cdd4e703d Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 15:59:02 -0400 Subject: [PATCH 11/41] dynamic chunk sizing --- xmtp_mls/src/groups/sync.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index ec46466c2..4ae0a7f73 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -69,6 +69,8 @@ use xmtp_proto::xmtp::mls::{ }, }; +pub const MAX_CHUNK: usize = 50 * 1024 * 1024; + impl MlsGroup { pub async fn sync(&self, client: &Client) -> Result<(), GroupError> where @@ -930,8 +932,20 @@ impl MlsGroup { }) .collect::, HpkeError>>()?; + let welcome = welcomes.first().unwrap(); + let chunk_size = MAX_CHUNK + / welcome + .version + .as_ref() + .map(|w| match w { + WelcomeMessageInputVersion::V1(w) => { + w.installation_key.len() + w.data.len() + w.hpke_public_key.len() + } + }) + .unwrap_or(MAX_CHUNK / 200); + log::debug!("Chunk Size {}", chunk_size); let mut futures = vec![]; - for welcomes in welcomes.chunks(200) { + for welcomes in welcomes.chunks(chunk_size) { futures.push(client.api_client.send_welcome_messages(welcomes)); } try_join_all(futures).await?; From cb1edf945c0cf79ab3c44bc809a0a38ba06d800d Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 16:05:06 -0400 Subject: [PATCH 12/41] remove log --- xmtp_mls/src/groups/sync.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index 4ae0a7f73..694c1e64a 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -69,6 +69,8 @@ use xmtp_proto::xmtp::mls::{ }, }; +// TODO: Change `send_welcomes` to be constant-sized +/// the max size gRPC will accept pub const MAX_CHUNK: usize = 50 * 1024 * 1024; impl MlsGroup { @@ -933,6 +935,7 @@ impl MlsGroup { .collect::, HpkeError>>()?; let welcome = welcomes.first().unwrap(); + let chunk_size = MAX_CHUNK / welcome .version @@ -943,7 +946,7 @@ impl MlsGroup { } }) .unwrap_or(MAX_CHUNK / 200); - log::debug!("Chunk Size {}", chunk_size); + let mut futures = vec![]; for welcomes in welcomes.chunks(chunk_size) { futures.push(client.api_client.send_welcome_messages(welcomes)); From 3adb77fcdb696495375870f24f1418c1d3315399 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 17:14:00 -0400 Subject: [PATCH 13/41] unwrap -> error --- Cargo.lock | 9 --------- Cargo.toml | 2 +- xmtp_mls/Cargo.toml | 1 + xmtp_mls/src/groups/sync.rs | 4 +++- 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 183edd2a4..825cb2692 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1927,15 +1927,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "generate_identities" -version = "0.1.0" -dependencies = [ - "tokio", - "tracing-subscriber", - "xmtp_mls", -] - [[package]] name = "generic-array" version = "0.14.7" diff --git a/Cargo.toml b/Cargo.toml index cbc9ec51d..07d44fc67 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ members = [ "xmtp_user_preferences", "xmtp_v2", "xmtp_mls", - "xmtp_id", "bin/generate_identities", + "xmtp_id", ] exclude = ["bindings_ffi", "bindings_wasm", "xmtp_api_grpc_gateway"] diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index a3ff82a0e..fc8c246ab 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -79,3 +79,4 @@ criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } name = "group_limit_empty" harness = false required_features = ["bench"] + diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index 694c1e64a..d132a5774 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -934,7 +934,9 @@ impl MlsGroup { }) .collect::, HpkeError>>()?; - let welcome = welcomes.first().unwrap(); + let welcome = welcomes + .first() + .ok_or(GroupError::Generic("No welcomes to send".to_string()))?; let chunk_size = MAX_CHUNK / welcome From e047bc174dc53f54edcd0610670e1d7129192817 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 31 May 2024 17:29:19 -0400 Subject: [PATCH 14/41] update diesel/sqlite-sys --- Cargo.lock | 111 ++++++++++++++++++++++++++++---------------- xmtp_mls/Cargo.toml | 2 +- 2 files changed, 72 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 825cb2692..3a1678a64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -972,6 +972,41 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.66", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.66", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -1021,9 +1056,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.1.6" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" dependencies = [ "diesel_derives", "libsqlite3-sys", @@ -1033,11 +1068,12 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +checksum = "0d6fdd83d5947068817016e939596d246e5367279453f2a3433287894f2f2996" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", "syn 2.0.66", @@ -1045,9 +1081,9 @@ dependencies = [ [[package]] name = "diesel_migrations" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" dependencies = [ "diesel", "migrations_internals", @@ -1056,9 +1092,9 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ "syn 2.0.66", ] @@ -1138,6 +1174,20 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" +[[package]] +name = "dsl_auto_type" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab32c18ea6760d951659768a3e35ea72fc1ba0916d665a88dfe048b2a41e543f" +dependencies = [ + "darling", + "either", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1462,7 +1512,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.66", - "toml 0.8.13", + "toml", "walkdir", ] @@ -2418,6 +2468,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -2843,19 +2899,19 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "migrations_internals" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.7.8", + "toml", ] [[package]] name = "migrations_macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" dependencies = [ "migrations_internals", "proc-macro2", @@ -5387,18 +5443,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.13" @@ -5420,19 +5464,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.21.1" @@ -6457,7 +6488,7 @@ dependencies = [ "thiserror", "tls_codec 0.4.1", "tokio", - "toml 0.8.13", + "toml", "tracing", "tracing-log", "tracing-subscriber", diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index fc8c246ab..5ba7210ed 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -20,7 +20,7 @@ aes = "0.8.4" aes-gcm = { version = "0.10.3", features = ["std"] } async-trait.workspace = true chrono = { workspace = true } -diesel = { version = "2.1.3", features = [ +diesel = { version = "2.2", features = [ "sqlite", "r2d2", "returning_clauses_for_sqlite_3_35", From 1cb1d54cca5933c680a5c6b29522daa636eaba8a Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Mon, 3 Jun 2024 17:55:04 -0400 Subject: [PATCH 15/41] comment add to populated group until 810 is fixed --- xmtp_mls/Cargo.toml | 3 +- xmtp_mls/benches/group_limit.rs | 321 ++++++++++++++++++++++++++ xmtp_mls/benches/group_limit_empty.rs | 109 --------- xmtp_mls/src/utils/bench.rs | 17 +- 4 files changed, 333 insertions(+), 117 deletions(-) create mode 100644 xmtp_mls/benches/group_limit.rs delete mode 100644 xmtp_mls/benches/group_limit_empty.rs diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 5ba7210ed..6dfc6a2bd 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -76,7 +76,6 @@ anyhow.workspace = true criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } [[bench]] -name = "group_limit_empty" +name = "group_limit" harness = false -required_features = ["bench"] diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs new file mode 100644 index 000000000..6f7b92e69 --- /dev/null +++ b/xmtp_mls/benches/group_limit.rs @@ -0,0 +1,321 @@ +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; +use ethers::signers::LocalWallet; +use std::{collections::HashMap, sync::Arc, sync::Once}; +use tokio::runtime::{Builder, Handle, Runtime}; +use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; +use xmtp_cryptography::utils::rng; +use xmtp_mls::{ + builder::ClientBuilder, + utils::{ + bench::{create_identities_if_dont_exist, Identity}, + test::TestClient, + }, +}; + +static INIT: Once = Once::new(); +pub const IDENTITY_SAMPLES: [usize; 12] = [5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800]; +pub const MAX_IDENTITIES: usize = 5_000; +pub const SAMPLE_SIZE: usize = 10; + +pub(crate) fn init_logging() { + INIT.call_once(|| { + let fmt = fmt::layer().compact(); + Registry::default() + .with(EnvFilter::from_default_env()) + .with(fmt) + .init() + }) +} + +fn setup() -> (Arc, Vec, Runtime) { + let runtime = Builder::new_multi_thread() + .worker_threads(4) + .enable_time() + .enable_io() + .thread_name("xmtp-bencher") + .build() + .unwrap(); + + let (client, identities) = runtime.block_on(async { + let identities: Vec = create_identities_if_dont_exist(MAX_IDENTITIES).await; + + let wallet = LocalWallet::new(&mut rng()); + let client = Arc::new(ClientBuilder::new_test_client(&wallet).await); + + (client, identities) + }); + + (client, identities, runtime) +} + +/// criterion `batch_iter` surrounds the closure in a `Runtime.block_on` despite being a sync +/// function, even in the async 'to_async` setup. Therefore we do this (only _slightly_) hacky +/// workaround to allow us to async setup some groups. +fn bench_async_setup(fun: F) -> T +where + F: Fn() -> Fut, + Fut: futures::future::Future, +{ + tokio::task::block_in_place(move || Handle::current().block_on(async move { fun().await })) +} + +fn add_to_empty_group(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("add_to_empty_group"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); + + let mut map = HashMap::>::new(); + for size in IDENTITY_SAMPLES { + map.insert(size, addresses.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let addrs = map.get(&size).unwrap(); + b.to_async(&runtime).iter_batched( + || (client.clone(), client.create_group(None).unwrap()), + |(client, group)| async move { + group.add_members(&client, addrs.clone()).await.unwrap(); + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + +fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("add_to_empty_group_by_inbox_id"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); + + let mut map = HashMap::>::new(); + for size in IDENTITY_SAMPLES { + map.insert(size, inbox_ids.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let ids = map.get(&size).unwrap(); + b.to_async(&runtime).iter_batched( + || (client.clone(), client.create_group(None).unwrap()), + |(client, group)| async move { + group + .add_members_by_inbox_id(&client, ids.clone()) + .await + .unwrap(); + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + +#[allow(dead_code)] +// requires https://github.com/xmtp/libxmtp/issues/810 to work +fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("add_to_100_member_group_by_inbox_id"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); + + let mut map = HashMap::>::new(); + + for size in IDENTITY_SAMPLES { + map.insert(size, inbox_ids.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let ids = map.get(&size).unwrap(); + // let setup = setup(&runtime); + b.to_async(&runtime).iter_batched( + || { + bench_async_setup(|| async { + let group = client.create_group(None).unwrap(); + group + .add_members_by_inbox_id( + &client, + // it is OK to take from the back for now because we aren't getting + // near MAX_IDENTITIES + inbox_ids.iter().rev().take(100).cloned().collect(), + ) + .await + .unwrap(); + + (client.clone(), group) + }) + }, + |(client, group)| { + let client = client.clone(); + async move { + group + .add_members_by_inbox_id(&client, ids.clone()) + .await + .unwrap(); + } + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + +#[allow(dead_code)] +// requires https://github.com/xmtp/libxmtp/issues/810 to work +fn add_to_100_member_group_by_address(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("add_to_100_member_group_by_address"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); + + let mut map = HashMap::>::new(); + + for size in IDENTITY_SAMPLES { + map.insert(size, addresses.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let ids = map.get(&size).unwrap(); + b.to_async(&runtime).iter_batched( + || { + bench_async_setup(|| async { + let group = client.create_group(None).unwrap(); + group + .add_members( + &client, + // it is OK to take from the back for now because we aren't getting + // near MAX_IDENTITIES + addresses.iter().rev().take(100).cloned().collect(), + ) + .await + .unwrap(); + + (client.clone(), group) + }) + }, + |(client, group)| async move { + println!("Adding {} to group", ids.len()); + group.add_members(&client, ids.clone()).await.unwrap(); + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + +fn remove_all_members_from_group(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("remove_all_members_from_group"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); + + let mut map = HashMap::>::new(); + + for size in IDENTITY_SAMPLES { + map.insert(size, inbox_ids.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let ids = map.get(&size).unwrap(); + + b.to_async(&runtime).iter_batched( + || { + bench_async_setup(|| async { + let group = client.create_group(None).unwrap(); + group + .add_members_by_inbox_id( + &client, + inbox_ids.iter().take(size).cloned().collect(), + ) + .await + .unwrap(); + (client.clone(), group) + }) + }, + |(client, group)| async move { + group + .remove_members_by_inbox_id(&client, ids.clone()) + .await + .unwrap(); + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + +fn remove_half_members_from_group(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("remove_half_members_from_group"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); + + let mut map = HashMap::>::new(); + + for size in IDENTITY_SAMPLES { + map.insert(size, inbox_ids.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let ids = map.get(&size).unwrap(); + + b.to_async(&runtime).iter_batched( + || { + bench_async_setup(|| async { + let group = client.create_group(None).unwrap(); + group + .add_members_by_inbox_id( + &client, + inbox_ids.iter().take(size).cloned().collect(), + ) + .await + .unwrap(); + (client.clone(), group) + }) + }, + |(client, group)| async move { + group + .remove_members_by_inbox_id(&client, ids[0..(size / 2)].into()) + .await + .unwrap(); + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + +criterion_group!( + name = group_limit; + config = Criterion::default().sample_size(10); + targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group); +criterion_main!(group_limit); diff --git a/xmtp_mls/benches/group_limit_empty.rs b/xmtp_mls/benches/group_limit_empty.rs deleted file mode 100644 index 90ebfeafa..000000000 --- a/xmtp_mls/benches/group_limit_empty.rs +++ /dev/null @@ -1,109 +0,0 @@ -use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; -use ethers::signers::LocalWallet; -use std::collections::HashMap; -use std::sync::Once; -use tokio::runtime::{Builder, Runtime}; -use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; -use xmtp_cryptography::utils::rng; -use xmtp_mls::{ - builder::ClientBuilder, - utils::{ - bench::{create_identities_if_dont_exist, Identity}, - test::TestClient, - }, -}; - -static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 12] = [5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800]; - -pub(crate) fn init_logging() { - INIT.call_once(|| { - let fmt = fmt::layer().compact(); - Registry::default() - .with(EnvFilter::from_default_env()) - .with(fmt) - .init() - }) -} - -fn setup() -> (TestClient, Vec, Runtime) { - let runtime = Builder::new_multi_thread() - .worker_threads(4) - .enable_time() - .enable_io() - .thread_name("xmtp-bencher") - .build() - .unwrap(); - - let (client, identities) = runtime.block_on(async { - let identities: Vec = create_identities_if_dont_exist().await; - - let wallet = LocalWallet::new(&mut rng()); - let client = ClientBuilder::new_test_client(&wallet).await; - - (client, identities) - }); - - (client, identities, runtime) -} - -fn add_to_empty_group(c: &mut Criterion) { - init_logging(); - let mut benchmark_group = c.benchmark_group("add_to_empty_group"); - benchmark_group.sample_size(10); - - let (client, identities, runtime) = setup(); - let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); - - let mut map = HashMap::>::new(); - for size in IDENTITY_SAMPLES { - map.insert(size, addresses.iter().take(size).cloned().collect()); - } - - for size in IDENTITY_SAMPLES.iter() { - benchmark_group.throughput(Throughput::Elements(*size as u64)); - benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { - let addrs = map.get(&size).unwrap(); - b.to_async(&runtime).iter(|| async { - let group = client.create_group(None).unwrap(); - group.add_members(&client, addrs.clone()).await.unwrap(); - }); - }); - } - benchmark_group.finish(); -} - -fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { - init_logging(); - let mut benchmark_group = c.benchmark_group("add_to_empty_group_by_inbox_id"); - benchmark_group.sample_size(10); - - let (client, identities, runtime) = setup(); - let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); - - let mut map = HashMap::>::new(); - for size in IDENTITY_SAMPLES { - map.insert(size, inbox_ids.iter().take(size).cloned().collect()); - } - - for size in IDENTITY_SAMPLES.iter() { - benchmark_group.throughput(Throughput::Elements(*size as u64)); - benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { - let ids = map.get(&size).unwrap(); - b.to_async(&runtime).iter(|| async { - let group = client.create_group(None).unwrap(); - group - .add_members_by_inbox_id(&client, ids.clone()) - .await - .unwrap(); - }); - }); - } - benchmark_group.finish(); -} - -criterion_group!( - name = group_limit_empty; - config = Criterion::default().sample_size(10); - targets = add_to_empty_group, add_to_empty_group_by_inbox_id); -criterion_main!(group_limit_empty); diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index ade3e6156..33097caf8 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -1,3 +1,6 @@ +//! Utilities for xmtp_mls benchmarks +//! Utilities mostly include pre-generating identities in order to save time when writing/testing +//! benchmarks. use crate::builder::ClientBuilder; use ethers::signers::{LocalWallet, Signer}; use indicatif::{ProgressBar, ProgressStyle}; @@ -13,8 +16,6 @@ pub enum BenchError { Io(#[from] std::io::Error), } -pub const MAX_IDENTITIES: usize = 20_000; - pub fn file_path() -> String { format!("{}/identities.generated", env!("CARGO_MANIFEST_DIR")) } @@ -86,7 +87,11 @@ async fn create_identities(n: usize) -> Vec { identities } -pub async fn create_identities_if_dont_exist() -> Vec { +/// Create identities if they don't already exist. +/// creates specified `identities` on the +/// local gRPC for [`ClientBuilder::new_test_client`] and saves them to the file, +/// `identities.generated`. Uses this file for subsequent runs. +pub async fn create_identities_if_dont_exist(identities: usize) -> Vec { match load_identities() { Ok(identities) => { log::info!("Found generated identities, checking for existence on backend..."); @@ -107,8 +112,8 @@ pub async fn create_identities_if_dont_exist() -> Vec { Beware, this fills $TMPDIR with ~10GBs of identities" ); - println!("Writing {MAX_IDENTITIES} identities... (this will take a while...)"); - let addresses = write_identities(MAX_IDENTITIES).await; - println!("Wrote {MAX_IDENTITIES} to {}", file_path()); + println!("Writing {identities} identities... (this will take a while...)"); + let addresses = write_identities(identities).await; + println!("Wrote {identities} to {}", file_path()); addresses } From 20e1e169af60f82d678eaa20063937be73fcde0c Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Mon, 3 Jun 2024 21:00:00 -0400 Subject: [PATCH 16/41] use ids where possible --- xmtp_mls/benches/group_limit.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index 6f7b92e69..ccbd7df67 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -246,10 +246,7 @@ fn remove_all_members_from_group(c: &mut Criterion) { bench_async_setup(|| async { let group = client.create_group(None).unwrap(); group - .add_members_by_inbox_id( - &client, - inbox_ids.iter().take(size).cloned().collect(), - ) + .add_members_by_inbox_id(&client, ids.clone()) .await .unwrap(); (client.clone(), group) @@ -292,10 +289,7 @@ fn remove_half_members_from_group(c: &mut Criterion) { bench_async_setup(|| async { let group = client.create_group(None).unwrap(); group - .add_members_by_inbox_id( - &client, - inbox_ids.iter().take(size).cloned().collect(), - ) + .add_members_by_inbox_id(&client, ids.clone()) .await .unwrap(); (client.clone(), group) From aef6fa760b540b1c8a27185183b6c2275d195f9d Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Mon, 3 Jun 2024 21:25:34 -0400 Subject: [PATCH 17/41] fix from merge --- xmtp_mls/benches/group_limit.rs | 3 ++- xmtp_mls/src/builder.rs | 40 --------------------------------- xmtp_mls/src/utils/test.rs | 7 ++++-- 3 files changed, 7 insertions(+), 43 deletions(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index ccbd7df67..2ad8081d6 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -13,7 +13,8 @@ use xmtp_mls::{ }; static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 12] = [5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800]; +pub const IDENTITY_SAMPLES: [usize; 2] = + [5, 10 /*20, 40, 80, 100, 200, 400, 500, 600, 700, 800*/]; pub const MAX_IDENTITIES: usize = 5_000; pub const SAMPLE_SIZE: usize = 10; diff --git a/xmtp_mls/src/builder.rs b/xmtp_mls/src/builder.rs index bf65bcb1a..61b0cd0f5 100644 --- a/xmtp_mls/src/builder.rs +++ b/xmtp_mls/src/builder.rs @@ -138,12 +138,6 @@ mod tests { Client, InboxOwner, }; - async fn get_local_grpc_client() -> GrpcClient { - GrpcClient::create("http://localhost:5556".to_string(), false) - .await - .unwrap() - } - async fn register_client(client: &Client, owner: &impl InboxOwner) { let mut signature_request = client.context.signature_request().unwrap(); let signature_text = signature_request.signature_text(); @@ -158,40 +152,6 @@ mod tests { client.register_identity(signature_request).await.unwrap(); } - impl ClientBuilder { - pub async fn local_grpc(self) -> Self { - self.api_client(get_local_grpc_client().await) - } - - fn temp_store(self) -> Self { - let tmpdb = tmp_path(); - self.store( - EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(), - ) - } - - pub async fn new_test_client(owner: &impl InboxOwner) -> Client { - let nonce = 1; - let inbox_id = generate_inbox_id(&owner.get_address(), &nonce); - let client = Self::new(IdentityStrategy::CreateIfNotFound( - inbox_id, - owner.get_address(), - nonce, - None, - )) - .temp_store() - .local_grpc() - .await - .build() - .await - .unwrap(); - - register_client(&client, owner).await; - - client - } - } - #[tokio::test] async fn builder_test() { let wallet = generate_local_wallet(); diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 716625715..9e472f4de 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -5,7 +5,7 @@ use rand::{ Rng, }; use xmtp_api_grpc::grpc_api_helper::Client as GrpcClient; -use xmtp_id::associations::RecoverableEcdsaSignature; +use xmtp_id::associations::{generate_inbox_id, RecoverableEcdsaSignature}; use crate::{ builder::ClientBuilder, @@ -53,15 +53,18 @@ impl ClientBuilder { pub fn temp_store(self) -> Self { let tmpdb = tmp_path(); - log::debug!("database path {}", tmpdb); self.store( EncryptedMessageStore::new_unencrypted(StorageOption::Persistent(tmpdb)).unwrap(), ) } pub async fn new_test_client(owner: &impl InboxOwner) -> Client { + let nonce = 1; + let inbox_id = generate_inbox_id(&owner.get_address(), &nonce); let client = Self::new(IdentityStrategy::CreateIfNotFound( + inbox_id, owner.get_address(), + nonce, None, )) .temp_store() From 154636b16aa98064cd33f7d9a9bff9169328dade Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 10:36:26 -0400 Subject: [PATCH 18/41] restore docker compose gRPC limit --- bindings_ffi/Cargo.lock | 128 ++++++++++++++++++++++++---------- dev/docker/docker-compose.yml | 4 +- 2 files changed, 94 insertions(+), 38 deletions(-) diff --git a/bindings_ffi/Cargo.lock b/bindings_ffi/Cargo.lock index dd41b5e8e..bd4f01613 100644 --- a/bindings_ffi/Cargo.lock +++ b/bindings_ffi/Cargo.lock @@ -661,7 +661,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -670,7 +670,7 @@ version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.48", @@ -912,6 +912,41 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.48", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.48", +] + [[package]] name = "data-encoding" version = "2.5.0" @@ -961,9 +996,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62c6fcf842f17f8c78ecf7c81d75c5ce84436b41ee07e03f490fbb5f5a8731d8" +checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" dependencies = [ "diesel_derives", "libsqlite3-sys", @@ -973,11 +1008,12 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8337737574f55a468005a83499da720f20c65586241ffea339db9ecdfd2b44" +checksum = "0d6fdd83d5947068817016e939596d246e5367279453f2a3433287894f2f2996" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", "syn 2.0.48", @@ -985,9 +1021,9 @@ dependencies = [ [[package]] name = "diesel_migrations" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" dependencies = [ "diesel", "migrations_internals", @@ -996,9 +1032,9 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ "syn 2.0.48", ] @@ -1063,6 +1099,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "dsl_auto_type" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab32c18ea6760d951659768a3e35ea72fc1ba0916d665a88dfe048b2a41e543f" +dependencies = [ + "darling", + "either", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1944,6 +1994,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.4" @@ -2223,6 +2279,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -2484,9 +2546,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "openssl-sys", @@ -2540,19 +2602,19 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "migrations_internals" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.7.8", + "toml 0.8.8", ] [[package]] name = "migrations_macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" dependencies = [ "migrations_internals", "proc-macro2", @@ -3024,7 +3086,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbb7b706f2afc610f3853550cdbbf6372fd324824a087806bd4480ea4996e24" dependencies = [ - "heck", + "heck 0.4.1", "itertools 0.10.5", "prost 0.11.9", "prost-types 0.11.9", @@ -3036,7 +3098,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" dependencies = [ - "heck", + "heck 0.4.1", "itertools 0.11.0", "prost 0.12.3", "prost-types 0.12.3", @@ -3447,7 +3509,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "119533552c9a7ffacc21e099c24a0ac8bb19c2a2a3f363de84cd9b844feab270" dependencies = [ "bytes", - "heck", + "heck 0.4.1", "itertools 0.10.5", "lazy_static", "log", @@ -3469,7 +3531,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c55e02e35260070b6f716a2423c2ff1c3bb1642ddca6f99e1f26d06268a0e2d2" dependencies = [ "bytes", - "heck", + "heck 0.4.1", "itertools 0.11.0", "log", "multimap", @@ -4433,6 +4495,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.25.0" @@ -4448,7 +4516,7 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", @@ -4819,18 +4887,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.8" @@ -4859,8 +4915,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.1.0", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] @@ -5122,7 +5176,7 @@ dependencies = [ "fs-err", "glob", "goblin", - "heck", + "heck 0.4.1", "once_cell", "paste", "serde", diff --git a/dev/docker/docker-compose.yml b/dev/docker/docker-compose.yml index f346a0812..01dda9772 100644 --- a/dev/docker/docker-compose.yml +++ b/dev/docker/docker-compose.yml @@ -11,7 +11,6 @@ services: - --mls-store.db-connection-string=postgres://postgres:xmtp@mlsdb:5432/postgres?sslmode=disable - --mls-validation.grpc-address=validation:50051 - --api.enable-mls - - --api.max-msg-size=104857600 - --wait-for-db=30s # Disable authn until we have reliable support for generating auth tokens # - --api.authn.enable @@ -20,16 +19,19 @@ services: - 5556:5556 depends_on: - db + validation: image: ghcr.io/xmtp/mls-validation-service:main platform: linux/amd64 build: context: ../.. dockerfile: ./dev/validation_service/local.Dockerfile + db: image: postgres:13 environment: POSTGRES_PASSWORD: xmtp + mlsdb: image: postgres:13 environment: From a9efd904c0c70c9ab9cd12faa2a715942aa89a95 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 10:42:38 -0400 Subject: [PATCH 19/41] misc fixes - set `MAX_GROUP_SIZE` to 300 - use all identity samples for benchmarks - reference github issue in TODO comment --- xmtp_mls/benches/group_limit.rs | 3 +-- xmtp_mls/src/api/mls.rs | 2 -- xmtp_mls/src/configuration.rs | 2 +- xmtp_mls/src/groups/sync.rs | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index 2ad8081d6..ccbd7df67 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -13,8 +13,7 @@ use xmtp_mls::{ }; static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 2] = - [5, 10 /*20, 40, 80, 100, 200, 400, 500, 600, 700, 800*/]; +pub const IDENTITY_SAMPLES: [usize; 12] = [5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800]; pub const MAX_IDENTITIES: usize = 5_000; pub const SAMPLE_SIZE: usize = 10; diff --git a/xmtp_mls/src/api/mls.rs b/xmtp_mls/src/api/mls.rs index 5344a8501..75188777c 100644 --- a/xmtp_mls/src/api/mls.rs +++ b/xmtp_mls/src/api/mls.rs @@ -246,8 +246,6 @@ where &self, messages: &[WelcomeMessageInput], ) -> Result<(), ApiError> { - log::debug!("Sending {} welcome messages", messages.len()); - retry_async!( self.retry_strategy, (async { diff --git a/xmtp_mls/src/configuration.rs b/xmtp_mls/src/configuration.rs index f9f8f27d8..0d5b7bd79 100644 --- a/xmtp_mls/src/configuration.rs +++ b/xmtp_mls/src/configuration.rs @@ -16,7 +16,7 @@ const NANOSECONDS_IN_HOUR: i64 = 3_600_000_000_000; pub const UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NANOSECONDS_IN_HOUR / 2; // 30 min -pub const MAX_GROUP_SIZE: u16 = 40_000; +pub const MAX_GROUP_SIZE: u16 = 300; pub const DELIMITER: char = '\x01'; diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index d132a5774..9080a1035 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -69,7 +69,7 @@ use xmtp_proto::xmtp::mls::{ }, }; -// TODO: Change `send_welcomes` to be constant-sized +// TODO: Change `send_welcomes` to be constant-sized (#812) /// the max size gRPC will accept pub const MAX_CHUNK: usize = 50 * 1024 * 1024; From 4a1b91bea26bdfc7ad78295bdbf8a5e7d79ae2e1 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 14:45:17 -0400 Subject: [PATCH 20/41] devnet --- xmtp_mls/benches/group_limit.rs | 16 +++++++-- xmtp_mls/src/utils/bench.rs | 57 +++++++++++++++++++++------------ xmtp_mls/src/utils/test.rs | 35 +++++++++++++++++++- 3 files changed, 84 insertions(+), 24 deletions(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index ccbd7df67..f8e7dc850 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -37,10 +37,20 @@ fn setup() -> (Arc, Vec, Runtime) { .unwrap(); let (client, identities) = runtime.block_on(async { - let identities: Vec = create_identities_if_dont_exist(MAX_IDENTITIES).await; - let wallet = LocalWallet::new(&mut rng()); - let client = Arc::new(ClientBuilder::new_test_client(&wallet).await); + + // use dev network if `DEV_GRPC` is set + let dev = std::env::var("DEV_GRPC"); + let is_dev_network = matches!(dev, Ok(d) if d == "true" || d == "1"); + let client = if is_dev_network { + println!("Using Dev GRPC"); + Arc::new(ClientBuilder::new_dev_client(&wallet).await) + } else { + Arc::new(ClientBuilder::new_test_client(&wallet).await) + }; + + let identities: Vec = + create_identities_if_dont_exist(MAX_IDENTITIES, &client, is_dev_network).await; (client, identities) }); diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index 33097caf8..d9836813d 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Serialize}; use thiserror::Error; use xmtp_cryptography::utils::rng; +use super::test::TestClient; + #[derive(Debug, Error)] pub enum BenchError { #[error(transparent)] @@ -16,21 +18,28 @@ pub enum BenchError { Io(#[from] std::io::Error), } -pub fn file_path() -> String { - format!("{}/identities.generated", env!("CARGO_MANIFEST_DIR")) +pub fn file_path(is_dev_network: bool) -> String { + if is_dev_network { + format!("{}/dev-identities.generated", env!("CARGO_MANIFEST_DIR")) + } else { + format!("{}/identities.generated", env!("CARGO_MANIFEST_DIR")) + } } -pub async fn write_identities(num_groups: usize) -> Vec { - let identities: Vec = create_identities(num_groups).await.into_iter().collect(); +pub async fn write_identities(num_groups: usize, is_dev_network: bool) -> Vec { + let identities: Vec = create_identities(num_groups, is_dev_network) + .await + .into_iter() + .collect(); let json = serde_json::to_string(&identities).unwrap(); - std::fs::write(file_path(), json).unwrap(); + std::fs::write(file_path(is_dev_network), json).unwrap(); identities } -pub fn load_identities() -> Result, BenchError> { - let identities = std::fs::read(file_path())?; +pub fn load_identities(is_dev_network: bool) -> Result, BenchError> { + let identities = std::fs::read(file_path(is_dev_network))?; Ok(serde_json::from_slice(identities.as_slice())?) } @@ -46,14 +55,17 @@ impl Identity { } } -async fn create_identity() -> Identity { +async fn create_identity(is_dev_network: bool) -> Identity { let wallet = LocalWallet::new(&mut rng()); - let client = ClientBuilder::new_test_client(&wallet).await; - + let client = if is_dev_network { + ClientBuilder::new_dev_client(&wallet).await + } else { + ClientBuilder::new_test_client(&wallet).await + }; Identity::new(client.inbox_id(), format!("0x{:x}", wallet.address())) } -async fn create_identities(n: usize) -> Vec { +async fn create_identities(n: usize, is_dev_network: bool) -> Vec { let mut identities = Vec::with_capacity(n); let style = @@ -66,7 +78,7 @@ async fn create_identities(n: usize) -> Vec { for _ in 0..n { let bar_pointer = bar.clone(); handles.push(set.spawn(async move { - let identity = create_identity().await; + let identity = create_identity(is_dev_network).await; bar_pointer.inc(1); identity })); @@ -89,14 +101,19 @@ async fn create_identities(n: usize) -> Vec { /// Create identities if they don't already exist. /// creates specified `identities` on the -/// local gRPC for [`ClientBuilder::new_test_client`] and saves them to the file, +/// local gRPC local/dev and saves them to the file, /// `identities.generated`. Uses this file for subsequent runs. -pub async fn create_identities_if_dont_exist(identities: usize) -> Vec { - match load_identities() { +pub async fn create_identities_if_dont_exist( + identities: usize, + client: &TestClient, + is_dev_network: bool, +) -> Vec { + match load_identities(is_dev_network) { Ok(identities) => { - log::info!("Found generated identities, checking for existence on backend..."); - let wallet = LocalWallet::new(&mut rng()); - let client = ClientBuilder::new_test_client(&wallet).await; + log::info!( + "Found generated identities at {}, checking for existence on backend...", + file_path(is_dev_network) + ); if client.is_registered(&identities[0].address).await { return identities; } @@ -113,7 +130,7 @@ pub async fn create_identities_if_dont_exist(identities: usize) -> Vec ); println!("Writing {identities} identities... (this will take a while...)"); - let addresses = write_identities(identities).await; - println!("Wrote {identities} to {}", file_path()); + let addresses = write_identities(identities, is_dev_network).await; + println!("Wrote {identities} to {}", file_path(is_dev_network)); addresses } diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 9e472f4de..2bb36dff3 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -46,11 +46,21 @@ pub async fn get_local_grpc_client() -> GrpcClient { .unwrap() } +pub async fn get_dev_grpc_client() -> GrpcClient { + GrpcClient::create("https://grpc.dev.xmtp.network:443".into(), true) + .await + .unwrap() +} + impl ClientBuilder { pub async fn local_grpc(self) -> Self { self.api_client(get_local_grpc_client().await) } + pub async fn dev_grpc(self) -> Self { + self.api_client(get_dev_grpc_client().await) + } + pub fn temp_store(self) -> Self { let tmpdb = tmp_path(); self.store( @@ -78,6 +88,27 @@ impl ClientBuilder { client } + + pub async fn new_dev_client(owner: &impl InboxOwner) -> Client { + let nonce = 1; + let inbox_id = generate_inbox_id(&owner.get_address(), &nonce); + let client = Self::new(IdentityStrategy::CreateIfNotFound( + inbox_id, + owner.get_address(), + nonce, + None, + )) + .temp_store() + .dev_grpc() + .await + .build() + .await + .unwrap(); + + register_client(&client, owner).await; + + client + } } impl Client { @@ -87,7 +118,9 @@ impl Client { .get_inbox_ids(vec![address.clone()]) .await .unwrap(); - ids.contains_key(address) + let s = ids.contains_key(address); + println!("Is registered? {}", s); + s } } From 915484fe542caed396b4236cece50228277de69d Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 14:45:33 -0400 Subject: [PATCH 21/41] remove println --- xmtp_mls/src/utils/test.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 2bb36dff3..7c5fdad21 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -119,7 +119,6 @@ impl Client { .await .unwrap(); let s = ids.contains_key(address); - println!("Is registered? {}", s); s } } From 80c2f9b2fef1e5de54dd972cbedc8745662c381c Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 14:50:43 -0400 Subject: [PATCH 22/41] fix --- xmtp_mls/src/utils/test.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/xmtp_mls/src/utils/test.rs b/xmtp_mls/src/utils/test.rs index 7c5fdad21..cffc96d67 100644 --- a/xmtp_mls/src/utils/test.rs +++ b/xmtp_mls/src/utils/test.rs @@ -118,8 +118,7 @@ impl Client { .get_inbox_ids(vec![address.clone()]) .await .unwrap(); - let s = ids.contains_key(address); - s + ids.contains_key(address) } } From cc9c3422f3aff9c2a0c0957bc8ee8bf753139d68 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 15:33:15 -0400 Subject: [PATCH 23/41] attempt to fix batch query --- xmtp_mls/src/configuration.rs | 2 +- xmtp_mls/src/groups/members.rs | 2 +- .../encrypted_store/association_state.rs | 33 +++++++++++-------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/xmtp_mls/src/configuration.rs b/xmtp_mls/src/configuration.rs index 0d5b7bd79..624992d05 100644 --- a/xmtp_mls/src/configuration.rs +++ b/xmtp_mls/src/configuration.rs @@ -16,7 +16,7 @@ const NANOSECONDS_IN_HOUR: i64 = 3_600_000_000_000; pub const UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NANOSECONDS_IN_HOUR / 2; // 30 min -pub const MAX_GROUP_SIZE: u16 = 300; +pub const MAX_GROUP_SIZE: u16 = 20_000; pub const DELIMITER: char = '\x01'; diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index e6755b39f..3d197f61b 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -40,7 +40,7 @@ impl MlsGroup { .members .into_iter() .map(|(inbox_id, sequence_id)| (inbox_id, sequence_id as i64)) - .collect(); + .collect::>(); let conn = provider.conn_ref(); let association_state_map = StoredAssociationState::batch_read_from_cache(conn, &requests)?; diff --git a/xmtp_mls/src/storage/encrypted_store/association_state.rs b/xmtp_mls/src/storage/encrypted_store/association_state.rs index 32c31a9e4..9f7a62131 100644 --- a/xmtp_mls/src/storage/encrypted_store/association_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/association_state.rs @@ -71,20 +71,27 @@ impl StoredAssociationState { pub fn batch_read_from_cache( conn: &DbConnection, - identifiers: &Vec<(InboxId, i64)>, + identifiers: &[(InboxId, i64)], ) -> Result, StorageError> { // If no identifier provided, return empty hash map if identifiers.is_empty() { return Ok(vec![]); } - let mut query = dsl::association_state.into_boxed(); - for (inbox_id, sequence_id) in identifiers { - query = query.or_filter( + + let (inbox_ids, sequence_ids): (Vec, Vec) = + identifiers.iter().cloned().unzip(); + + let query = dsl::association_state + .select((dsl::inbox_id, dsl::sequence_id, dsl::state)) + .filter( dsl::inbox_id - .eq(inbox_id) - .and(dsl::sequence_id.eq(sequence_id)), + .eq_any(inbox_ids) + .and(dsl::sequence_id.eq_any(sequence_ids)), ); - } + + let dbg = diesel::debug_query::(&query).to_string(); + println!("QUERY: {}", dbg); + let association_states = conn.raw_query(|query_conn| query.load::(query_conn))?; @@ -125,17 +132,15 @@ mod tests { ) .unwrap(); - let first_association_state = StoredAssociationState::batch_read_from_cache( - conn, - &vec![(inbox_id.to_string(), 1)], - ) - .unwrap(); + let first_association_state = + StoredAssociationState::batch_read_from_cache(conn, &[(inbox_id.to_string(), 1)]) + .unwrap(); assert_eq!(first_association_state.len(), 1); assert_eq!(first_association_state[0].inbox_id(), &inbox_id); let both_association_states = StoredAssociationState::batch_read_from_cache( conn, - &vec![(inbox_id.to_string(), 1), (inbox_id_2.to_string(), 2)], + &[(inbox_id.to_string(), 1), (inbox_id_2.to_string(), 2)], ) .unwrap(); @@ -144,7 +149,7 @@ mod tests { let no_results = StoredAssociationState::batch_read_from_cache( conn, // Mismatched inbox_id and sequence_id - &vec![(inbox_id.to_string(), 2)], + &[(inbox_id.to_string(), 2)], ) .unwrap(); assert_eq!(no_results.len(), 0); From 2d199e82f950f779cde8bf41eb27c8050423ad99 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 16:03:34 -0400 Subject: [PATCH 24/41] benchmark adding one member to large group --- xmtp_mls/benches/group_limit.rs | 50 +++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index f8e7dc850..ef0952246 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -130,8 +130,6 @@ fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { benchmark_group.finish(); } -#[allow(dead_code)] -// requires https://github.com/xmtp/libxmtp/issues/810 to work fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { init_logging(); let mut benchmark_group = c.benchmark_group("add_to_100_member_group_by_inbox_id"); @@ -184,7 +182,6 @@ fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { benchmark_group.finish(); } -#[allow(dead_code)] // requires https://github.com/xmtp/libxmtp/issues/810 to work fn add_to_100_member_group_by_address(c: &mut Criterion) { init_logging(); @@ -222,7 +219,6 @@ fn add_to_100_member_group_by_address(c: &mut Criterion) { }) }, |(client, group)| async move { - println!("Adding {} to group", ids.len()); group.add_members(&client, ids.clone()).await.unwrap(); }, BatchSize::SmallInput, @@ -318,8 +314,52 @@ fn remove_half_members_from_group(c: &mut Criterion) { benchmark_group.finish(); } +// requires https://github.com/xmtp/libxmtp/issues/810 to work +fn add_1_member_to_large_group(c: &mut Criterion) { + init_logging(); + let mut benchmark_group = c.benchmark_group("add_to_100_member_group_by_address"); + benchmark_group.sample_size(SAMPLE_SIZE); + + let (client, identities, runtime) = setup(); + let inbox_ids: Vec = identities.into_iter().map(|i| i.inbox_id).collect(); + + let mut map = HashMap::>::new(); + + for size in IDENTITY_SAMPLES { + map.insert(size, inbox_ids.iter().take(size).cloned().collect()); + } + + for size in IDENTITY_SAMPLES.iter() { + benchmark_group.throughput(Throughput::Elements(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let ids = map.get(&size).unwrap(); + b.to_async(&runtime).iter_batched( + || { + bench_async_setup(|| async { + let group = client.create_group(None).unwrap(); + group.add_members(&client, ids.clone()).await.unwrap(); + + (client.clone(), group) + }) + }, + |(client, group)| { + let single_member = inbox_ids.last().unwrap().clone(); + async move { + group + .add_members(&client, vec![single_member]) + .await + .unwrap(); + } + }, + BatchSize::SmallInput, + ); + }); + } + benchmark_group.finish(); +} + criterion_group!( name = group_limit; config = Criterion::default().sample_size(10); - targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group); + targets = /*add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group,*/ add_to_100_member_group_by_inbox_id, add_to_100_member_group_by_inbox_id, add_1_member_to_large_group); criterion_main!(group_limit); From a9186551e218106587b74aa2ee58db77c9510f91 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 16:14:07 -0400 Subject: [PATCH 25/41] prefer not to clone --- xmtp_mls/src/groups/members.rs | 2 +- xmtp_mls/src/storage/encrypted_store/association_state.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/xmtp_mls/src/groups/members.rs b/xmtp_mls/src/groups/members.rs index 3d197f61b..4e9b073db 100644 --- a/xmtp_mls/src/groups/members.rs +++ b/xmtp_mls/src/groups/members.rs @@ -43,7 +43,7 @@ impl MlsGroup { .collect::>(); let conn = provider.conn_ref(); - let association_state_map = StoredAssociationState::batch_read_from_cache(conn, &requests)?; + let association_state_map = StoredAssociationState::batch_read_from_cache(conn, requests)?; let mutable_metadata = self.mutable_metadata()?; // TODO: Figure out what to do with missing members from the local DB. Do we go to the network? Load from identity updates? // Right now I am just omitting them diff --git a/xmtp_mls/src/storage/encrypted_store/association_state.rs b/xmtp_mls/src/storage/encrypted_store/association_state.rs index 9f7a62131..8ea47cbc6 100644 --- a/xmtp_mls/src/storage/encrypted_store/association_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/association_state.rs @@ -71,15 +71,14 @@ impl StoredAssociationState { pub fn batch_read_from_cache( conn: &DbConnection, - identifiers: &[(InboxId, i64)], + identifiers: Vec<(InboxId, i64)>, ) -> Result, StorageError> { // If no identifier provided, return empty hash map if identifiers.is_empty() { return Ok(vec![]); } - let (inbox_ids, sequence_ids): (Vec, Vec) = - identifiers.iter().cloned().unzip(); + let (inbox_ids, sequence_ids): (Vec, Vec) = identifiers.into_iter().unzip(); let query = dsl::association_state .select((dsl::inbox_id, dsl::sequence_id, dsl::state)) @@ -89,9 +88,6 @@ impl StoredAssociationState { .and(dsl::sequence_id.eq_any(sequence_ids)), ); - let dbg = diesel::debug_query::(&query).to_string(); - println!("QUERY: {}", dbg); - let association_states = conn.raw_query(|query_conn| query.load::(query_conn))?; From a0f614261b51cb1cfc9da19da7399d7a29d4a2cd Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 16:14:49 -0400 Subject: [PATCH 26/41] uncomment --- xmtp_mls/benches/group_limit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index ef0952246..bb857fdef 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -361,5 +361,5 @@ fn add_1_member_to_large_group(c: &mut Criterion) { criterion_group!( name = group_limit; config = Criterion::default().sample_size(10); - targets = /*add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group,*/ add_to_100_member_group_by_inbox_id, add_to_100_member_group_by_inbox_id, add_1_member_to_large_group); + targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, add_to_100_member_group_by_inbox_id, add_1_member_to_large_group); criterion_main!(group_limit); From 318713fcf454b9be11cb1214601eff5f3917e013 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Tue, 4 Jun 2024 16:27:02 -0400 Subject: [PATCH 27/41] fix test --- xmtp_mls/benches/group_limit.rs | 57 ++----------------- .../encrypted_store/association_state.rs | 12 ++-- 2 files changed, 13 insertions(+), 56 deletions(-) diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index bb857fdef..c04eeb8e2 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -13,7 +13,9 @@ use xmtp_mls::{ }; static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 12] = [5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800]; +pub const IDENTITY_SAMPLES: [usize; 15] = [ + 5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800, 1600, 3200, 4_000, +]; pub const MAX_IDENTITIES: usize = 5_000; pub const SAMPLE_SIZE: usize = 10; @@ -182,52 +184,6 @@ fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { benchmark_group.finish(); } -// requires https://github.com/xmtp/libxmtp/issues/810 to work -fn add_to_100_member_group_by_address(c: &mut Criterion) { - init_logging(); - let mut benchmark_group = c.benchmark_group("add_to_100_member_group_by_address"); - benchmark_group.sample_size(SAMPLE_SIZE); - - let (client, identities, runtime) = setup(); - let addresses: Vec = identities.into_iter().map(|i| i.address).collect(); - - let mut map = HashMap::>::new(); - - for size in IDENTITY_SAMPLES { - map.insert(size, addresses.iter().take(size).cloned().collect()); - } - - for size in IDENTITY_SAMPLES.iter() { - benchmark_group.throughput(Throughput::Elements(*size as u64)); - benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { - let ids = map.get(&size).unwrap(); - b.to_async(&runtime).iter_batched( - || { - bench_async_setup(|| async { - let group = client.create_group(None).unwrap(); - group - .add_members( - &client, - // it is OK to take from the back for now because we aren't getting - // near MAX_IDENTITIES - addresses.iter().rev().take(100).cloned().collect(), - ) - .await - .unwrap(); - - (client.clone(), group) - }) - }, - |(client, group)| async move { - group.add_members(&client, ids.clone()).await.unwrap(); - }, - BatchSize::SmallInput, - ); - }); - } - benchmark_group.finish(); -} - fn remove_all_members_from_group(c: &mut Criterion) { init_logging(); let mut benchmark_group = c.benchmark_group("remove_all_members_from_group"); @@ -314,10 +270,9 @@ fn remove_half_members_from_group(c: &mut Criterion) { benchmark_group.finish(); } -// requires https://github.com/xmtp/libxmtp/issues/810 to work -fn add_1_member_to_large_group(c: &mut Criterion) { +fn add_1_member_to_group(c: &mut Criterion) { init_logging(); - let mut benchmark_group = c.benchmark_group("add_to_100_member_group_by_address"); + let mut benchmark_group = c.benchmark_group("add_1_member_to_group"); benchmark_group.sample_size(SAMPLE_SIZE); let (client, identities, runtime) = setup(); @@ -361,5 +316,5 @@ fn add_1_member_to_large_group(c: &mut Criterion) { criterion_group!( name = group_limit; config = Criterion::default().sample_size(10); - targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, add_to_100_member_group_by_inbox_id, add_1_member_to_large_group); + targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, add_1_member_to_group); criterion_main!(group_limit); diff --git a/xmtp_mls/src/storage/encrypted_store/association_state.rs b/xmtp_mls/src/storage/encrypted_store/association_state.rs index 8ea47cbc6..9ff51cca1 100644 --- a/xmtp_mls/src/storage/encrypted_store/association_state.rs +++ b/xmtp_mls/src/storage/encrypted_store/association_state.rs @@ -128,15 +128,17 @@ mod tests { ) .unwrap(); - let first_association_state = - StoredAssociationState::batch_read_from_cache(conn, &[(inbox_id.to_string(), 1)]) - .unwrap(); + let first_association_state = StoredAssociationState::batch_read_from_cache( + conn, + vec![(inbox_id.to_string(), 1)], + ) + .unwrap(); assert_eq!(first_association_state.len(), 1); assert_eq!(first_association_state[0].inbox_id(), &inbox_id); let both_association_states = StoredAssociationState::batch_read_from_cache( conn, - &[(inbox_id.to_string(), 1), (inbox_id_2.to_string(), 2)], + vec![(inbox_id.to_string(), 1), (inbox_id_2.to_string(), 2)], ) .unwrap(); @@ -145,7 +147,7 @@ mod tests { let no_results = StoredAssociationState::batch_read_from_cache( conn, // Mismatched inbox_id and sequence_id - &[(inbox_id.to_string(), 2)], + vec![(inbox_id.to_string(), 2)], ) .unwrap(); assert_eq!(no_results.len(), 0); From 643b0d10c06b46130e7f1527a62589d8aa2d5de2 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 5 Jun 2024 12:32:34 -0400 Subject: [PATCH 28/41] add bench for encrypt from welcome --- xmtp_mls/Cargo.toml | 4 ++ xmtp_mls/benches/crypto.rs | 68 +++++++++++++++++++++++++++++++++ xmtp_mls/benches/group_limit.rs | 26 ++++++------- xmtp_mls/src/lib.rs | 2 +- 4 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 xmtp_mls/benches/crypto.rs diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 6dfc6a2bd..5cef83d04 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -79,3 +79,7 @@ criterion = { version = "0.5", features = ["html_reports", "async_tokio"] } name = "group_limit" harness = false +[[bench]] +name = "crypto" +harness = false + diff --git a/xmtp_mls/benches/crypto.rs b/xmtp_mls/benches/crypto.rs new file mode 100644 index 000000000..4c05f575e --- /dev/null +++ b/xmtp_mls/benches/crypto.rs @@ -0,0 +1,68 @@ +use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; +use openmls_rust_crypto::RustCrypto; +use openmls_traits::{crypto::OpenMlsCrypto, random::OpenMlsRand}; +use rand::{rngs::OsRng, RngCore}; +use xmtp_mls::configuration::CIPHERSUITE; +use xmtp_mls::hpke::encrypt_welcome; + +fn bench_encrypt_welcome(c: &mut Criterion) { + let sizes = [ + 16, + 32, + 64, + 128, + 256, + 512, + 1024, + 2048, + 4096, + 8192, + 16384, + 32768, + 65536, + 131_072, + 262_144, + 524_288, + 1_048_576, + 2_097_152, + 4_194_304, + 8_388_608, + 16_777_216, + 33_554_432, + 67_108_864, + 134_217_728, + ]; + + let mut benchmark_group = c.benchmark_group("encrypt_welcome"); + benchmark_group.sample_size(10); + + for size in sizes.iter() { + benchmark_group.throughput(Throughput::Bytes(*size as u64)); + benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + b.iter_batched( + || { + let crypto = RustCrypto::default(); + let ikm = crypto.random_vec(CIPHERSUITE.hash_length()).unwrap(); + let keypair = crypto + .derive_hpke_keypair(CIPHERSUITE.hpke_config(), ikm.as_slice()) + .unwrap(); + + let mut payload = vec![0; size]; + OsRng.fill_bytes(payload.as_mut_slice()); + (payload, keypair.public) + }, + |(payload, key)| encrypt_welcome(&payload, &key), + BatchSize::SmallInput, + ) + }); + } + + benchmark_group.finish(); +} + +criterion_group!( + name = crypto; + config = Criterion::default().sample_size(10); + targets = bench_encrypt_welcome +); +criterion_main!(crypto); diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index c04eeb8e2..75d917a4c 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -13,8 +13,8 @@ use xmtp_mls::{ }; static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 15] = [ - 5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800, 1600, 3200, 4_000, +pub const IDENTITY_SAMPLES: [usize; 14] = [ + 5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800, 1600, 3200, ]; pub const MAX_IDENTITIES: usize = 5_000; pub const SAMPLE_SIZE: usize = 10; @@ -292,19 +292,19 @@ fn add_1_member_to_group(c: &mut Criterion) { || { bench_async_setup(|| async { let group = client.create_group(None).unwrap(); - group.add_members(&client, ids.clone()).await.unwrap(); - - (client.clone(), group) - }) - }, - |(client, group)| { - let single_member = inbox_ids.last().unwrap().clone(); - async move { group - .add_members(&client, vec![single_member]) + .add_members_by_inbox_id(&client, ids.clone()) .await .unwrap(); - } + let member = inbox_ids.last().unwrap().clone(); + (client.clone(), group, member) + }) + }, + |(client, group, member)| async move { + group + .add_members_by_inbox_id(&client, vec![member]) + .await + .unwrap(); }, BatchSize::SmallInput, ); @@ -316,5 +316,5 @@ fn add_1_member_to_group(c: &mut Criterion) { criterion_group!( name = group_limit; config = Criterion::default().sample_size(10); - targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, add_1_member_to_group); + targets = /*add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, */ add_1_member_to_group); criterion_main!(group_limit); diff --git a/xmtp_mls/src/lib.rs b/xmtp_mls/src/lib.rs index e7bded9b7..3c4389089 100644 --- a/xmtp_mls/src/lib.rs +++ b/xmtp_mls/src/lib.rs @@ -5,7 +5,7 @@ pub mod codecs; pub mod configuration; pub mod credential; pub mod groups; -mod hpke; +pub mod hpke; pub mod identity; mod identity_updates; pub mod owner; From cc1317f534ae5ac950585cbf9a4037404bd7b987 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 6 Jun 2024 11:54:47 -0400 Subject: [PATCH 29/41] instrumentation --- .gitignore | 2 + Cargo.lock | 13 +++++ Cargo.toml | 1 + bindings_ffi/Cargo.lock | 1 + xmtp_mls/Cargo.toml | 10 ++-- xmtp_mls/benches/group_limit.rs | 49 +++++++++++++------ xmtp_mls/src/api/identity.rs | 1 + xmtp_mls/src/api/mls.rs | 1 + xmtp_mls/src/groups/mod.rs | 3 ++ xmtp_mls/src/groups/sync.rs | 14 +++++- xmtp_mls/src/groups/validated_commit.rs | 1 + xmtp_mls/src/hpke.rs | 1 + xmtp_mls/src/identity_updates.rs | 1 + xmtp_mls/src/retry.rs | 9 ++-- .../storage/encrypted_store/group_intent.rs | 3 ++ .../encrypted_store/identity_update.rs | 1 + xmtp_mls/src/utils/bench.rs | 28 +++++++++++ 17 files changed, 116 insertions(+), 23 deletions(-) diff --git a/.gitignore b/.gitignore index 6606a5609..9c1d23a64 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,5 @@ ecies_bindings_wasm/ # Locally Generated Stuff *.generated **/test_output.jsonl +**/tracing.folded +**/tracing-flamegraph.svg diff --git a/Cargo.lock b/Cargo.lock index 31d6a1c12..cc94bc514 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5585,6 +5585,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-flame" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bae117ee14789185e129aaee5d93750abe67fdc5a9a62650452bfe4e122a3a9" +dependencies = [ + "lazy_static", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tracing-futures" version = "0.2.5" @@ -6473,6 +6484,7 @@ dependencies = [ "log", "mockall", "mockito", + "once_cell", "openmls", "openmls_basic_credential", "openmls_rust_crypto", @@ -6491,6 +6503,7 @@ dependencies = [ "tokio", "toml", "tracing", + "tracing-flame", "tracing-log", "tracing-subscriber", "tracing-test", diff --git a/Cargo.toml b/Cargo.toml index 41d96d5c9..11e80f41e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,7 @@ tls_codec = "0.4.0" tokio = { version = "1.35.1", features = ["macros"] } tonic = "^0.11" tracing = "0.1" +tracing-subscriber = "0.3" url = "2.5.0" # Internal Crate Dependencies diff --git a/bindings_ffi/Cargo.lock b/bindings_ffi/Cargo.lock index bd4f01613..2a011dc9f 100644 --- a/bindings_ffi/Cargo.lock +++ b/bindings_ffi/Cargo.lock @@ -5878,6 +5878,7 @@ dependencies = [ "tls_codec 0.4.0", "tokio", "toml 0.8.8", + "tracing", "xmtp_cryptography", "xmtp_id", "xmtp_proto", diff --git a/xmtp_mls/Cargo.toml b/xmtp_mls/Cargo.toml index 5cef83d04..7901f6cc4 100644 --- a/xmtp_mls/Cargo.toml +++ b/xmtp_mls/Cargo.toml @@ -13,7 +13,7 @@ default = ["native"] grpc = ["xmtp_proto/grpc"] native = ["libsqlite3-sys/bundled-sqlcipher-vendored-openssl"] test-utils = ["xmtp_api_grpc"] -bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow" ] +bench = ["test-utils", "indicatif", "tracing-subscriber", "anyhow", "tracing-flame", "once_cell"] [dependencies] aes = "0.8.4" @@ -33,6 +33,7 @@ futures.workspace = true hex.workspace = true libsqlite3-sys = { version = "0.28.0", optional = true } log.workspace = true +tracing.workspace = true openmls = { workspace = true, features = ["test-utils"] } openmls_basic_credential = { workspace = true } openmls_rust_crypto = { workspace = true } @@ -54,11 +55,13 @@ xmtp_id = { path = "../xmtp_id" } xmtp_proto = { workspace = true, features = ["proto_full", "convert"] } xmtp_v2 = { path = "../xmtp_v2" } -# Test Utils +# Test/Bench Utils xmtp_api_grpc = { path = "../xmtp_api_grpc", optional = true } -tracing-subscriber = { version = "0.3", optional = true } +tracing-subscriber = { workspace = true, optional = true } indicatif = { version = "0.17", optional = true } anyhow = { workspace = true, optional = true } +tracing-flame = { version = "0.2", optional = true } +once_cell = { version = "1.19", optional = true } [dev-dependencies] ctor.workspace = true @@ -66,7 +69,6 @@ flume = "0.11" mockall = "0.11.4" mockito = "1.4.0" tempfile = "3.5.0" -tracing = "0.1" tracing-log = "0.2.0" tracing-test = "0.2.4" xmtp_api_grpc = { path = "../xmtp_api_grpc" } diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index 75d917a4c..422b7e5a7 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -1,37 +1,50 @@ +//! Benchmarks for group limit +//! using `RUST_LOG=trace` will additionally output a `tracing.folded` file, which +//! may be used to generate a flamegraph of execution from tracing logs. use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; use ethers::signers::LocalWallet; +use once_cell::sync::OnceCell; use std::{collections::HashMap, sync::Arc, sync::Once}; use tokio::runtime::{Builder, Handle, Runtime}; -use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry}; +use tracing::{span, Instrument, Level}; +use tracing_flame::{FlameLayer, FlushGuard}; +use tracing_subscriber::{ + layer::{Layer, SubscriberExt}, + util::SubscriberInitExt, + EnvFilter, +}; use xmtp_cryptography::utils::rng; use xmtp_mls::{ builder::ClientBuilder, utils::{ - bench::{create_identities_if_dont_exist, Identity}, + bench::{create_identities_if_dont_exist, BenchFilter, Identity}, test::TestClient, }, }; static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 14] = [ - 5, 10, 20, 40, 80, 100, 200, 400, 500, 600, 700, 800, 1600, 3200, -]; +pub const IDENTITY_SAMPLES: [usize; 1] = [10 /*20, 40, 80, 100, 200, 400, 800*/]; pub const MAX_IDENTITIES: usize = 5_000; pub const SAMPLE_SIZE: usize = 10; +static LOGGER: OnceCell>> = OnceCell::new(); + pub(crate) fn init_logging() { INIT.call_once(|| { - let fmt = fmt::layer().compact(); - Registry::default() + let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded").unwrap(); + let flame_layer = flame_layer.with_threads_collapsed(true); + + tracing_subscriber::registry() .with(EnvFilter::from_default_env()) - .with(fmt) - .init() + .with(flame_layer.with_filter(BenchFilter)) + .init(); + + LOGGER.set(guard).unwrap(); }) } fn setup() -> (Arc, Vec, Runtime) { let runtime = Builder::new_multi_thread() - .worker_threads(4) .enable_time() .enable_io() .thread_name("xmtp-bencher") @@ -288,6 +301,7 @@ fn add_1_member_to_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); + let span = span!(Level::TRACE, "bench", size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -300,11 +314,16 @@ fn add_1_member_to_group(c: &mut Criterion) { (client.clone(), group, member) }) }, - |(client, group, member)| async move { - group - .add_members_by_inbox_id(&client, vec![member]) - .await - .unwrap(); + |(client, group, member)| { + let span = span.clone(); + async move { + group + .add_members_by_inbox_id(&client, vec![member]) + .in_current_span() + .await + .unwrap(); + } + .instrument(span) }, BatchSize::SmallInput, ); diff --git a/xmtp_mls/src/api/identity.rs b/xmtp_mls/src/api/identity.rs index ae0dc6c38..14c098740 100644 --- a/xmtp_mls/src/api/identity.rs +++ b/xmtp_mls/src/api/identity.rs @@ -78,6 +78,7 @@ where Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn get_identity_updates_v2( &self, filters: Vec, diff --git a/xmtp_mls/src/api/mls.rs b/xmtp_mls/src/api/mls.rs index 75188777c..877afe16f 100644 --- a/xmtp_mls/src/api/mls.rs +++ b/xmtp_mls/src/api/mls.rs @@ -69,6 +69,7 @@ impl ApiClientWrapper where ApiClient: XmtpApi, { + #[tracing::instrument(level = "trace", skip_all)] pub async fn query_group_messages( &self, group_id: Vec, diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 3aad306b4..70c8a4510 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -182,6 +182,7 @@ impl RetryableError for GroupError { } } +#[derive(Debug)] pub struct MlsGroup { pub group_id: Vec, pub created_at_ns: i64, @@ -217,6 +218,7 @@ impl MlsGroup { } // Load the stored MLS group from the OpenMLS provider's keystore + #[tracing::instrument(level = "trace", skip_all)] fn load_mls_group(&self, provider: impl OpenMlsProvider) -> Result { let mls_group = OpenMlsGroup::load(provider.storage(), &GroupId::from_slice(&self.group_id)) @@ -495,6 +497,7 @@ impl MlsGroup { .await } + #[tracing::instrument(level = "trace", skip_all)] pub async fn add_members_by_inbox_id( &self, client: &Client, diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index 9080a1035..eec21f003 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -86,6 +86,7 @@ impl MlsGroup { self.sync_with_conn(conn, client).await } + #[tracing::instrument(level = "trace", skip(client, conn))] pub(super) async fn sync_with_conn( &self, conn: DbConnection, @@ -130,6 +131,7 @@ impl MlsGroup { * * This method will retry up to `crate::configuration::MAX_GROUP_SYNC_RETRIES` times. */ + #[tracing::instrument(level = "trace", skip(client, conn))] pub(super) async fn sync_until_intent_resolved( &self, conn: DbConnection, @@ -174,6 +176,7 @@ impl MlsGroup { } #[allow(clippy::too_many_arguments)] + #[tracing::instrument(level = "trace", skip_all)] async fn process_own_message( &self, client: &Client, @@ -287,6 +290,7 @@ impl MlsGroup { Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] async fn process_external_message( &self, client: &Client, @@ -425,6 +429,7 @@ impl MlsGroup { Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] pub(super) async fn process_message( &self, client: &Client, @@ -476,6 +481,7 @@ impl MlsGroup { } } + #[tracing::instrument(level = "trace", skip_all)] async fn consume_message( &self, envelope: &GroupMessage, @@ -505,6 +511,7 @@ impl MlsGroup { Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn process_messages( &self, messages: Vec, @@ -540,6 +547,7 @@ impl MlsGroup { } } + #[tracing::instrument(level = "trace", skip(conn, client))] pub(super) async fn receive( &self, conn: &DbConnection, @@ -601,6 +609,7 @@ impl MlsGroup { Ok(Some(msg)) } + #[tracing::instrument(level = "trace", skip(conn, client))] pub(super) async fn publish_intents( &self, conn: DbConnection, @@ -661,6 +670,7 @@ impl MlsGroup { } // Takes a StoredGroupIntent and returns the payload and post commit data as a tuple + #[tracing::instrument(level = "trace", skip_all)] async fn get_publish_intent_data( &self, provider: &XmtpOpenMlsProvider, @@ -747,6 +757,7 @@ impl MlsGroup { } } + #[tracing::instrument(level = "trace", skip(conn, client))] pub(crate) async fn post_commit( &self, conn: &DbConnection, @@ -849,6 +860,7 @@ impl MlsGroup { * * Callers may also include a list of added or removed inboxes */ + #[tracing::instrument(level = "trace", skip_all)] pub(super) async fn get_membership_update_intent( &self, client: &Client, @@ -907,6 +919,7 @@ impl MlsGroup { )) } + #[tracing::instrument(level = "trace", skip_all)] pub(super) async fn send_welcomes( &self, action: SendWelcomesAction, @@ -954,7 +967,6 @@ impl MlsGroup { futures.push(client.api_client.send_welcome_messages(welcomes)); } try_join_all(futures).await?; - Ok(()) } } diff --git a/xmtp_mls/src/groups/validated_commit.rs b/xmtp_mls/src/groups/validated_commit.rs index aa5f4913f..cae925895 100644 --- a/xmtp_mls/src/groups/validated_commit.rs +++ b/xmtp_mls/src/groups/validated_commit.rs @@ -563,6 +563,7 @@ fn extract_commit_participant( /// Get the [`GroupMembership`] from a [`GroupContext`] struct by iterating through all extensions /// until a match is found +#[tracing::instrument(level = "trace", skip_all)] pub fn extract_group_membership( extensions: &Extensions, ) -> Result { diff --git a/xmtp_mls/src/hpke.rs b/xmtp_mls/src/hpke.rs index 090cb9528..9948c4bcf 100644 --- a/xmtp_mls/src/hpke.rs +++ b/xmtp_mls/src/hpke.rs @@ -26,6 +26,7 @@ pub enum HpkeError { KeyNotFound, } +#[tracing::instrument(level = "trace", skip_all)] pub fn encrypt_welcome(welcome_payload: &[u8], hpke_key: &[u8]) -> Result, HpkeError> { let crypto = RustCrypto::default(); let ciphertext = encrypt_with_label( diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 9600dd114..2ff401bfe 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -318,6 +318,7 @@ where } /// For the given list of `inbox_id`s get all updates from the network that are newer than the last known `sequence_id`, write them in the db, and return the updates +#[tracing::instrument(level = "trace", skip_all)] pub async fn load_identity_updates( api_client: &ApiClientWrapper, conn: &DbConnection, diff --git a/xmtp_mls/src/retry.rs b/xmtp_mls/src/retry.rs index 958238781..4fbdf7468 100644 --- a/xmtp_mls/src/retry.rs +++ b/xmtp_mls/src/retry.rs @@ -154,7 +154,7 @@ macro_rules! retry_sync { #[allow(unused)] use $crate::retry::RetryableError; let mut attempts = 0; - loop { + tracing::trace_span!("retry").in_scope(|| loop { #[allow(clippy::redundant_closure_call)] match $code() { Ok(v) => break Ok(v), @@ -171,7 +171,7 @@ macro_rules! retry_sync { } } } - } + }) }}; } @@ -221,12 +221,15 @@ macro_rules! retry_sync { #[macro_export] macro_rules! retry_async { ($retry: expr, $code: tt) => {{ + use tracing::Instrument as _; #[allow(unused)] use $crate::retry::RetryableError; let mut attempts = 0; + let span = tracing::trace_span!("retry"); loop { + let span = span.clone(); #[allow(clippy::redundant_closure_call)] - match $code.await { + match $code.instrument(span).await { Ok(v) => break Ok(v), Err(e) => { if (&e).is_retryable() && attempts < $retry.retries() { diff --git a/xmtp_mls/src/storage/encrypted_store/group_intent.rs b/xmtp_mls/src/storage/encrypted_store/group_intent.rs index ef518f72e..e80dbe506 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_intent.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_intent.rs @@ -85,6 +85,7 @@ impl NewGroupIntent { } impl DbConnection { + #[tracing::instrument(level = "trace", skip(self))] pub fn insert_group_intent( &self, to_save: NewGroupIntent, @@ -97,6 +98,7 @@ impl DbConnection { } // Query for group_intents by group_id, optionally filtering by state and kind + #[tracing::instrument(level = "trace", skip(self))] pub fn find_group_intents( &self, group_id: Vec, @@ -194,6 +196,7 @@ impl DbConnection { } // Set the intent with the given ID to `Committed` + #[tracing::instrument(level = "trace", skip(self))] pub fn set_group_intent_error(&self, intent_id: ID) -> Result<(), StorageError> { let res = self.raw_query(|conn| { diesel::update(dsl::group_intents) diff --git a/xmtp_mls/src/storage/encrypted_store/identity_update.rs b/xmtp_mls/src/storage/encrypted_store/identity_update.rs index 995130746..e4e8dae4d 100644 --- a/xmtp_mls/src/storage/encrypted_store/identity_update.rs +++ b/xmtp_mls/src/storage/encrypted_store/identity_update.rs @@ -97,6 +97,7 @@ impl DbConnection { } /// Given a list of inbox_ids return a hashamp of each inbox ID -> highest known sequence ID + #[tracing::instrument(level = "trace", skip_all)] pub fn get_latest_sequence_id( &self, inbox_ids: &[String], diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index d9836813d..b3a3b2100 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -6,6 +6,11 @@ use ethers::signers::{LocalWallet, Signer}; use indicatif::{ProgressBar, ProgressStyle}; use serde::{Deserialize, Serialize}; use thiserror::Error; +use tracing::{Metadata, Subscriber}; +use tracing_subscriber::{ + layer::{Context, Filter}, + registry::LookupSpan, +}; use xmtp_cryptography::utils::rng; use super::test::TestClient; @@ -18,6 +23,29 @@ pub enum BenchError { Io(#[from] std::io::Error), } +/// Filters for only spans where the root span name is "bench" +pub struct BenchFilter; + +impl Filter for BenchFilter +where + S: Subscriber + for<'lookup> LookupSpan<'lookup> + std::fmt::Debug, + for<'lookup> >::Data: std::fmt::Debug, +{ + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { + if meta.name() == "bench" { + return true; + } + if let Some(id) = cx.current_span().id() { + if let Some(s) = cx.span_scope(id) { + if let Some(s) = s.from_root().take(1).collect::>().first() { + return s.name() == "bench"; + } + } + } + false + } +} + pub fn file_path(is_dev_network: bool) -> String { if is_dev_network { format!("{}/dev-identities.generated", env!("CARGO_MANIFEST_DIR")) From ad4db19147cea4aa61731e963d07d919a13bbdcb Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Thu, 6 Jun 2024 15:15:14 -0400 Subject: [PATCH 30/41] more instrumentation --- Cargo.lock | 1 + xmtp_api_grpc/Cargo.toml | 1 + xmtp_api_grpc/src/identity.rs | 3 + xmtp_mls/benches/group_limit.rs | 130 +++++++++--------- xmtp_mls/src/groups/mod.rs | 1 + xmtp_mls/src/identity_updates.rs | 26 ++-- .../encrypted_store/identity_update.rs | 1 + xmtp_mls/src/utils/bench.rs | 32 ++++- 8 files changed, 116 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cc94bc514..04fb0d56b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6374,6 +6374,7 @@ dependencies = [ "tokio", "tonic", "tower", + "tracing", "uuid 1.8.0", "webpki-roots 0.23.1", "xmtp_proto", diff --git a/xmtp_api_grpc/Cargo.toml b/xmtp_api_grpc/Cargo.toml index 6b5ea6022..c3b5de304 100644 --- a/xmtp_api_grpc/Cargo.toml +++ b/xmtp_api_grpc/Cargo.toml @@ -25,6 +25,7 @@ tower = "0.4.13" webpki-roots = "0.23.0" xmtp_proto = { path = "../xmtp_proto", features = ["proto_full", "grpc"] } xmtp_v2 = { path = "../xmtp_v2" } +tracing.workspace = true [dev-dependencies] uuid = { version = "1.3.1", features = ["v4"] } diff --git a/xmtp_api_grpc/src/identity.rs b/xmtp_api_grpc/src/identity.rs index b3e324bdf..5b635ef77 100644 --- a/xmtp_api_grpc/src/identity.rs +++ b/xmtp_api_grpc/src/identity.rs @@ -12,6 +12,7 @@ use crate::Client; #[async_trait] impl XmtpIdentityClient for Client { + #[tracing::instrument(level = "trace", skip_all)] async fn publish_identity_update( &self, request: PublishIdentityUpdateRequest, @@ -28,6 +29,7 @@ impl XmtpIdentityClient for Client { .map_err(|err| Error::new(ErrorKind::IdentityError).with(err)) } + #[tracing::instrument(level = "trace", skip_all)] async fn get_inbox_ids( &self, request: GetInboxIdsRequest, @@ -44,6 +46,7 @@ impl XmtpIdentityClient for Client { .map_err(|err| Error::new(ErrorKind::IdentityError).with(err)) } + #[tracing::instrument(level = "trace", skip_all)] async fn get_identity_updates_v2( &self, request: GetIdentityUpdatesV2Request, diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index 422b7e5a7..854a49b6b 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -3,46 +3,22 @@ //! may be used to generate a flamegraph of execution from tracing logs. use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion, Throughput}; use ethers::signers::LocalWallet; -use once_cell::sync::OnceCell; -use std::{collections::HashMap, sync::Arc, sync::Once}; +use std::{collections::HashMap, sync::Arc}; use tokio::runtime::{Builder, Handle, Runtime}; -use tracing::{span, Instrument, Level}; -use tracing_flame::{FlameLayer, FlushGuard}; -use tracing_subscriber::{ - layer::{Layer, SubscriberExt}, - util::SubscriberInitExt, - EnvFilter, -}; +use tracing::{trace_span, Instrument}; use xmtp_cryptography::utils::rng; use xmtp_mls::{ builder::ClientBuilder, utils::{ - bench::{create_identities_if_dont_exist, BenchFilter, Identity}, + bench::{create_identities_if_dont_exist, init_logging, Identity}, test::TestClient, }, }; -static INIT: Once = Once::new(); -pub const IDENTITY_SAMPLES: [usize; 1] = [10 /*20, 40, 80, 100, 200, 400, 800*/]; -pub const MAX_IDENTITIES: usize = 5_000; +pub const IDENTITY_SAMPLES: [usize; 8] = [10, 20, 40, 80, 100, 200, 400, 800]; +pub const MAX_IDENTITIES: usize = 2_000; pub const SAMPLE_SIZE: usize = 10; -static LOGGER: OnceCell>> = OnceCell::new(); - -pub(crate) fn init_logging() { - INIT.call_once(|| { - let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded").unwrap(); - let flame_layer = flame_layer.with_threads_collapsed(true); - - tracing_subscriber::registry() - .with(EnvFilter::from_default_env()) - .with(flame_layer.with_filter(BenchFilter)) - .init(); - - LOGGER.set(guard).unwrap(); - }) -} - fn setup() -> (Arc, Vec, Runtime) { let runtime = Builder::new_multi_thread() .enable_time() @@ -101,10 +77,22 @@ fn add_to_empty_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let addrs = map.get(&size).unwrap(); + let span = trace_span!("bench", size); b.to_async(&runtime).iter_batched( - || (client.clone(), client.create_group(None).unwrap()), - |(client, group)| async move { - group.add_members(&client, addrs.clone()).await.unwrap(); + || { + ( + client.clone(), + client.create_group(None).unwrap(), + addrs.clone(), + span.clone(), + ) + }, + |(client, group, addrs, span)| async move { + group + .add_members(&client, addrs) + .instrument(span) + .await + .unwrap(); }, BatchSize::SmallInput, ); @@ -130,11 +118,20 @@ fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); + let span = trace_span!("bench", size); b.to_async(&runtime).iter_batched( - || (client.clone(), client.create_group(None).unwrap()), - |(client, group)| async move { + || { + ( + client.clone(), + client.create_group(None).unwrap(), + span.clone(), + ids.clone(), + ) + }, + |(client, group, span, ids)| async move { group - .add_members_by_inbox_id(&client, ids.clone()) + .add_members_by_inbox_id(&client, ids) + .instrument(span) .await .unwrap(); }, @@ -163,7 +160,7 @@ fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - // let setup = setup(&runtime); + let span = trace_span!("bench", size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -178,17 +175,15 @@ fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { .await .unwrap(); - (client.clone(), group) + (client.clone(), group, span.clone(), ids.clone()) }) }, - |(client, group)| { - let client = client.clone(); - async move { - group - .add_members_by_inbox_id(&client, ids.clone()) - .await - .unwrap(); - } + |(client, group, span, ids)| async move { + group + .add_members_by_inbox_id(&client, ids) + .instrument(span) + .await + .unwrap(); }, BatchSize::SmallInput, ); @@ -215,7 +210,7 @@ fn remove_all_members_from_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - + let span = trace_span!("bench", size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -224,12 +219,13 @@ fn remove_all_members_from_group(c: &mut Criterion) { .add_members_by_inbox_id(&client, ids.clone()) .await .unwrap(); - (client.clone(), group) + (client.clone(), group, span.clone(), ids.clone()) }) }, - |(client, group)| async move { + |(client, group, span, ids)| async move { group - .remove_members_by_inbox_id(&client, ids.clone()) + .remove_members_by_inbox_id(&client, ids) + .instrument(span) .await .unwrap(); }, @@ -258,7 +254,7 @@ fn remove_half_members_from_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - + let span = trace_span!("bench", size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -267,12 +263,18 @@ fn remove_half_members_from_group(c: &mut Criterion) { .add_members_by_inbox_id(&client, ids.clone()) .await .unwrap(); - (client.clone(), group) + ( + client.clone(), + group, + span.clone(), + ids[0..(size / 2)].into(), + ) }) }, - |(client, group)| async move { + |(client, group, span, ids)| async move { group - .remove_members_by_inbox_id(&client, ids[0..(size / 2)].into()) + .remove_members_by_inbox_id(&client, ids) + .instrument(span) .await .unwrap(); }, @@ -301,7 +303,7 @@ fn add_1_member_to_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - let span = span!(Level::TRACE, "bench", size); + let span = trace_span!("bench", size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -311,19 +313,15 @@ fn add_1_member_to_group(c: &mut Criterion) { .await .unwrap(); let member = inbox_ids.last().unwrap().clone(); - (client.clone(), group, member) + (client.clone(), group, vec![member], span.clone()) }) }, - |(client, group, member)| { - let span = span.clone(); - async move { - group - .add_members_by_inbox_id(&client, vec![member]) - .in_current_span() - .await - .unwrap(); - } - .instrument(span) + |(client, group, member, span)| async move { + group + .add_members_by_inbox_id(&client, member) + .instrument(span) + .await + .unwrap(); }, BatchSize::SmallInput, ); @@ -335,5 +333,5 @@ fn add_1_member_to_group(c: &mut Criterion) { criterion_group!( name = group_limit; config = Criterion::default().sample_size(10); - targets = /*add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, */ add_1_member_to_group); + targets = add_to_empty_group, add_to_empty_group_by_inbox_id, remove_all_members_from_group, remove_half_members_from_group, add_to_100_member_group_by_inbox_id, add_1_member_to_group); criterion_main!(group_limit); diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 70c8a4510..61d7934e6 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -465,6 +465,7 @@ impl MlsGroup { * If any existing members have new installations that have not been added, the missing installations * will be added as part of this process as well. */ + #[tracing::instrument(level = "trace", skip_all)] pub async fn add_members( &self, client: &Client, diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index 2ff401bfe..a768620ee 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -334,7 +334,7 @@ pub async fn load_identity_updates( .map(|inbox_id| GetIdentityUpdatesV2Filter { sequence_id: existing_sequence_ids .get(&inbox_id) - .cloned() + .copied() .map(|i| i as u64), inbox_id, }) @@ -342,18 +342,20 @@ pub async fn load_identity_updates( let updates = api_client.get_identity_updates_v2(filters).await?; - let to_store = updates - .clone() - .into_iter() - .flat_map(|(inbox_id, updates)| { - updates.into_iter().map(move |update| StoredIdentityUpdate { - inbox_id: inbox_id.clone(), - sequence_id: update.sequence_id as i64, - server_timestamp_ns: update.server_timestamp_ns as i64, - payload: update.update.to_proto().encode_to_vec(), + let to_store = tracing::trace_span!("store updates").in_scope(|| { + updates + .clone() + .into_iter() + .flat_map(|(inbox_id, updates)| { + updates.into_iter().map(move |update| StoredIdentityUpdate { + inbox_id: inbox_id.clone(), + sequence_id: update.sequence_id as i64, + server_timestamp_ns: update.server_timestamp_ns as i64, + payload: update.update.to_proto().encode_to_vec(), + }) }) - }) - .collect::>(); + .collect::>() + }); conn.insert_or_ignore_identity_updates(&to_store)?; Ok(updates) diff --git a/xmtp_mls/src/storage/encrypted_store/identity_update.rs b/xmtp_mls/src/storage/encrypted_store/identity_update.rs index e4e8dae4d..d25b10cb7 100644 --- a/xmtp_mls/src/storage/encrypted_store/identity_update.rs +++ b/xmtp_mls/src/storage/encrypted_store/identity_update.rs @@ -72,6 +72,7 @@ impl DbConnection { } /// Batch insert identity updates, ignoring duplicates. + #[tracing::instrument(level = "trace", skip(updates))] pub fn insert_or_ignore_identity_updates( &self, updates: &[StoredIdentityUpdate], diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index b3a3b2100..0a2655a80 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -4,12 +4,17 @@ use crate::builder::ClientBuilder; use ethers::signers::{LocalWallet, Signer}; use indicatif::{ProgressBar, ProgressStyle}; +use once_cell::sync::OnceCell; use serde::{Deserialize, Serialize}; +use std::sync::Once; use thiserror::Error; use tracing::{Metadata, Subscriber}; +use tracing_flame::{FlameLayer, FlushGuard}; use tracing_subscriber::{ - layer::{Context, Filter}, + layer::{Context, Filter, Layer, SubscriberExt}, registry::LookupSpan, + util::SubscriberInitExt, + EnvFilter, }; use xmtp_cryptography::utils::rng; @@ -23,6 +28,31 @@ pub enum BenchError { Io(#[from] std::io::Error), } +static INIT: Once = Once::new(); + +static LOGGER: OnceCell>> = OnceCell::new(); + +/// initializes logging for benchmarks +/// this does not include any fmt output +/// but instead generates a flamegraph of the benched execution +/// if `RUST_LOG=trace` +pub fn init_logging() { + INIT.call_once(|| { + let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded").unwrap(); + let flame_layer = flame_layer + .with_threads_collapsed(true) + .with_module_path(true) + .with_empty_samples(false); + + tracing_subscriber::registry() + .with(EnvFilter::from_default_env()) + .with(flame_layer.with_filter(BenchFilter)) + .init(); + + LOGGER.set(guard).unwrap(); + }) +} + /// Filters for only spans where the root span name is "bench" pub struct BenchFilter; From 31fa71c832785f4555678ca96fd011e592517cc8 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 7 Jun 2024 15:32:05 -0400 Subject: [PATCH 31/41] add more instrumentation/organize --- README.md | 48 ++++++++++++++++++++-------- bindings_ffi/Cargo.lock | 1 + xmtp_api_grpc/src/grpc_api_helper.rs | 8 +++++ xmtp_mls/benches/group_limit.rs | 20 ++++++------ xmtp_mls/src/api/mls.rs | 7 ++++ xmtp_mls/src/client.rs | 1 + xmtp_mls/src/configuration.rs | 4 +++ xmtp_mls/src/groups/mod.rs | 2 ++ xmtp_mls/src/groups/sync.rs | 19 ++++++----- xmtp_mls/src/utils/bench.rs | 23 +++++++------ 10 files changed, 92 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index eaf3fc032..2ce821b71 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,23 @@ # LibXMTP -![https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg) ![https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg) ![Status](https://img.shields.io/badge/Project_status-Alpha-orange) +![https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg) +![https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg) +![Status](https://img.shields.io/badge/Project_status-Alpha-orange) -LibXMTP is a shared library encapsulating the core functionality of the XMTP messaging protocol, such as cryptography, networking, and language bindings. +LibXMTP is a shared library encapsulating the core functionality of the XMTP +messaging protocol, such as cryptography, networking, and language bindings. -> **Important** -> This software is in **alpha** status and ready for you to start experimenting with. However, we do not recommend using alpha software in production apps. Expect frequent changes as we add features and iterate based on feedback. +> **Important**\ +> This software is in **alpha** status and ready for you to start experimenting +> with. However, we do not recommend using alpha software in production apps. +> Expect frequent changes as we add features and iterate based on feedback. ## Requirements - Install [Rustup](https://rustup.rs/) - Install [Docker](https://www.docker.com/get-started/) -- Install [Foundry](https://book.getfoundry.sh/getting-started/installation#using-foundryup) +- Install + [Foundry](https://book.getfoundry.sh/getting-started/installation#using-foundryup) ## Development @@ -33,7 +39,8 @@ Start Docker Desktop. ## Quick Start (Dev Containers) -This project supports containerized development. From Visual Studio Code Dev Containers extension specify the Dockerfile as the target: +This project supports containerized development. From Visual Studio Code Dev +Containers extension specify the Dockerfile as the target: `Reopen in Container` @@ -49,24 +56,37 @@ docker build . -t libxmtp:1 libxmtp/ -├ [`bindings_ffi`](./bindings_ffi): FFI bindings for Android and iOS (in progress) +├ [`bindings_ffi`](./bindings_ffi): FFI bindings for Android and iOS (in +progress) ├ [`bindings_wasm`](./bindings_wasm): Wasm bindings (in progress) ├ examples/ -   ├ [`android/xmtpv3_example`](./examples/android/xmtpv3_example): Example Android app (in progress) +   ├ [`android/xmtpv3_example`](./examples/android/xmtpv3_example): Example +Android app (in progress) -   └ [`cli`](./examples/cli): Example XMTP console client. Use the CLI to try out sending double ratchet messages on the XMTP `dev` network. +   └ [`cli`](./examples/cli): Example XMTP console client. Use the CLI to try +out sending double ratchet messages on the XMTP `dev` network. -├ [`xmtp_api_grpc`](./xmtp_api_grpc): API client for XMTP's gRPC API, using code from `xmtp_proto` +├ [`xmtp_api_grpc`](./xmtp_api_grpc): API client for XMTP's gRPC API, using code +from `xmtp_proto` -├ [`xmtp_api_grpc_gateway`](./xmtp_api_grpc_gateway): API client for XMTP's gRPC Gateway API, using code from `xmtp_proto` (in progress) +├ [`xmtp_api_grpc_gateway`](./xmtp_api_grpc_gateway): API client for XMTP's gRPC +Gateway API, using code from `xmtp_proto` (in progress) ├ [`xmtp_cryptography`](./xmtp_cryptography): Cryptographic operations -├ [`xmtp_mls`](./xmtp_mls): Version 3 of XMTP which implements [Messaging Layer Security](https://messaginglayersecurity.rocks/). +├ [`xmtp_mls`](./xmtp_mls): Version 3 of XMTP which implements +[Messaging Layer Security](https://messaginglayersecurity.rocks/). -├ [`xmtp_proto`](./xmtp_proto): Generated code for handling XMTP protocol buffers +├ [`xmtp_proto`](./xmtp_proto): Generated code for handling XMTP protocol +buffers -└ [`xmtp_v2`](./xmtp_v2): Version 2 of XMTP which uses a [user key bundle](https://xmtp.org/docs/concepts/key-generation-and-usage) to encrypt and exchange messages. +└ [`xmtp_v2`](./xmtp_v2): Version 2 of XMTP which uses a +[user key bundle](https://xmtp.org/docs/concepts/key-generation-and-usage) to +encrypt and exchange messages. + +## Benchmarks + +relevant environment variables: `DEV_GRPC`, `XMTP_FLAMEGRAPH` diff --git a/bindings_ffi/Cargo.lock b/bindings_ffi/Cargo.lock index 2a011dc9f..4c1def887 100644 --- a/bindings_ffi/Cargo.lock +++ b/bindings_ffi/Cargo.lock @@ -5790,6 +5790,7 @@ dependencies = [ "tokio", "tonic", "tower", + "tracing", "webpki-roots 0.23.1", "xmtp_proto", "xmtp_v2", diff --git a/xmtp_api_grpc/src/grpc_api_helper.rs b/xmtp_api_grpc/src/grpc_api_helper.rs index 1fa49c0cd..6ec010a44 100644 --- a/xmtp_api_grpc/src/grpc_api_helper.rs +++ b/xmtp_api_grpc/src/grpc_api_helper.rs @@ -344,6 +344,7 @@ impl MutableApiSubscription for GrpcMutableSubscription { #[async_trait] impl XmtpMlsClient for Client { + #[tracing::instrument(level = "trace", skip_all)] async fn register_installation( &self, req: RegisterInstallationRequest, @@ -356,6 +357,7 @@ impl XmtpMlsClient for Client { } } + #[tracing::instrument(level = "trace", skip_all)] async fn upload_key_package(&self, req: UploadKeyPackageRequest) -> Result<(), Error> { let client = &mut self.mls_client.clone(); let res = client.upload_key_package(req).await; @@ -365,6 +367,7 @@ impl XmtpMlsClient for Client { } } + #[tracing::instrument(level = "trace", skip_all)] async fn fetch_key_packages( &self, req: FetchKeyPackagesRequest, @@ -376,6 +379,7 @@ impl XmtpMlsClient for Client { .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) } + #[tracing::instrument(level = "trace", skip_all)] async fn send_group_messages(&self, req: SendGroupMessagesRequest) -> Result<(), Error> { let client = &mut self.mls_client.clone(); let res = client.send_group_messages(req).await; @@ -386,6 +390,7 @@ impl XmtpMlsClient for Client { } } + #[tracing::instrument(level = "trace", skip_all)] async fn send_welcome_messages(&self, req: SendWelcomeMessagesRequest) -> Result<(), Error> { let client = &mut self.mls_client.clone(); let res = client.send_welcome_messages(req).await; @@ -396,6 +401,7 @@ impl XmtpMlsClient for Client { } } + #[tracing::instrument(level = "trace", skip_all)] async fn query_group_messages( &self, req: QueryGroupMessagesRequest, @@ -407,6 +413,7 @@ impl XmtpMlsClient for Client { .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) } + #[tracing::instrument(level = "trace", skip_all)] async fn query_welcome_messages( &self, req: QueryWelcomeMessagesRequest, @@ -418,6 +425,7 @@ impl XmtpMlsClient for Client { .map_err(|e| Error::new(ErrorKind::MlsError).with(e)) } + #[tracing::instrument(level = "trace", skip_all)] async fn get_identity_updates( &self, req: GetIdentityUpdatesRequest, diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs index 854a49b6b..3d62a5544 100644 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -10,13 +10,13 @@ use xmtp_cryptography::utils::rng; use xmtp_mls::{ builder::ClientBuilder, utils::{ - bench::{create_identities_if_dont_exist, init_logging, Identity}, + bench::{create_identities_if_dont_exist, init_logging, Identity, BENCH_ROOT_SPAN}, test::TestClient, }, }; -pub const IDENTITY_SAMPLES: [usize; 8] = [10, 20, 40, 80, 100, 200, 400, 800]; -pub const MAX_IDENTITIES: usize = 2_000; +pub const IDENTITY_SAMPLES: [usize; 9] = [10, 20, 40, 80, 100, 200, 300, 400, 450]; +pub const MAX_IDENTITIES: usize = 1_000; pub const SAMPLE_SIZE: usize = 10; fn setup() -> (Arc, Vec, Runtime) { @@ -34,7 +34,7 @@ fn setup() -> (Arc, Vec, Runtime) { let dev = std::env::var("DEV_GRPC"); let is_dev_network = matches!(dev, Ok(d) if d == "true" || d == "1"); let client = if is_dev_network { - println!("Using Dev GRPC"); + log::info!("Using Dev GRPC"); Arc::new(ClientBuilder::new_dev_client(&wallet).await) } else { Arc::new(ClientBuilder::new_test_client(&wallet).await) @@ -77,7 +77,7 @@ fn add_to_empty_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let addrs = map.get(&size).unwrap(); - let span = trace_span!("bench", size); + let span = trace_span!(BENCH_ROOT_SPAN, size); b.to_async(&runtime).iter_batched( || { ( @@ -118,7 +118,7 @@ fn add_to_empty_group_by_inbox_id(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - let span = trace_span!("bench", size); + let span = trace_span!(BENCH_ROOT_SPAN, size); b.to_async(&runtime).iter_batched( || { ( @@ -160,7 +160,7 @@ fn add_to_100_member_group_by_inbox_id(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - let span = trace_span!("bench", size); + let span = trace_span!(BENCH_ROOT_SPAN, size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -210,7 +210,7 @@ fn remove_all_members_from_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - let span = trace_span!("bench", size); + let span = trace_span!(BENCH_ROOT_SPAN, size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -254,7 +254,7 @@ fn remove_half_members_from_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - let span = trace_span!("bench", size); + let span = trace_span!(BENCH_ROOT_SPAN, size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { @@ -303,7 +303,7 @@ fn add_1_member_to_group(c: &mut Criterion) { benchmark_group.throughput(Throughput::Elements(*size as u64)); benchmark_group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { let ids = map.get(&size).unwrap(); - let span = trace_span!("bench", size); + let span = trace_span!(BENCH_ROOT_SPAN, size); b.to_async(&runtime).iter_batched( || { bench_async_setup(|| async { diff --git a/xmtp_mls/src/api/mls.rs b/xmtp_mls/src/api/mls.rs index 877afe16f..eaf3af6b4 100644 --- a/xmtp_mls/src/api/mls.rs +++ b/xmtp_mls/src/api/mls.rs @@ -113,6 +113,7 @@ where Ok(out) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn query_welcome_messages( &self, installation_id: Vec, @@ -160,6 +161,7 @@ where /// New InboxID clients should set `is_inbox_id_credential` to true. /// V3 clients should have `is_inbox_id_credential` to `false`. /// Not indicating your client version will result in validation failure. + #[tracing::instrument(level = "trace", skip_all)] pub async fn register_installation( &self, key_package: Vec, @@ -186,6 +188,7 @@ where /// New InboxID clients should set `is_inbox_id_credential` to true. /// V3 clients should have `is_inbox_id_credential` to `false`. /// Not indicating your client version will result in validation failure. + #[tracing::instrument(level = "trace", skip_all)] pub async fn upload_key_package( &self, key_package: Vec, @@ -208,6 +211,7 @@ where Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn fetch_key_packages( &self, installation_keys: Vec>, @@ -243,6 +247,7 @@ where Ok(mapping) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn send_welcome_messages( &self, messages: &[WelcomeMessageInput], @@ -261,6 +266,7 @@ where Ok(()) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn get_identity_updates( &self, start_time_ns: u64, @@ -320,6 +326,7 @@ where Ok(mapping) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn send_group_messages(&self, group_messages: Vec<&[u8]>) -> Result<(), ApiError> { let to_send: Vec = group_messages .iter() diff --git a/xmtp_mls/src/client.rs b/xmtp_mls/src/client.rs index 3cd082c4a..a6cb08c60 100644 --- a/xmtp_mls/src/client.rs +++ b/xmtp_mls/src/client.rs @@ -412,6 +412,7 @@ where Ok(welcomes) } + #[tracing::instrument(level = "trace", skip_all)] pub(crate) async fn get_key_packages_for_installation_ids( &self, installation_ids: Vec>, diff --git a/xmtp_mls/src/configuration.rs b/xmtp_mls/src/configuration.rs index 624992d05..6d3e627d7 100644 --- a/xmtp_mls/src/configuration.rs +++ b/xmtp_mls/src/configuration.rs @@ -18,6 +18,10 @@ pub const UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NANOSECONDS_IN_HOUR / 2; // 30 pub const MAX_GROUP_SIZE: u16 = 20_000; +/// the max amount of data that can be sent in one gRPC call +/// we leave 5 * 1024 * 1024 as extra buffer room +pub const GRPC_DATA_LIMIT: usize = 45 * 1024 * 1024; + pub const DELIMITER: char = '\x01'; /// MLS Extension Types diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 61d7934e6..abb5ec952 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -752,6 +752,7 @@ pub fn build_mutable_metadata_extension_default( )) } +#[tracing::instrument(level = "trace", skip_all)] pub fn build_mutable_metadata_extensions_for_metadata_update( group: &OpenMlsGroup, field_name: String, @@ -773,6 +774,7 @@ pub fn build_mutable_metadata_extensions_for_metadata_update( Ok(extensions) } +#[tracing::instrument(level = "trace", skip_all)] pub fn build_mutable_metadata_extensions_for_admin_lists_update( group: &OpenMlsGroup, admin_lists_update: UpdateAdminListIntentData, diff --git a/xmtp_mls/src/groups/sync.rs b/xmtp_mls/src/groups/sync.rs index eec21f003..a5b1bb39d 100644 --- a/xmtp_mls/src/groups/sync.rs +++ b/xmtp_mls/src/groups/sync.rs @@ -16,7 +16,10 @@ use super::{ use crate::{ client::MessageProcessingError, codecs::{group_updated::GroupUpdatedCodec, ContentCodec}, - configuration::{DELIMITER, MAX_INTENT_PUBLISH_ATTEMPTS, UPDATE_INSTALLATIONS_INTERVAL_NS}, + configuration::{ + DELIMITER, GRPC_DATA_LIMIT, MAX_GROUP_SIZE, MAX_INTENT_PUBLISH_ATTEMPTS, + UPDATE_INSTALLATIONS_INTERVAL_NS, + }, groups::{intents::UpdateMetadataIntentData, validated_commit::ValidatedCommit}, hpke::{encrypt_welcome, HpkeError}, identity::parse_credential, @@ -69,10 +72,6 @@ use xmtp_proto::xmtp::mls::{ }, }; -// TODO: Change `send_welcomes` to be constant-sized (#812) -/// the max size gRPC will accept -pub const MAX_CHUNK: usize = 50 * 1024 * 1024; - impl MlsGroup { pub async fn sync(&self, client: &Client) -> Result<(), GroupError> where @@ -951,17 +950,20 @@ impl MlsGroup { .first() .ok_or(GroupError::Generic("No welcomes to send".to_string()))?; - let chunk_size = MAX_CHUNK + let chunk_size = GRPC_DATA_LIMIT / welcome .version .as_ref() .map(|w| match w { WelcomeMessageInputVersion::V1(w) => { - w.installation_key.len() + w.data.len() + w.hpke_public_key.len() + let w = w.installation_key.len() + w.data.len() + w.hpke_public_key.len(); + log::debug!("total welcome message proto bytes={w}"); + w } }) - .unwrap_or(MAX_CHUNK / 200); + .unwrap_or(GRPC_DATA_LIMIT / usize::from(MAX_GROUP_SIZE)); + log::debug!("welcome chunk_size={chunk_size}"); let mut futures = vec![]; for welcomes in welcomes.chunks(chunk_size) { futures.push(client.api_client.send_welcome_messages(welcomes)); @@ -997,6 +999,7 @@ fn extract_message_sender( // Takes UpdateGroupMembershipIntentData and applies it to the openmls group // returning the commit and post_commit_action +#[tracing::instrument(level = "trace", skip_all)] async fn apply_update_group_membership_intent( client: &Client, provider: &XmtpOpenMlsProvider, diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index 0a2655a80..5fee366fb 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -20,6 +20,8 @@ use xmtp_cryptography::utils::rng; use super::test::TestClient; +pub const BENCH_ROOT_SPAN: &str = "xmtp-trace-bench"; + #[derive(Debug, Error)] pub enum BenchError { #[error(transparent)] @@ -33,20 +35,23 @@ static INIT: Once = Once::new(); static LOGGER: OnceCell>> = OnceCell::new(); /// initializes logging for benchmarks -/// this does not include any fmt output -/// but instead generates a flamegraph of the benched execution -/// if `RUST_LOG=trace` +/// - FMT logging is enabled by passing the normal `RUST_LOG` environment variable options. +/// - Generate a flamegraph from tracing data by passing `XMTP_FLAMEGRAPH=trace` pub fn init_logging() { INIT.call_once(|| { let (flame_layer, guard) = FlameLayer::with_file("./tracing.folded").unwrap(); let flame_layer = flame_layer .with_threads_collapsed(true) - .with_module_path(true) - .with_empty_samples(false); + .with_module_path(true); + // .with_empty_samples(false); tracing_subscriber::registry() - .with(EnvFilter::from_default_env()) - .with(flame_layer.with_filter(BenchFilter)) + .with(tracing_subscriber::fmt::layer().with_filter(EnvFilter::from_default_env())) + .with( + flame_layer + .with_filter(BenchFilter) + .with_filter(EnvFilter::from_env("XMTP_FLAMEGRAPH")), + ) .init(); LOGGER.set(guard).unwrap(); @@ -62,13 +67,13 @@ where for<'lookup> >::Data: std::fmt::Debug, { fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { - if meta.name() == "bench" { + if meta.name() == BENCH_ROOT_SPAN { return true; } if let Some(id) = cx.current_span().id() { if let Some(s) = cx.span_scope(id) { if let Some(s) = s.from_root().take(1).collect::>().first() { - return s.name() == "bench"; + return s.name() == BENCH_ROOT_SPAN; } } } From 973b601806550f83698d69d1d85743316777264d Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 7 Jun 2024 16:04:35 -0400 Subject: [PATCH 32/41] describe running benchmarks in readme --- README.md | 21 +++++++++++++++++++-- dev/bench | 8 ++++++++ dev/flamegraph | 15 +++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100755 dev/bench create mode 100755 dev/flamegraph diff --git a/README.md b/README.md index 2ce821b71..602c3be0e 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,23 @@ buffers [user key bundle](https://xmtp.org/docs/concepts/key-generation-and-usage) to encrypt and exchange messages. -## Benchmarks +## XMTP MLS Rust SDK Benchmarks -relevant environment variables: `DEV_GRPC`, `XMTP_FLAMEGRAPH` +### Run the benchmarks + +**possible benchmarks include:** + +- `group_limit`: benchmarks surrounding maximum members adding/removed from + group +- `crypto`: benchmarks surrounding cryptographic functions + +**Example Commands** + +- **Run a specific category of benchmark** + `cargo bench --features bench -p xmtp_mls --bench group_limit` +- **Run against dev grpc** DEV_GRPC=1 cargo bench --features bench -p xmtp_mls + --bench group_limit +- **Just run all benchmarks** ./dev/bench +- **Run one specific benchmark** ./dev/bench add_1_member_to_group +- **Generate flamegraph from one benchmark** ./dev/flamegraph + add_1_member_to_group diff --git a/dev/bench b/dev/bench new file mode 100755 index 000000000..5f15c8ff6 --- /dev/null +++ b/dev/bench @@ -0,0 +1,8 @@ +#!/bin/bash +set -eou pipefail + +if [[ -z "${1-}" ]]; then + cargo bench --no-fail-fast --features bench +else + cargo bench --no-fail-fast --features bench -- $1 +fi diff --git a/dev/flamegraph b/dev/flamegraph new file mode 100755 index 000000000..1cf35918c --- /dev/null +++ b/dev/flamegraph @@ -0,0 +1,15 @@ +#!/bin/bash +set -eou pipefail + + +if [[ "${OSTYPE}" == "darwin"* ]]; then + if ! which inferno-flamegraph &>/dev/null; then cargo install inferno; fi +fi + +if [[ -z "${1-}" ]]; then + XMTP_FLAMEGRAPH=trace cargo bench --no-fail-fast --features bench +else + XMTP_FLAMEGRAPH=trace cargo bench --no-fail-fast --features bench -- $1 +fi + +cat xmtp_mls/tracing.folded | inferno-flamegraph > tracing-flamegraph.svg From 9c4c23118f030af3f99cc45d2be6852c46360f81 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 7 Jun 2024 16:18:36 -0400 Subject: [PATCH 33/41] group size to 400 --- xmtp_mls/src/api/identity.rs | 1 + xmtp_mls/src/configuration.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/xmtp_mls/src/api/identity.rs b/xmtp_mls/src/api/identity.rs index 14c098740..28ed1c5e3 100644 --- a/xmtp_mls/src/api/identity.rs +++ b/xmtp_mls/src/api/identity.rs @@ -116,6 +116,7 @@ where Ok(inbox_map) } + #[tracing::instrument(level = "trace", skip_all)] pub async fn get_inbox_ids( &self, account_addresses: Vec, diff --git a/xmtp_mls/src/configuration.rs b/xmtp_mls/src/configuration.rs index 6cbe0ef3b..1299070a7 100644 --- a/xmtp_mls/src/configuration.rs +++ b/xmtp_mls/src/configuration.rs @@ -16,7 +16,7 @@ const NANOSECONDS_IN_HOUR: i64 = 3_600_000_000_000; pub const UPDATE_INSTALLATIONS_INTERVAL_NS: i64 = NANOSECONDS_IN_HOUR / 2; // 30 min -pub const MAX_GROUP_SIZE: u16 = 20_000; +pub const MAX_GROUP_SIZE: u16 = 400; /// the max amount of data that can be sent in one gRPC call /// we leave 5 * 1024 * 1024 as extra buffer room From 2d05834400e386be9f8c0c20f4447faaf408bf4e Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 7 Jun 2024 16:30:14 -0400 Subject: [PATCH 34/41] restore readme --- README.md | 45 ++++++++++++++------------------------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 602c3be0e..207e5c5c4 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,17 @@ # LibXMTP -![https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg) -![https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg) -![Status](https://img.shields.io/badge/Project_status-Alpha-orange) +![https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/test.yml/badge.svg) ![https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg](https://github.com/xmtp/libxmtp/actions/workflows/lint.yml/badge.svg) ![Status](https://img.shields.io/badge/Project_status-Alpha-orange) -LibXMTP is a shared library encapsulating the core functionality of the XMTP -messaging protocol, such as cryptography, networking, and language bindings. +LibXMTP is a shared library encapsulating the core functionality of the XMTP messaging protocol, such as cryptography, networking, and language bindings. -> **Important**\ -> This software is in **alpha** status and ready for you to start experimenting -> with. However, we do not recommend using alpha software in production apps. -> Expect frequent changes as we add features and iterate based on feedback. +> **Important** +> This software is in **alpha** status and ready for you to start experimenting with. However, we do not recommend using alpha software in production apps. Expect frequent changes as we add features and iterate based on feedback. ## Requirements - Install [Rustup](https://rustup.rs/) - Install [Docker](https://www.docker.com/get-started/) -- Install - [Foundry](https://book.getfoundry.sh/getting-started/installation#using-foundryup) +- Install [Foundry](https://book.getfoundry.sh/getting-started/installation#using-foundryup) ## Development @@ -39,8 +33,7 @@ Start Docker Desktop. ## Quick Start (Dev Containers) -This project supports containerized development. From Visual Studio Code Dev -Containers extension specify the Dockerfile as the target: +This project supports containerized development. From Visual Studio Code Dev Containers extension specify the Dockerfile as the target: `Reopen in Container` @@ -56,38 +49,28 @@ docker build . -t libxmtp:1 libxmtp/ -├ [`bindings_ffi`](./bindings_ffi): FFI bindings for Android and iOS (in -progress) +├ [`bindings_ffi`](./bindings_ffi): FFI bindings for Android and iOS (in progress) ├ [`bindings_wasm`](./bindings_wasm): Wasm bindings (in progress) ├ examples/ -   ├ [`android/xmtpv3_example`](./examples/android/xmtpv3_example): Example -Android app (in progress) +   ├ [`android/xmtpv3_example`](./examples/android/xmtpv3_example): Example Android app (in progress) -   └ [`cli`](./examples/cli): Example XMTP console client. Use the CLI to try -out sending double ratchet messages on the XMTP `dev` network. +   └ [`cli`](./examples/cli): Example XMTP console client. Use the CLI to try out sending double ratchet messages on the XMTP `dev` network. -├ [`xmtp_api_grpc`](./xmtp_api_grpc): API client for XMTP's gRPC API, using code -from `xmtp_proto` +├ [`xmtp_api_grpc`](./xmtp_api_grpc): API client for XMTP's gRPC API, using code from `xmtp_proto` -├ [`xmtp_api_grpc_gateway`](./xmtp_api_grpc_gateway): API client for XMTP's gRPC -Gateway API, using code from `xmtp_proto` (in progress) +├ [`xmtp_api_grpc_gateway`](./xmtp_api_grpc_gateway): API client for XMTP's gRPC Gateway API, using code from `xmtp_proto` (in progress) ├ [`xmtp_cryptography`](./xmtp_cryptography): Cryptographic operations -├ [`xmtp_mls`](./xmtp_mls): Version 3 of XMTP which implements -[Messaging Layer Security](https://messaginglayersecurity.rocks/). +├ [`xmtp_mls`](./xmtp_mls): Version 3 of XMTP which implements [Messaging Layer Security](https://messaginglayersecurity.rocks/). -├ [`xmtp_proto`](./xmtp_proto): Generated code for handling XMTP protocol -buffers +├ [`xmtp_proto`](./xmtp_proto): Generated code for handling XMTP protocol buffers -└ [`xmtp_v2`](./xmtp_v2): Version 2 of XMTP which uses a -[user key bundle](https://xmtp.org/docs/concepts/key-generation-and-usage) to -encrypt and exchange messages. +└ [`xmtp_v2`](./xmtp_v2): Version 2 of XMTP which uses a [user key bundle](https://xmtp.org/docs/concepts/key-generation-and-usage) to encrypt and exchange messages. -## XMTP MLS Rust SDK Benchmarks ### Run the benchmarks From fc8272890489c9aeccbaf03798cd4a02cc364fab Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Fri, 7 Jun 2024 16:35:08 -0400 Subject: [PATCH 35/41] make comment about identity generation better --- xmtp_mls/src/utils/bench.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index 5fee366fb..e5f442a56 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -164,8 +164,9 @@ async fn create_identities(n: usize, is_dev_network: bool) -> Vec { /// Create identities if they don't already exist. /// creates specified `identities` on the -/// local gRPC local/dev and saves them to the file, -/// `identities.generated`. Uses this file for subsequent runs. +/// gRPC local docker or development node and saves them to a file. +/// `identities.generated`/`dev-identities.generated`. Uses this file for subsequent runs if +/// node still has those identities. pub async fn create_identities_if_dont_exist( identities: usize, client: &TestClient, From bd8c3069fb6d5c920f887b6bb6b39da8906c9a04 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Mon, 10 Jun 2024 12:12:22 -0400 Subject: [PATCH 36/41] restore to_store --- xmtp_mls/src/identity_updates.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/xmtp_mls/src/identity_updates.rs b/xmtp_mls/src/identity_updates.rs index a768620ee..12647ffdb 100644 --- a/xmtp_mls/src/identity_updates.rs +++ b/xmtp_mls/src/identity_updates.rs @@ -342,20 +342,18 @@ pub async fn load_identity_updates( let updates = api_client.get_identity_updates_v2(filters).await?; - let to_store = tracing::trace_span!("store updates").in_scope(|| { - updates - .clone() - .into_iter() - .flat_map(|(inbox_id, updates)| { - updates.into_iter().map(move |update| StoredIdentityUpdate { - inbox_id: inbox_id.clone(), - sequence_id: update.sequence_id as i64, - server_timestamp_ns: update.server_timestamp_ns as i64, - payload: update.update.to_proto().encode_to_vec(), - }) + let to_store = updates + .clone() + .into_iter() + .flat_map(|(inbox_id, updates)| { + updates.into_iter().map(move |update| StoredIdentityUpdate { + inbox_id: inbox_id.clone(), + sequence_id: update.sequence_id as i64, + server_timestamp_ns: update.server_timestamp_ns as i64, + payload: update.update.to_proto().encode_to_vec(), }) - .collect::>() - }); + }) + .collect::>(); conn.insert_or_ignore_identity_updates(&to_store)?; Ok(updates) From d501f9ca2ab5dd222e761d2dd734d4b03c651895 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 12 Jun 2024 14:28:29 -0400 Subject: [PATCH 37/41] recursion limit --- bindings_ffi/src/lib.rs | 1 + 1 file changed, 1 insertion(+) mode change 100644 => 100755 bindings_ffi/src/lib.rs diff --git a/bindings_ffi/src/lib.rs b/bindings_ffi/src/lib.rs old mode 100644 new mode 100755 index 4702444bc..5df8d2918 --- a/bindings_ffi/src/lib.rs +++ b/bindings_ffi/src/lib.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "256"] pub mod inbox_owner; pub mod logger; pub mod mls; From 32d1b73dd34c3577e47a56706e924bb766506794 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 12 Jun 2024 14:39:03 -0400 Subject: [PATCH 38/41] recursion limit for node bindings --- bindings_node/Cargo.lock | 123 ++++++++++++++++++++++++++------------- bindings_node/src/lib.rs | 1 + 2 files changed, 82 insertions(+), 42 deletions(-) mode change 100644 => 100755 bindings_node/src/lib.rs diff --git a/bindings_node/Cargo.lock b/bindings_node/Cargo.lock index 62081d3de..6b6e19386 100644 --- a/bindings_node/Cargo.lock +++ b/bindings_node/Cargo.lock @@ -752,6 +752,41 @@ dependencies = [ "syn 2.0.64", ] +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.64", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.64", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -801,9 +836,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.1.6" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff236accb9a5069572099f0b350a92e9560e8e63a9b8d546162f4a5e03026bb2" +checksum = "35b696af9ff4c0d2a507db2c5faafa8aa0205e297e5f11e203a24226d5355e7a" dependencies = [ "diesel_derives", "libsqlite3-sys", @@ -813,11 +848,12 @@ dependencies = [ [[package]] name = "diesel_derives" -version = "2.1.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14701062d6bed917b5c7103bdffaee1e4609279e240488ad24e7bd979ca6866c" +checksum = "0d6fdd83d5947068817016e939596d246e5367279453f2a3433287894f2f2996" dependencies = [ "diesel_table_macro_syntax", + "dsl_auto_type", "proc-macro2", "quote", "syn 2.0.64", @@ -825,9 +861,9 @@ dependencies = [ [[package]] name = "diesel_migrations" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6036b3f0120c5961381b570ee20a02432d7e2d27ea60de9578799cf9156914ac" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" dependencies = [ "diesel", "migrations_internals", @@ -836,9 +872,9 @@ dependencies = [ [[package]] name = "diesel_table_macro_syntax" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ "syn 2.0.64", ] @@ -897,6 +933,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "dsl_auto_type" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab32c18ea6760d951659768a3e35ea72fc1ba0916d665a88dfe048b2a41e543f" +dependencies = [ + "darling", + "either", + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.64", +] + [[package]] name = "dunce" version = "1.0.4" @@ -1192,7 +1242,7 @@ dependencies = [ "serde", "serde_json", "syn 2.0.64", - "toml 0.8.13", + "toml", "walkdir", ] @@ -2037,6 +2087,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -2307,9 +2363,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.26.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326" +checksum = "0c10584274047cb335c23d3e61bcef8e323adae7c5c8c760540f73610177fc3f" dependencies = [ "cc", "openssl-sys", @@ -2363,19 +2419,19 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "migrations_internals" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f23f71580015254b020e856feac3df5878c2c7a8812297edd6c0a485ac9dada" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" dependencies = [ "serde", - "toml 0.7.8", + "toml", ] [[package]] name = "migrations_macros" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce3325ac70e67bbab5bd837a31cae01f1a6db64e0e744a33cb03a543469ef08" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" dependencies = [ "migrations_internals", "proc-macro2", @@ -4196,6 +4252,12 @@ dependencies = [ "precomputed-hash", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.26.2" @@ -4547,18 +4609,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.13" @@ -4580,19 +4630,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.2.6", - "serde", - "serde_spanned", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.21.1" @@ -5317,6 +5354,7 @@ dependencies = [ "tokio", "tonic", "tower", + "tracing", "webpki-roots 0.23.1", "xmtp_proto", "xmtp_v2", @@ -5404,7 +5442,8 @@ dependencies = [ "thiserror", "tls_codec 0.4.1", "tokio", - "toml 0.8.13", + "toml", + "tracing", "xmtp_cryptography", "xmtp_id", "xmtp_proto", diff --git a/bindings_node/src/lib.rs b/bindings_node/src/lib.rs old mode 100644 new mode 100755 index 4a27be979..3084a43f8 --- a/bindings_node/src/lib.rs +++ b/bindings_node/src/lib.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "256"] mod conversations; pub mod encoded_content; mod groups; From 76687d14b76d4465e71259d16014fdc09ff71a0e Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 12 Jun 2024 14:45:55 -0400 Subject: [PATCH 39/41] disable trace level for release builds (disable instrumentation in release) --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e40ab23e0..c749b407a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ ethers-core = "2.0.4" futures = "0.3.30" futures-core = "0.3.30" hex = "0.4.3" -log = "0.4" +log = { "0.4", features = ["release_max_level_debug"] } openmls = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } openmls_basic_credential = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } openmls_rust_crypto = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } @@ -52,7 +52,7 @@ thiserror = "1.0" tls_codec = "0.4.0" tokio = { version = "1.35.1", features = ["macros"] } tonic = "^0.11" -tracing = "0.1" +tracing = { version = "0.1", features = ["release_max_level_debug"] } tracing-subscriber = "0.3" url = "2.5.0" From bc197c269bc89d18d20287d6fc988d358da245d9 Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 12 Jun 2024 14:48:20 -0400 Subject: [PATCH 40/41] all the recursion limits --- Cargo.toml | 2 +- examples/cli/cli-client.rs | 1 + xmtp_mls/benches/group_limit.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) mode change 100644 => 100755 examples/cli/cli-client.rs mode change 100644 => 100755 xmtp_mls/benches/group_limit.rs diff --git a/Cargo.toml b/Cargo.toml index c749b407a..c6130f81c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ ethers-core = "2.0.4" futures = "0.3.30" futures-core = "0.3.30" hex = "0.4.3" -log = { "0.4", features = ["release_max_level_debug"] } +log = { version = "0.4", features = ["release_max_level_debug"] } openmls = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } openmls_basic_credential = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } openmls_rust_crypto = { git = "https://github.com/xmtp/openmls", rev = "99b2d5e7d0e034ac57644395e2194c5a102afb9a" } diff --git a/examples/cli/cli-client.rs b/examples/cli/cli-client.rs old mode 100644 new mode 100755 index 8b88139da..47dc1eaa4 --- a/examples/cli/cli-client.rs +++ b/examples/cli/cli-client.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "256"] /* XLI is a Commandline client using XMTPv3. */ diff --git a/xmtp_mls/benches/group_limit.rs b/xmtp_mls/benches/group_limit.rs old mode 100644 new mode 100755 index 3d62a5544..39a7d15a0 --- a/xmtp_mls/benches/group_limit.rs +++ b/xmtp_mls/benches/group_limit.rs @@ -1,3 +1,4 @@ +#![recursion_limit = "256"] //! Benchmarks for group limit //! using `RUST_LOG=trace` will additionally output a `tracing.folded` file, which //! may be used to generate a flamegraph of execution from tracing logs. From 8f42fb640487e4a84f44cc4b701a2dbc0540d97b Mon Sep 17 00:00:00 2001 From: Andrew Plaza Date: Wed, 12 Jun 2024 14:57:06 -0400 Subject: [PATCH 41/41] make hpke private --- xmtp_mls/benches/crypto.rs | 2 +- xmtp_mls/src/lib.rs | 2 +- xmtp_mls/src/utils/bench.rs | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/xmtp_mls/benches/crypto.rs b/xmtp_mls/benches/crypto.rs index 4c05f575e..17306e01b 100644 --- a/xmtp_mls/benches/crypto.rs +++ b/xmtp_mls/benches/crypto.rs @@ -3,7 +3,7 @@ use openmls_rust_crypto::RustCrypto; use openmls_traits::{crypto::OpenMlsCrypto, random::OpenMlsRand}; use rand::{rngs::OsRng, RngCore}; use xmtp_mls::configuration::CIPHERSUITE; -use xmtp_mls::hpke::encrypt_welcome; +use xmtp_mls::utils::bench::re_export::encrypt_welcome; fn bench_encrypt_welcome(c: &mut Criterion) { let sizes = [ diff --git a/xmtp_mls/src/lib.rs b/xmtp_mls/src/lib.rs index 3c4389089..e7bded9b7 100644 --- a/xmtp_mls/src/lib.rs +++ b/xmtp_mls/src/lib.rs @@ -5,7 +5,7 @@ pub mod codecs; pub mod configuration; pub mod credential; pub mod groups; -pub mod hpke; +mod hpke; pub mod identity; mod identity_updates; pub mod owner; diff --git a/xmtp_mls/src/utils/bench.rs b/xmtp_mls/src/utils/bench.rs index e5f442a56..101f0ab6b 100644 --- a/xmtp_mls/src/utils/bench.rs +++ b/xmtp_mls/src/utils/bench.rs @@ -22,6 +22,11 @@ use super::test::TestClient; pub const BENCH_ROOT_SPAN: &str = "xmtp-trace-bench"; +/// Re-export of functions in private modules for benchmarks +pub mod re_export { + pub use crate::hpke::encrypt_welcome; +} + #[derive(Debug, Error)] pub enum BenchError { #[error(transparent)]