Utilizzo delle operazioni a lunga esecuzione

A volte, un'API potrebbe esporre un metodo il cui completamento richiede una quantità di tempo significativa. Anziché bloccare l'esecuzione dell'attività, puoi restituire una promessa e consentire all'utente di controllare lo stato.

Le Google Cloud librerie client per Rust forniscono helper per lavorare con queste operazioni a lunga esecuzione (LRO). Questa guida mostra come avviare le LRO e attendere il loro completamento.

Prerequisiti

Questa guida utilizza il servizio Cloud Storage per mantenere concreti gli snippet di codice. Questi concetti si applicano a qualsiasi altro servizio che utilizza le LRO.

Prima di seguire questa guida, devi:

Per istruzioni complete sulla configurazione delle librerie Rust, consulta la sezione Configurare l'ambiente di sviluppo.

Dipendenze

Dichiara Google Cloud le dipendenze nel tuo Cargo.toml file:

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

Hai anche bisogno di diverse funzionalità tokio:

cargo add tokio --features full,macros

Avviare un'operazione a lunga esecuzione

Questo esempio utilizza la ridenominazione della cartella. Questa operazione può richiedere molto tempo per le cartelle di grandi dimensioni, ma è relativamente veloce per le cartelle più piccole.

Per avviare un'operazione a lunga esecuzione, devi inizializzare un client ed eseguire la RPC.

Innanzitutto, aggiungi le dichiarazioni use per evitare nomi di pacchetti lunghi:

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

Poi, crea il client:

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

Nelle librerie client Rust, ogni richiesta è rappresentata da un metodo che restituisce un generatore di richieste. Chiama il metodo sul client per creare il generatore di richieste:

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

Le funzioni di esempio accettano i nomi del bucket e della cartella come argomenti:

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

Invia la richiesta e attendi la restituzione di un'Operazione che venga restituita. Questa Operation funge da promessa per il risultato della richiesta a lunga esecuzione:

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

Questa richiesta avvia l'operazione in background. Attendi il completamento dell'operazione per determinare se è andata a buon fine.

Eseguire automaticamente il polling di un'operazione a lunga esecuzione

Per configurare il polling automatico, avvia un'operazione a lunga esecuzione con un Poller anziché .send().wait, come segue:

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

Innanzitutto, introduci il tratto Poller nell'ambito utilizzando una dichiarazione use:

use google_cloud_lro::Poller;

Poi, inizializza il client e prepara la richiesta come prima:

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

Esegui il polling fino al completamento dell'operazione e stampa il risultato:

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

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

Eseguire il polling di un'operazione a lunga esecuzione con risultati intermedi

Il metodo .until_done() è pratico, ma omette i report di avanzamento parziale delle operazioni a lunga esecuzione. Se la tua applicazione richiede queste informazioni, utilizza direttamente il poller:

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

Poi usa il poller in un loop:

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;
}

Questo loop attende esplicitamente prima di eseguire di nuovo il polling. Il periodo di polling dipende dall'operazione specifica e dal relativo payload. Consulta la documentazione del servizio o sperimenta con i tuoi dati per determinare un valore valido.

Eseguire manualmente il polling di un'operazione a lunga esecuzione

Sebbene consigliamo approcci di polling automatico, è anche possibile eseguire manualmente il polling di un'operazione a lunga esecuzione. Per saperne di più, consulta la documentazione di riferimento del messaggio Operation.

Avvia l'operazione a lunga esecuzione utilizzando il client:

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

Avvia un loop di polling e controlla se l'operazione è stata completata utilizzando il campo done:

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

Al termine dell'operazione, in genere contiene un risultato. Il campo del risultato è facoltativo perché il servizio potrebbe restituire done come true senza un risultato. Ad esempio, un'operazione di eliminazione riuscita non ha un valore restituito. In questo esempio, il servizio Cloud Storage restituisce sempre un valore:

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

Un'operazione avviata potrebbe non essere completata correttamente. Il risultato può essere un errore o una risposta valida. Controlla prima la presenza di errori:

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

Il tipo di errore è un tipo di messaggio Status. Questo NON implementa il tratto Error standard. Convertilo manualmente in un errore valido utilizzando Error::service.

Se il risultato è positivo, estrai il tipo di risposta. Trova questo tipo nella documentazione del metodo LRO o nella documentazione dell'API del servizio:

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

Tieni presente che l'estrazione del valore potrebbe non riuscire se il tipo non corrisponde a quello inviato dal servizio.

Google Cloud I tipi potrebbero aggiungere campi e rami in futuro. Le Google Cloud librerie client per Rust contrassegnano tutti gli struct e gli enum come #[non_exhaustive]. Gestisci questo caso:

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

Se l'operazione non è stata completata, potrebbe contenere metadati. Alcuni servizi includono informazioni iniziali sulla richiesta, mentre altri includono report di avanzamento parziale. Puoi estrarre e segnalare questi metadati:

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

Attendi prima di eseguire di nuovo il polling. Valuta la possibilità di modificare il periodo di polling utilizzando il backoff esponenziale troncato. Questo esempio esegue il polling ogni 500 ms:

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

Esegui una query sullo stato dell'operazione:

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

Per semplicità, questo esempio ignora tutti gli errori. Nella tua applicazione, puoi trattare un sottoinsieme di errori come non recuperabili e limitare il numero di tentativi di polling.

Passaggi successivi

  • Visualizza il codice sorgente degli esempi su GitHub.