Bagikan melalui


Kegigihan biji-bijian

Biji-bijian dapat memiliki beberapa objek data persisten bernama yang terkait dengannya. Objek status ini dimuat dari penyimpanan selama aktivasi biji-bijian sehingga tersedia selama permintaan. Grain persistence menggunakan model plugin yang dapat diperluas sehingga penyedia penyimpanan untuk database apa pun dapat digunakan. Model persistensi ini dirancang untuk kesederhanaan, dan tidak dimaksudkan untuk mencakup semua pola akses data. Biji-bijian juga dapat mengakses database secara langsung, tanpa menggunakan model persistensi biji-bijian.

Pada diagram di atas, UserGrain memiliki status Profil dan status Cart , yang masing-masing disimpan dalam sistem penyimpanan terpisah.

Tujuan

  1. Beberapa objek data persisten bernama per butir.
  2. Beberapa penyedia penyimpanan yang dikonfigurasi, yang masing-masing dapat memiliki konfigurasi yang berbeda dan didukung oleh sistem penyimpanan yang berbeda.
  3. Storage dapat dikembangkan dan dipublikasikan oleh masyarakat.
  4. Storage penyedia memiliki kontrol penuh atas bagaimana mereka menyimpan data status biji-bijian di toko pendukung yang persisten. Corollary: Orleans tidak menyediakan solusi penyimpanan ORM yang komprehensif, tetapi memungkinkan penyedia penyimpanan khusus untuk mendukung persyaratan ORM tertentu saat dan bila diperlukan.

Paket

Penyedia penyimpanan biji-bijian Orleans dapat ditemukan di NuGet. Paket yang dipelihara secara resmi meliputi:

API

Biji-bijian berinteraksi dengan keadaan persisten mereka menggunakan IPersistentState<TState> di mana TState jenis status serializable:

public interface IPersistentState<TState> where TState : new()
{
    TState State { get; set; }
    string Etag { get; }
    Task ClearStateAsync();
    Task WriteStateAsync();
    Task ReadStateAsync();
}

IPersistentState<TState> Contoh disuntikkan ke dalam biji-bijian sebagai parameter konstruktor. Parameter ini dapat dianotasi dengan PersistentStateAttribute atribut untuk mengidentifikasi nama status yang disuntikkan dan nama penyedia penyimpanan yang menyediakannya. Contoh berikut menunjukkan hal ini dengan menyuntikkan dua keadaan bernama ke UserGrain dalam konstruktor:

public class UserGrain : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;
    private readonly IPersistentState<CartState> _cart;

    public UserGrain(
        [PersistentState("profile", "profileStore")] IPersistentState<ProfileState> profile,
        [PersistentState("cart", "cartStore")] IPersistentState<CartState> cart)
    {
        _profile = profile;
        _cart = cart;
    }
}

Jenis butir yang berbeda dapat menggunakan penyedia penyimpanan yang dikonfigurasi berbeda, bahkan jika keduanya adalah jenis yang sama; misalnya, dua instans penyedia Storage Azure Table yang berbeda, terhubung ke akun Azure Storage yang berbeda.

Baca status

Keadaan butir akan secara otomatis dibaca ketika biji-bijian diaktifkan, tetapi biji-bijian bertanggung jawab untuk secara eksplisit memicu penulisan untuk setiap keadaan butir yang berubah bila diperlukan.

Jika biji-bijian ingin secara eksplisit membaca kembali keadaan terbaru untuk biji-bijian ini dari toko pendukung, biji-bijian harus memanggil metode ini ReadStateAsync . Ini akan memuat ulang keadaan butir dari penyimpanan persisten melalui penyedia penyimpanan, dan salinan dalam memori sebelumnya dari keadaan butir akan ditimpa dan diganti ketika Task dari ReadStateAsync() selesai.

Nilai negara diakses menggunakan State properti. Misalnya, metode berikut mengakses status profil yang dinyatakan dalam kode di atas:

public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

Tidak perlu menelepon ReadStateAsync() selama operasi normal; status dimuat secara otomatis selama aktivasi. Namun, ReadStateAsync() dapat digunakan untuk menyegarkan keadaan yang dimodifikasi secara eksternal.

Lihat bagian Mode Kegagalan di bawah ini untuk detail mekanisme penanganan kesalahan.

Tulis status

Negara dapat dimodifikasi melalui State properti. Status yang dimodifikasi tidak secara otomatis dipertahankan. Sebaliknya, pengembang memutuskan kapan harus tetap menyatakan dengan WriteStateAsync memanggil metode. Misalnya, metode berikut memperbarui properti State dan mempertahankan status yang diperbarui:

public async Task SetNameAsync(string name)
{
    _profile.State.Name = name;
    await _profile.WriteStateAsync();
}

Secara konseptual, Orleans Runtime akan mengambil salinan mendalam dari objek data grain state untuk penggunaannya selama operasi tulis apa pun. Di bawah penutup, runtime dapat menggunakan aturan optimasi dan heuristik untuk menghindari melakukan beberapa atau semua salinan mendalam dalam beberapa keadaan, asalkan semantik isolasi logis yang diharapkan dipertahankan.

Lihat bagian Mode Kegagalan di bawah ini untuk detail mekanisme penanganan kesalahan.

Status yang jelas

Metode ini ClearStateAsync membersihkan keadaan biji-bijian dalam penyimpanan. Bergantung pada penyedia, operasi ini secara opsional dapat menghapus status butir sepenuhnya.

Mulai

Sebelum biji-bijian dapat menggunakan ketekunan, penyedia penyimpanan harus dikonfigurasi pada silo.

Pertama, konfigurasikan penyedia penyimpanan, satu untuk status profil dan satu untuk status keranjang:

var host = new HostBuilder()
    .UseOrleans(siloBuilder =>
    {
        siloBuilder.AddAzureTableGrainStorage(
            name: "profileStore",
            configureOptions: options =>
            {
                // Use JSON for serializing the state in storage
                options.UseJson = true;

                // Configure the storage connection key
                options.ConnectionString =
                    "DefaultEndpointsProtocol=https;AccountName=data1;AccountKey=SOMETHING1";
            })
            .AddAzureBlobGrainStorage(
                name: "cartStore",
                configureOptions: options =>
                {
                    // Use JSON for serializing the state in storage
                    options.UseJson = true;

                    // Configure the storage connection key
                    options.ConnectionString =
                        "DefaultEndpointsProtocol=https;AccountName=data2;AccountKey=SOMETHING2";
                });
    })
    .Build();

Sekarang penyedia penyimpanan telah dikonfigurasi dengan nama "profileStore", kita dapat mengakses penyedia ini dari biji-bijian.

Keadaan persisten dapat ditambahkan ke biji-bijian dalam dua cara utama:

  1. Dengan IPersistentState<TState> menyuntikkan ke konstruktor biji-bijian.
  2. Dengan mewarisi dari Grain<TGrainState>.

Cara yang disarankan untuk menambahkan penyimpanan ke biji-bijian adalah dengan IPersistentState<TState> menyuntikkan ke konstruktor biji-bijian dengan atribut terkait [PersistentState("stateName", "providerName")] . Untuk detailnya Grain<TState>, lihat di bawah ini. Ini masih didukung tetapi dianggap sebagai pendekatan warisan.

Nyatakan kelas untuk menahan keadaan biji-bijian kita:

[Serializable]
public class ProfileState
{
    public string Name { get; set; }

    public Date DateOfBirth
}

Suntikkan IPersistentState<ProfileState> ke konstruktor biji-bijian:

public class UserGrain : Grain, IUserGrain
{
    private readonly IPersistentState<ProfileState> _profile;

    public UserGrain(
        [PersistentState("profile", "profileStore")]
        IPersistentState<ProfileState> profile)
    {
        _profile = profile;
    }
}

Catatan

Status profil tidak akan dimuat pada saat disuntikkan ke konstruktor, sehingga mengaksesnya tidak valid pada saat itu. Negara akan dimuat sebelum OnActivateAsync dipanggil.

Sekarang biji-bijian memiliki keadaan persisten, kita dapat menambahkan metode untuk membaca dan menulis keadaan:

public class UserGrain : Grain, IUserGrain
    {
    private readonly IPersistentState<ProfileState> _profile;

    public UserGrain(
        [PersistentState("profile", "profileStore")]
        IPersistentState<ProfileState> profile)
    {
        _profile = profile;
    }

    public Task<string> GetNameAsync() => Task.FromResult(_profile.State.Name);

    public async Task SetNameAsync(string name)
    {
        _profile.State.Name = name;
        await _profile.WriteStateAsync();
    }
}

Mode kegagalan untuk operasi persistensi

Mode kegagalan untuk operasi baca

Kegagalan yang dikembalikan oleh penyedia penyimpanan selama pembacaan awal data negara untuk butir tertentu akan gagal operasi aktivasi untuk butir itu; dalam kasus seperti itu, tidak akan ada panggilan ke metode callback siklus hidup biji-bijian itu OnActivateAsync() . Permintaan asli untuk biji-bijian yang menyebabkan aktivasi akan disalahkan kembali ke penelepon, dengan cara yang sama seperti kegagalan lainnya selama aktivasi biji-bijian. Kegagalan yang dihadapi oleh penyedia penyimpanan saat membaca data negara untuk butir tertentu akan menghasilkan pengecualian dari ReadStateAsync()Task. Biji-bijian dapat memilih untuk menangani atau mengabaikan Task pengecualian, sama seperti yang lain Task di Orleans.

Setiap upaya untuk mengirim pesan ke butir yang gagal dimuat pada waktu startup silo karena konfigurasi penyedia penyimpanan yang hilang / buruk akan mengembalikan kesalahan BadProviderConfigExceptionpermanen .

Mode kegagalan untuk operasi tulis

Kegagalan yang dihadapi oleh penyedia penyimpanan saat menulis data status untuk butir tertentu akan menghasilkan pengecualian yang dilemparkan oleh WriteStateAsync()Task. Biasanya, ini berarti bahwa pengecualian panggilan biji-bijian akan dilemparkan kembali ke penelepon klien, asalkan dirantai WriteStateAsync()Task dengan benar ke dalam pengembalian Task akhir untuk metode biji-bijian ini. Namun, dimungkinkan dalam skenario lanjutan tertentu untuk menulis kode butir untuk secara khusus menangani kesalahan penulisan tersebut, sama seperti mereka dapat menangani kesalahan Tasklainnya .

Butir yang mengeksekusi kode penanganan kesalahan / pemulihan harus menangkap pengecualian / kesalahan WriteStateAsync()Tasks dan tidak melemparkannya kembali, untuk menandakan bahwa mereka telah berhasil menangani kesalahan tulis.

Rekomendasi

Gunakan serialisasi JSON atau format serialisasi toleran versi lainnya

Kode berkembang dan ini sering termasuk jenis penyimpanan juga. Untuk mengakomodasi perubahan ini, serializer yang sesuai harus dikonfigurasi. Untuk sebagian besar penyedia penyimpanan, UseJson opsi atau yang serupa tersedia untuk menggunakan JSON sebagai format serialisasi. Pastikan bahwa ketika kontrak data yang berkembang, data yang sudah disimpan akan tetap dapat dimuat.

Menggunakan GrainTState<> untuk menambahkan penyimpanan ke biji-bijian

Penting

Menggunakan Grain<T> untuk menambahkan penyimpanan ke biji-bijian dianggap sebagai fungsi warisan : penyimpanan biji-bijian harus ditambahkan menggunakan IPersistentState<T> seperti yang dijelaskan sebelumnya.

Kelas grain yang mewarisi dari Grain<T> (di mana T adalah jenis data status khusus aplikasi yang perlu dipertahankan) akan memiliki status mereka dimuat secara otomatis dari penyimpanan yang ditentukan.

Butir tersebut ditandai dengan a StorageProviderAttribute yang menentukan contoh bernama dari penyedia penyimpanan untuk digunakan untuk membaca / menulis data negara untuk biji-bijian ini.

[StorageProvider(ProviderName="store1")]
public class MyGrain : Grain<MyGrainState>, /*...*/
{
  /*...*/
}

Kelas Grain<T> dasar mendefinisikan metode berikut untuk memanggil subkelas:

protected virtual Task ReadStateAsync() { /*...*/ }
protected virtual Task WriteStateAsync() { /*...*/ }
protected virtual Task ClearStateAsync() { /*...*/ }

Perilaku metode ini sesuai dengan rekan-rekan mereka pada IPersistentState<TState> didefinisikan sebelumnya.

Membuat penyedia penyimpanan

Ada dua bagian untuk API persistensi negara: API yang terkena biji-bijian melalui IPersistentState<T> atau Grain<T>, dan API penyedia penyimpanan, yang berpusat di sekitar IGrainStorage - antarmuka yang harus diterapkan oleh penyedia penyimpanan:

/// <summary>
/// Interface to be implemented for a storage able to read and write Orleans grain state data.
/// </summary>
public interface IGrainStorage
{
    /// <summary>Read data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">State data object to be populated for this grain.</param>
    /// <returns>Completion promise for the Read operation on the specified grain.</returns>
    Task ReadStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);

    /// <summary>Write data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">State data object to be written for this grain.</param>
    /// <returns>Completion promise for the Write operation on the specified grain.</returns>
    Task WriteStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);

    /// <summary>Delete / Clear data function for this storage instance.</summary>
    /// <param name="grainType">Type of this grain [fully qualified class name]</param>
    /// <param name="grainReference">Grain reference object for this grain.</param>
    /// <param name="grainState">Copy of last-known state data object for this grain.</param>
    /// <returns>Completion promise for the Delete operation on the specified grain.</returns>
    Task ClearStateAsync(
        string grainType, GrainReference grainReference, IGrainState grainState);
}

Buat penyedia penyimpanan khusus dengan menerapkan antarmuka ini dan mendaftarkan implementasi itu. Untuk contoh implementasi penyedia penyimpanan yang ada, lihat AzureBlobGrainStorage.

semantik penyedia Storage

Nilai spesifik Etag penyedia buram (string) dapat ditetapkan oleh penyedia penyimpanan sebagai bagian dari metadata status butir yang diisi saat status dibaca. Beberapa penyedia dapat memilih untuk meninggalkan ini seolah-olah null mereka tidak menggunakan Etags.

Setiap upaya untuk melakukan operasi tulis ketika penyedia penyimpanan mendeteksi pelanggaran kendala harus menyebabkan penulisan Task kesalahan dengan kesalahan InconsistentStateException sementara dan membungkus pengecualian penyimpanan yang Etag mendasarinya.

public class InconsistentStateException : OrleansException
{
    public InconsistentStateException(
    string message,
    string storedEtag,
    string currentEtag,
    Exception storageException)
        : base(message, storageException)
    {
        StoredEtag = storedEtag;
        CurrentEtag = currentEtag;
    }

    public InconsistentStateException(
        string storedEtag,
        string currentEtag,
        Exception storageException)
        : this(storageException.Message, storedEtag, currentEtag, storageException)
    {
    }

    /// <summary>The Etag value currently held in persistent storage.</summary>
    public string StoredEtag { get; }

    /// <summary>The Etag value currently held in memory, and attempting to be updated.</summary>
    public string CurrentEtag { get; }
}

Setiap kondisi kegagalan lain dari operasi penyimpanan harus menyebabkan yang dikembalikan Task rusak dengan pengecualian yang menunjukkan masalah penyimpanan yang mendasarinya. Dalam banyak kasus, pengecualian ini dapat dilemparkan kembali ke penelepon yang memicu operasi penyimpanan dengan memanggil metode pada biji-bijian. Penting untuk mempertimbangkan apakah penelepon akan dapat deserialize pengecualian ini atau tidak. Misalnya, klien mungkin tidak memuat pustaka persistensi tertentu yang berisi jenis pengecualian. Untuk alasan ini, disarankan untuk mengubah pengecualian menjadi pengecualian yang dapat disebarkan kembali ke penelepon.

Pemetaan data

Penyedia penyimpanan individu harus memutuskan cara terbaik untuk menyimpan status butir – blob (berbagai format / formulir serial) atau kolom per bidang adalah pilihan yang jelas.

Mendaftarkan penyedia penyimpanan

Runtime Orleans akan menyelesaikan penyedia penyimpanan dari penyedia layanan (IServiceProvider) ketika biji-bijian dibuat. Runtime akan menyelesaikan instance IGrainStorage. Jika penyedia penyimpanan diberi nama, misalnya melalui [PersistentState(stateName, storageName)] atribut, maka instans IGrainStorage bernama akan diselesaikan.

Untuk mendaftarkan instans IGrainStoragebernama , gunakan AddSingletonNamedService metode ekstensi mengikuti contoh penyedia AzureTableGrainStorage di sini.