עבודה עם פעולות ממושכות

לפעמים, יכול להיות ש-API יחשוף שיטה שלוקח לה הרבה זמן להסתיים. במקום לחסום בזמן שהמשימה פועלת, אפשר להחזיר הבטחה ולאפשר למשתמש לבדוק את הסטטוס.

Google Cloud ספריות הלקוח של Rust מספקות כלים לעבודה עם פעולות ממושכות (LRO). במדריך הזה מוסבר איך להתחיל פעולות 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

התחלת פעולה ממושכת

בדוגמה הזו נשתמש ב-rename folder. הפעולה הזו עשויה להימשך זמן רב בתיקיות גדולות, אבל היא מהירה יחסית בתיקיות קטנות יותר.

כדי להתחיל פעולה ממושכת, צריך לאתחל לקוח ולבצע את ה-RPC.

קודם כול, מוסיפים הצהרות של use כדי להימנע משמות ארוכים של חבילות:

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

אחר כך יוצרים את הלקוח:

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

בספריות הלקוח של Rust, כל בקשה מיוצגת על ידי שיטה שמחזירה כלי ליצירת בקשות. מפעילים את השיטה (method) בלקוח כדי ליצור את הכלי ליצירת בקשות:

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. ה-Operation הזה משמש כהבטחה לתוצאה של הבקשה ארוכת הטווח:

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

הבקשה הזו מתחילה את הפעולה ברקע. כדי לדעת אם הפעולה הצליחה, צריך לחכות עד שהיא תסתיים.

דגימה אוטומטית של פעולה ממושכת

כדי להגדיר סקר אוטומטי, צריך להתחיל פעולה ארוכת טווח עם Poller במקום .send().wait, כמו בדוגמה הבאה:

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

קודם כל, צריך להציג את המאפיין Poller בהיקף באמצעות הצהרת use:

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() נוחה, אבל היא לא כוללת דוחות התקדמות חלקיים של פעולות ממושכות. אם האפליקציה שלכם דורשת את המידע הזה, אתם יכולים להשתמש ישירות ב-poller:

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

אחר כך משתמשים ב-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:?}"))
        }

סוג השגיאה הוא סוג ההודעה Status. המאפיין הזה לא מיישם את מאפיין התקן Error. אפשר להמיר אותו באופן ידני לשגיאה תקינה באמצעות Error::service.

אם התוצאה היא הצלחה, מחלצים את סוג התגובה. אפשר למצוא את הסוג הזה במאמרי העזרה של שיטת ה-LRO או במאמרי העזרה של ה-API של השירות:

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

שימו לב: יכול להיות שהחילוץ של הערך ייכשל אם הסוג לא תואם למה שהשירות שלח.

יכול להיות שבעתיד נוסיף שדות וענפים לסוגים שלGoogle Cloud . בספריות הלקוח של 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.