Membatalkan penulisan objek Cloud Storage dengan error

Klien Cloud Storage di Library Klien Cloud untuk Rust menulis objek dengan menarik potongan payload dari jenis yang mengimplementasikan trait StreamingSource. Jika aplikasi Anda mendeteksi kondisi error saat membuat atau melakukan streaming data, menampilkan error dari implementasi StreamingSource akan menghentikan library klien untuk menyelesaikan pembuatan objek.

Dokumen ini menunjukkan cara menentukan jenis error kustom dan menerapkan StreamingSource untuk menampilkan error tersebut di tengah streaming. Anda juga memverifikasi bahwa upload yang gagal menampilkan error tanpa membuat objek. Jika Anda baru menggunakan library klien, lihat Menggunakan Cloud Storage dengan Rust terlebih dahulu.

Sebelum memulai

Sebelum menjalankan kode dalam dokumen ini, selesaikan prasyarat berikut:

Dependensi

Tambahkan library klien Cloud Storage dan crate pendukung ke file Cargo.toml Anda:

cargo add google-cloud-storage anyhow bytes

Membuat jenis error kustom

Untuk membatalkan penulisan objek sebelum finalisasi, implementasi StreamingSource Anda harus menampilkan error yang mengimplementasikan std::error::Error + Send + Sync + 'static. Anda dapat menggunakan jenis error yang ada yang memenuhi batasan ini atau menentukan enum kustom untuk aplikasi Anda. Selesaikan langkah-langkah berikut untuk menentukan enum error kustom:

  1. Tentukan enum error kustom (MyError):

    #[derive(Debug)]
    pub enum MyError {
        ExpectedProblem,
        OhNoes,
    }

  2. Terapkan trait Error standar agar library klien dapat mempertahankan error Anda sebagai penyebab utamanya:

    impl std::error::Error for MyError {}

  3. Terapkan trait Display yang diperlukan oleh std::error::Error:

    impl std::fmt::Display for MyError {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            match self {
                Self::ExpectedProblem => write!(f, "this kind of thing happens"),
                Self::OhNoes => write!(f, "oh noes! something terrible happened"),
            }
        }
    }

Mengimplementasikan sumber streaming kustom

Selanjutnya, buat jenis yang menghasilkan potongan sintetis dan menampilkan error setelah 41 potongan sehingga upload berhenti sebelum selesai. Selesaikan langkah-langkah berikut:

  1. Tentukan struct untuk melacak penghitung potongan:

    #[derive(Debug, Default)]
    struct MySource(u32);

  2. Terapkan StreamingSource untuk MySource, yang mengaitkan type Error = MyError dan menampilkan Some(Err(MyError::ExpectedProblem)) pada iterasi 42:

    impl google_cloud_storage::streaming_source::StreamingSource for MySource {
        type Error = MyError;
        async fn next(&mut self) -> Option<Result<bytes::Bytes, Self::Error>> {
            self.0 += 1;
            match self.0 {
                42 => Some(Err(MyError::ExpectedProblem)),
                n if n > 42 => None,
                n => Some(Ok(bytes::Bytes::from_owner(format!(
                    "test data for the example {n}\n"
                )))),
            }
        }
    }

Saat next menampilkan Some(Err(...)), library klien akan berhenti membaca dari sumber dan membatalkan permintaan tulis tanpa menyelesaikan objek.

Mencoba upload dan memeriksa error

Setelah menerapkan StreamingSource, coba tulis objek yang di-buffer dan verifikasi bahwa klien menampilkan error serialisasi yang menggabungkan MyError. Karena MySource adalah sumber single-pass yang tidak mengimplementasikan Seek, mulai penulisan dengan send_buffered, bukan send_unbuffered.

Di dalam fungsi async yang menerima bucket_name dalam format projects/_/buckets/BUCKET_ID, selesaikan langkah-langkah berikut:

  1. Lakukan inisialisasi klien Storage:

    pub async fn attempt_upload(bucket_name: &str) -> anyhow::Result<()> {
        use google_cloud_storage::client::Storage;
        let client = Storage::builder().build().await?;

  2. Mulai penulisan objek yang di-buffer menggunakan MySource::default() sebagai sumber payload:

    let upload = client
        .write_object(bucket_name, "expect-error", MySource::default())
        .send_buffered()
        .await;

  3. Pastikan upload gagal dengan error serialisasi yang error sumbernya adalah MyError:

    println!("Upload result {upload:?}");
    let err = upload.expect_err("the source is supposed to terminate the upload");
    assert!(err.is_serialization(), "{err:?}");
    use std::error::Error as _;
    assert!(err.source().is_some_and(|e| e.is::<MyError>()), "{err:?}");

  4. Tampilkan Ok(()) untuk menyelesaikan fungsi:

        Ok(())
    }

Saat Anda menjalankan attempt_upload, pesan Upload result Err(...) yang berisi MyError::ExpectedProblem akan dicetak. Pernyataan tersebut memverifikasi bahwa klien menghentikan upload dan menampilkan error serialisasi yang menggabungkan MyError. Karena operasi tulis dibatalkan sebelum finalisasi, Cloud Storage tidak membuat objek expect-error di bucket.

Pembersihan

Meskipun penulisan yang dibatalkan tidak membuat objek, hapus objek pengujian apa pun yang Anda buat untuk dokumen ini. Menghapus resource pengujian setelah selesai akan menghindari tagihan berkelanjutan.

Kode lengkap

Contoh berikut menggabungkan semua langkah sebelumnya menjadi modul lengkap:

#[derive(Debug)]
pub enum MyError {
    ExpectedProblem,
    OhNoes,
}

impl std::error::Error for MyError {}

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ExpectedProblem => write!(f, "this kind of thing happens"),
            Self::OhNoes => write!(f, "oh noes! something terrible happened"),
        }
    }
}

#[derive(Debug, Default)]
struct MySource(u32);

impl google_cloud_storage::streaming_source::StreamingSource for MySource {
    type Error = MyError;
    async fn next(&mut self) -> Option<Result<bytes::Bytes, Self::Error>> {
        self.0 += 1;
        match self.0 {
            42 => Some(Err(MyError::ExpectedProblem)),
            n if n > 42 => None,
            n => Some(Ok(bytes::Bytes::from_owner(format!(
                "test data for the example {n}\n"
            )))),
        }
    }
}

pub async fn attempt_upload(bucket_name: &str) -> anyhow::Result<()> {
    use google_cloud_storage::client::Storage;
    let client = Storage::builder().build().await?;
    let upload = client
        .write_object(bucket_name, "expect-error", MySource::default())
        .send_buffered()
        .await;
    println!("Upload result {upload:?}");
    let err = upload.expect_err("the source is supposed to terminate the upload");
    assert!(err.is_serialization(), "{err:?}");
    use std::error::Error as _;
    assert!(err.source().is_some_and(|e| e.is::<MyError>()), "{err:?}");
    Ok(())
}

Langkah berikutnya