パフォーマンスに関する考慮事項
Overview
このガイドでは、Rust ドライバーのパフォーマンスを最適化する方法を学習できます。 MongoDB に接続するには、 Client
インスタンスを作成する必要があります。 Client
インスタンスは、サーバー トポロジーの検出、接続のモニタリング、内部接続プールの維持など、接続のほとんどの要素を自動的に処理します。 このガイドでは、 Client
インスタンスの構成と使用に関するベストプラクティスについて説明します。
このガイドには、次のセクションが含まれています。
クライアント ライフサイクルでは、
Client
インスタンスの作成と管理に関するベストプラクティスについて説明します。接続プールは、ドライバーでの接続プーリングの仕組みを説明します
並列処理では、タスクを並行して実行するためのサンプルコードが提供されます
Runtimeでは、
tokio
作成とasync_std
作成の機能を使用してランタイムを管理する方法について説明しています。追加情報では、このガイドで言及されている型とメソッドのリソースとAPIドキュメントへのリンクを提供します
クライアントのライフサイクル
クライアントは複数のセッションやオペレーションで再利用することをお勧めします。 同じClient
インスタンスを使用して複数のタスクを実行できます。 Client
型は複数のスレッドで安全に同時使用できます。 リクエストごとに新しいClient
インスタンスを作成すると、パフォーマンスが低下します。
次のコードでは、既存のClient
インスタンスへのポインターを受け入れるメソッドが作成されます。これにより、同じクライアントを使用して多くのリクエストを実行できます。
// ... Create a client earlier in your code async fn make_request(client: &Client) -> Result<(), Box<dyn Error>> { // Use the client to perform operations Ok(()) }
接続プール
すべてのClient
インスタンスには、MongoDB トポロジー内の各サーバーに対する接続プールが組み込まれています。 接続プールはオンデマンドでソケットを開き、アプリケーション内の MongoDB への同時要求をサポートします。
Client
のデフォルト構成はほとんどのアプリケーションで動作します。 次のコードは、デフォルトの接続設定を持つクライアントを作成する方法を示しています。
let client = Client::with_uri_str("<connection string>").await?;
あるいは、アプリケーションのニーズに合わせて接続プールを調整し、パフォーマンスを最適化することもできます。 接続設定をカスタマイズする方法の詳細については、このガイドの次のサブセクションを参照してください。
Tip
接続プールの構成の詳細については、サーバー マニュアルの「 接続プール設定の調整 」を参照してください。
最大プール サイズの設定
各接続プールの最大サイズはmax_pool_size
オプションによって設定され、デフォルトは10
になります。 サーバーへの使用中の接続数がmax_pool_size
の値に達した場合、そのサーバーへの次のリクエストは接続が利用可能になるまで待機します。
アプリケーションのリクエストをサポートするために必要なソケットに加えて、各Client
インスタンスは、サーバーの状態を監視するために MongoDB トポロジー内のサーバーごとにさらに 2 つのソケットを開きます。 たとえば、3 ノードのレプリカセットに接続されたクライアントは、6 つの監視ソケットを開きます。 アプリケーションがmax_pool_size
のデフォルト設定を使用し、プライマリ(デフォルト)ノードのみをクエリする場合、接続プールには最大 16 個の接続を存在できます。 アプリケーションが 読み込み設定 (read preference) を使用してセカンダリ ノードをクエリすると、それらの接続プールは増大し、合計接続数は 36 になります。
1 つのプロセス内で多数の同時 MongoDB リクエストをサポートするには、 max_pool_size
オプションの値を増やすことができます。 次のコードは、 Client
をインスタンス化するときにmax_pool_size
の値を指定する方法を示しています。
let mut client_options = ClientOptions::parse("<connection string>").await?; client_options.max_pool_size = Some(20); let client = Client::with_options(client_options)?;
同時接続オプションの構成
接続プールにはレート制限があります。 max_connecting
オプションによって、プールが並行して作成できる接続数が決定されます。 たとえば、 max_connecting
の値がデフォルト値である2
の場合、接続を同時にチェックアウトしようとする 3 番目のリクエストは、次のいずれかの場合にのみ成功します。
接続プールは接続の作成を完了し、プール内の接続数は
max_pool_size
の値以下になります。既存の接続がプールにチェックバックされます。
接続作成のレート制限により、既存の接続を再利用するドライバーの能力が向上します。
min_pool_size
オプションを使用して、各サーバーへの同時接続の最小数を設定できます。デフォルトは0
になります。 ドライバーは、このソケット数で接続プールを初期化します。 ソケットが閉じられ、使用中とアイドル状態の両方を含むソケットの合計数が最小値を下回ると、最小値に達するまで接続プールはさらにソケットを開きます。
max_connecting
次のコードでは、min_pool_size
をインスタンス化するときに オプションとClient
オプションを設定します。
let mut client_options = ClientOptions::parse("<connection string>").await?; client_options.max_connecting = Some(3); client_options.min_pool_size = Some(1); let client = Client::with_options(client_options)?;
最大アイドル時間の設定
max_idle_time
オプションを設定することで、プール内で接続がアイドル状態を維持できる最大時間を設定できます。 max_idle_time
で指定された期間、接続がアイドル状態になると、接続プールはその接続を削除し、置き換えます。 このオプションのデフォルトは0
であるか、制限なしです。
アプリケーション内の任意の点でClient::shutdown()
メソッドが呼び出されると、ドライバーはすべてのアイドル ソケットを閉じ、プールに返されるときに使用されているすべてのソケットを閉じます。 Client::shutdown()
を呼び出すと非アクティブなソケットのみが閉じられるため、このメソッドを使用して実行中の操作を中断したり終了したりすることはできません。 ドライバーはこれらのソケットを、プロセスが完了した場合にのみ閉じます。
次のコードは、 Client
をインスタンス化するときに、 max_idle_time
オプションの値を90
秒に設定します。
let mut client_options = ClientOptions::parse("<connection string>").await?; client_options.max_idle_time = Some(Duration::new(90, 0)); let client = Client::with_options(client_options)?;
並列処理
並列データ操作を実行できる場合は、非同期の同時実行タスクを実行することでパフォーマンスを最適化できます。 次のコードでは、 tokio::task
モジュールのspawn()
メソッドを使用して、挿入操作を実行するための個別の同時実行タスクを作成します。
let client = Client::with_uri_str("<connection string>").await?; let data = doc! { "title": "1984", "author": "George Orwell" }; for i in 0..5 { let client_ref = client.clone(); let data_ref = data.clone(); task::spawn(async move { let collection = client_ref .database("items") .collection::<Document>(&format!("coll{}", i)); collection.insert_one(data_ref).await }); }
ランタイム
Client
インスタンスは、それを作成したtokio
またはasync-std
ランタイムのインスタンスにバインドされます。 Client
インスタンスを使用して別のランタイムで操作を実行すると、予期しない動作や失敗が発生する可能性があります。
tokio
またはasync_std
のtest
ヘルパー マイクロを使用してアプリケーションをテストすると、意図したランタイムとは異なるランタイムで操作を実行する可能性があります。 これは、これらのヘルパー マイクロがテストごとに新しいランタイムを作成するためです。 ただし、この問題を回避するには、次のいずれかの戦略を使用できます。
test
ヘルパー マイクロを使用せずに、ランタイムをClient
インスタンスにアタッチします。async
テストごとに新しいClient
インスタンスを作成します。
この例では、最初の戦略に従い、テスト専用のグローバル ランタイムを作成しています。 次のコードでは、 test_list_dbs()
メソッドは、このランタイムに手動で接続するクライアントを使用して配置内のデータベースを一覧表示します。
use tokio::runtime::Runtime; use once_cell::sync::Lazy; static CLIENT_RUNTIME: Lazy<(Client, Runtime)> = Lazy::new(|| { let rt = Runtime::new().unwrap(); let client = rt.block_on(async { Client::with_uri_str("<connection string>").await.unwrap() }); (client, rt) }); fn test_list_dbs() -> Result<(), Box<dyn Error>> { let (client, rt) = &*CLIENT_RUNTIME; rt.block_on(async { client.list_database_names().await })?; Ok(()) }
2 つ目の戦略を実装することで、次のコードはtokio::test
を使用して実行されるテストごとに新しいClient
インスタンスを作成し、ランタイム間で意図しない相互作用が存在しないようにします。
async fn test_list_dbs() -> Result<(), Box<dyn Error>> { let client = Client::with_uri_str("<connection string>").await?; client.list_database_names().await?; Ok(()) }
詳細情報
MongoDB への接続の詳細については、「 接続ガイド 」を参照してください。
Rust ドライバーで利用可能なランタイムの詳細については、「非同期 API と同期 API に関するガイド 」を参照してください。
API ドキュメント
spawn()
tokio::task
モジュール内tokio::runtime module