Exercise - Persist grain state
Your app is configured to use Orleans and two endpoints to provide basic functionality. Next you enhance this setup by adding persistent storage for your grains. In this scenario, you use in-memory storage for simplicity. However, keep in mind that once your code is set up, switching to a more permanent store such as Azure Blob Storage is as simple as changing the connection configuration.
Configure grain storage
You can configure how the app stores grains by modifying the UseOrleans
method. Update the existing siloBuilder
configuration code in your app so that it matches the following example:
builder.Host.UseOrleans(siloBuilder =>
{
siloBuilder.UseLocalhostClustering();
siloBuilder.AddMemoryGrainStorage("urls");
});
This code instructs Orleans to store grains in memory and names the storage urls
. If you wanted to use other storage options such as Azure Blob Storage, you would change this method to AddAzureBlobGrainStorage
and provide the necessary configuration parameters. The rest of your Orleans code would remain the same.
Create an object to persist
Rather than using the KeyValuePair
, you create a new class to store the URL details. This class is used to store the shortened route segment and the full URL. This setup allows both items to be stored in state for whenever they're needed, and can be easily retrieved using the alias.
Add a new class named UrlDetails
to the project with the following code:
[GenerateSerializer]
public sealed record class UrlDetails
{
[Id(0)]
public string FullUrl { get; set; } = "";
[Id(1)]
public string ShortenedRouteSegment { get; set; } = "";
}
Add persistent storage to the grains
The next step is to update the UrlShortenerGrain
to use persistent state.
Replace the class definition to use a primary constructor in the
UrlShortenerGrain
and delete the_cache
field so they match the following code:public sealed class UrlShortenerGrain( [PersistentState( stateName: "url", storageName: "urls")] IPersistentState<UrlDetails> state) : Grain, IUrlShortenerGrain { // Omitted for this step, but will be updated in the next step... }
The
PersistentState
attribute is provided by Orleans. The attribute handles many internal tasks related to storing and retrieving state values for the grain. You simply need to provide a name for the state object and the name of the storage silo. ThestorageName
should be the same as the value you provided to theAddMemoryGrainStorage
method in the silo configuration.Update the grain methods to use the new state field so that they match the following code:
public async Task SetUrl(string fullUrl) { state.State = new() { ShortenedRouteSegment = this.GetPrimaryKeyString(), FullUrl = fullUrl }; await state.WriteStateAsync(); } public Task<string> GetUrl() => Task.FromResult(state.State.FullUrl);
This code accomplishes the following tasks:
The
SetUrl
method assigns the URL alias and the full URL to aUrlDetails
that is persisted in the grain state. Orleans handles managing that state and saving it to the location you configured, which in this case is in-memory. However, remember that state could also be a database or service such as Azure Blob Storage.The
GetUrl
method simply returns the full URL from theUrlDetails
stored on the grain so that the application can use it. Saving both the alias and full URL allows for flexibility in future use cases where we might need one or the other.
Your application now supports persistent state. Shortened URL redirects could also now be saved between user sessions and application restarts if you were to configure Orleans to use permanent storage instead of in-memory.