長時間実行オペレーションによる作業

API には、完了までにかなりの時間がかかるメソッドが用意されている場合があります。タスクの実行中にブロックするのではなく、Promise を返して、ユーザーがステータスを確認できるようにします。

Client Libraries for Rust には、長時間実行オペレーション(LRO)を操作するためのヘルパーが用意されています。 Google Cloud このガイドでは、LRO を開始して完了を待つ方法について説明します。

前提条件

このガイドでは、コード スニペットを具体的に示すために Cloud Storage サービスを使用します。 これらのコンセプトは、LRO を使用する他のサービスにも適用されます。

このガイドに沿って操作する前に、次のことを行う必要があります。

Rust ライブラリの完全な設定手順については、 開発環境をセットアップするをご覧ください。

依存関係

Cargo.toml ファイルで依存関係を宣言します。 Google Cloud

cargo add google-cloud-storage google-cloud-lro google-cloud-longrunning

また、いくつかの tokio 機能も必要です。

cargo add tokio --features full,macros

長時間実行オペレーションを開始する

この例では、フォルダの名前を変更します。このオペレーションは、大きなフォルダでは時間がかかることがありますが、小さなフォルダでは比較的短時間で完了します。

長時間実行オペレーションを開始するには、クライアントを初期化して RPC を行う必要があります。

まず、パッケージ名が長くなるのを避けるために、use 宣言を追加します。

use anyhow::anyhow;
use google_cloud_longrunning as longrunning;
use google_cloud_storage::client::StorageControl;

次に、クライアントを作成します。

let client = StorageControl::builder().build().await?;

Rust クライアント ライブラリでは、各リクエストはリクエスト ビルダーを返すメソッドで表されます。クライアントでメソッドを呼び出して、リクエスト ビルダーを作成します。

let operation = client
    .rename_folder()
    .set_name(format!("projects/_/buckets/{bucket}/folders/{folder}"))
    .set_destination_folder_id(dest)

サンプル関数は、バケット名とフォルダ名を引数として受け取ります。

pub async fn manual(bucket: &str, folder: &str, dest: &str) -> anyhow::Result<()> {

リクエストを行い、オペレーションが 返されるまで待ちます。この Operation は、長時間実行リクエストの結果の Promise として機能します。

    let operation =
        // ...
        .send()
        .await?;

このリクエストにより、オペレーションがバックグラウンドで開始されます。オペレーションが完了するまで待って、成功したかどうかを確認します。

長時間実行オペレーションを自動的にポーリングする

自動ポーリングを構成するには、次のように 長時間実行オペレーションを 使用してPollerを開始します。.send().wait

.poller()
.until_done()
.await?;

まず、use 宣言を使用して、スコープに Poller トレイトを導入します。

use google_cloud_lro::Poller;

次に、前と同じようにクライアントを初期化してリクエストを準備します。

let response = client
    .rename_folder()
    .set_name(format!("projects/_/buckets/{bucket}/folders/{folder}"))
    .set_destination_folder_id(dest)

オペレーションが完了するまでポーリングし、結果を出力します。

    .poller()
    .until_done()
    .await?;

println!("LRO completed, response={response:?}");

中間結果を使用して長時間実行オペレーションをポーリングする

.until_done() メソッドは便利ですが、長時間実行オペレーションの部分的な進行状況レポートは省略されます。アプリケーションでこの情報が必要な場合は、ポーリング処理を直接使用します。

    let mut poller = client
        .rename_folder()
        /* more stuff */
        .poller();

次に、ループでポーリング処理を使用します。

while let Some(p) = poller.poll().await {
    match p {
        PollingResult::Completed(r) => {
            println!("LRO completed, response={r:?}");
        }
        PollingResult::InProgress(m) => {
            println!("LRO in progress, metadata={m:?}");
        }
        PollingResult::PollingError(e) => {
            println!("Transient error polling the LRO: {e}");
        }
    }
    tokio::time::sleep(std::time::Duration::from_millis(500)).await;
}

このループでは、再度ポーリングする前に明示的に待機します。ポーリング期間は、特定のオペレーションとそのペイロードによって異なります。適切な値を判断するには、サービスのドキュメントを参照するか、データでテストしてください。

長時間実行オペレーションを手動でポーリングする

自動ポーリング アプローチをおすすめしますが、長時間実行オペレーションを手動でポーリングすることもできます。詳細については、 オペレーション メッセージのリファレンス ドキュメントをご覧ください。

クライアントを使用して長時間実行オペレーションを開始します。

    let mut operation = client
        .rename_folder()
        /* more stuff */
        .send()
        .await?;

ポーリング ループを開始し、done フィールドを使用してオペレーションが完了したかどうかを確認します。

let response: anyhow::Result<Folder> = loop {
    if operation.done {

オペレーションが完了すると、通常は結果が含まれます。サービスが結果なしで done を true として返す可能性があるため、結果フィールドは省略可能です。たとえば、削除オペレーションが成功した場合、戻り値はありません。この例では、Cloud Storage サービスは常に値を返します。

match &operation.result {
    None => {
        break Err(anyhow!("missing result for finished operation"));
    }

開始されたオペレーションが正常に完了しない場合があります。結果はエラーまたは有効なレスポンスになります。まずエラーを確認します。

Some(r) => {
    break match r {
        longrunning::model::operation::Result::Error(s) => {
            Err(anyhow!("operation completed with error {s:?}"))
        }

エラータイプはステータス メッセージ タイプです。これは標準の Error トレイトを実装していません。Error::service を使用して、有効なエラーに手動で変換します。

結果が成功した場合は、レスポンス タイプを抽出します。このタイプは、LRO メソッドのドキュメントまたはサービス API ドキュメントに記載されています。

longrunning::model::operation::Result::Response(any) => {
    let response = any.to_msg::<Folder>()?;
    Ok(response)
}

タイプがサービスから送信されたものと一致しない場合、値の抽出に失敗する可能性があります。

Google Cloud タイプには、今後フィールドとブランチが追加される可能性があります。Client Libraries for Rust では、すべての構造体と列挙型が #[non_exhaustive] としてマークされます。 Google Cloudこのケースに対応するには:

_ => Err(anyhow!("unexpected result branch {r:?}")),

オペレーションが完了していない場合、メタデータが含まれている可能性があります。リクエストに関する初期情報を含むサービスもあれば、部分的な進行状況レポートを含むサービスもあります。このメタデータを抽出してレポートできます。

if let Some(any) = &operation.metadata {
    let metadata = any.to_msg::<RenameFolderMetadata>()?;
    println!("LRO in progress, metadata={metadata:?}");
}

再度ポーリングするまで待ちます。切り捨て型指数バックオフを使用してポーリング期間を調整することを検討してください。この例では、500 ミリ秒ごとにポーリングします。

tokio::time::sleep(std::time::Duration::from_millis(500)).await;

オペレーションのステータスをクエリします。

if let Ok(attempt) = client
    .get_operation()
    .set_name(&operation.name)
    .send()
    .await
{
    operation = attempt;
}

この例では、わかりやすくするためにすべてのエラーを無視しています。アプリケーションでは、エラーのサブセットを回復不能として扱い、ポーリング試行回数を制限できます。

次のステップ

  • GitHub で例のソースコードを確認する。