Teach you to learn Dapr - 5. State management


With state management, your application can store data as key / value pairs in a supported state store.

Your application can use Dapr's State Management API to use the state storage component to save and read key / value pairs, as shown in the following figure. For example, by using HTTP POST, you can save key / value pairs, and by using HTTP GET, you can read keys and return their values.


Pluggable state storage

The Dapr data store is modeled as a component that can be replaced without changing the code. For example, MySQL, Redis, Azure CosmosDB, etc.

Configurable state storage behavior

Dapr allows developers to attach additional metadata to state operation requests to describe how requests are processed. For example:

  • Concurrency requirements
  • Conformance requirements

By default, your application should assume that the data store is ultimately consistent and use the concurrency mode in which the last write wins


Dapr supports optimistic concurrency control (OCC) using ETags. When requesting status, dapr always attaches the ETag attribute to the returned status. When the user code attempts to update or delete the status, it should be updated by attaching an ETag to the request body or deleted through the if match header. The write operation will succeed only if the ETag provided matches the ETag in the state store. It is recommended that you use a retry policy when using ETag to compensate for such conflicts.

If your application omits the ETag when writing a request, Dapr skips the ETag check when processing the request. Compared with the first win mode using ETag, this essentially enables the last win mode.

Automatic encryption

Dapr supports automatic client encryption of application state and key rotation. This is a preview function, which is supported by all dapr state stores.


Dapr supports strong consistency and final consistency, with final consistency as the default behavior.

  • When strong consistency is used, Dapr waits for all copies (or specified quorum) to confirm before confirming the write request.
  • When final consistency is used, Dapr will return as soon as the underlying data store accepts the write request, even if it is a single copy.

Batch operation

Dapr supports two types of batch operations - bulk or multi.

Note: the difference between bulk and multi is that bulk is not transactional and multi is transactional.

Actor status

The transaction state store can be used to store Actor state. To specify the state store for the Actor, specify the value of the property actorStateStore as true in the metadata section of the state store component.

Note: the Actors state is stored in the transaction state store in a specific scheme, and consistent queries are allowed. Therefore, only one state storage component can be used for all Actors.

Direct query status storage

Dapr saves and retrieves status values without any conversion. You can query and aggregate state directly from the underlying state store.

For example, to obtain all status keys associated with the application ID "myApp" in Redis, use:

KEYS "myApp*"

Query Actor status

If the data store supports SQL queries, you can use SQL queries to query the status of participants. For example, use:

SELECT * FROM StateTable WHERE Id='<app-id>||<actor-type>||<actor-id>||<key>'

You can also perform aggregate queries across Actor instances to avoid the round based concurrency limitations common to the Actor framework. For example, to calculate the average temperature of all thermometer actors, use:

SELECT AVG(value) FROM StateTable WHERE Id LIKE '<app-id>||<thermometer>||*||temperature'

Save and get status

State management is one of the most common requirements of any application: new or legacy, monomer or microservice. Handling different databases, tests, retries, and failures can be time-consuming and laborious.


If you have prepared the Dapr running environment, you can see the previous article

Teach you to learn Dapr - 3. Use Dapr to run the first. Net program

Set status store

Windows opens the directory% USERPROFILE%\.dapr\components

  1. Create the file statestore.yaml
  2. Use redis as the database for status storage

    apiVersion: dapr.io/v1alpha1
    kind: Component
      name: statestore
      type: state.redis
      version: v1
      - name: redisHost
        value: localhost:6379
      - name: redisPassword
        value: ""
      - name: actorStateStore
        value: "true"

    Note: this yaml has enabled the Actor state through the Actor state store

Saving and retrieving individual states

Note: setting app ID is important because the status key is prefixed with this value. If you do not set it, a will be generated for you at runtime, and a new one will be generated the next time you run the command, and you will no longer be able to access the previously saved state. In other words, if you want to share the status, you can customize a reserved app ID as the sharing status instead of leaving it blank.

Run Dapr Sidecar

Run an empty Sidecar. Because we only use it to help access the state store, the difference is that dapr run is not followed by dotnet run as the Sidecar of a program

dapr run --app-id myapp --dapr-http-port 3500 --dapr-grpc-port 50001

Create client

Create a console program and add the Dapr.Client NuGet package reference.

Modify Program.cs

using Dapr.Client;

var storeName = "statestore";
var key = "myFirstKey";
var value = "myFirstValue";

var client = new DaprClientBuilder().Build();
await client.SaveStateAsync(storeName, key, value);
Console.WriteLine("State has been stored");

var data = await client.GetStateAsync<string>(storeName, key);
Console.WriteLine($"Got value: {data}");


Delete a single state

await client.DeleteStateAsync(storeName, key);

Save and retrieve multiple states through transactions

Dapr also allows you to save and retrieve multiple states in the same call.

var lst = new List<StateTransactionRequest>()
    new StateTransactionRequest("test1", System.Text.Encoding.UTF8.GetBytes("value1"), StateOperationType.Upsert),
    new StateTransactionRequest("test2", System.Text.Encoding.UTF8.GetBytes("value2"), StateOperationType.Upsert),
await client.ExecuteStateTransactionAsync(storeName, lst);

var datas = await client.GetBulkStateAsync(storeName, lst.Select(r => r.Key).ToList(), 0);
Console.WriteLine($"Got items: {string.Join(",", datas.Select(d => $"{d.Key}={d.Value}"))}");

Strong consistency

When using strong consistency, Dapr will ensure that the underlying state is stored before the write or delete state. Once the data is written to all copies or receives an ack from quorum, it will return a response.

For GET requests, Dapr will ensure that the latest data is returned consistently between the copies stored. The default is final consistency, unless otherwise stated in the request to the status API.

await client.SaveStateAsync(storeName, key, value, new StateOptions() { Consistency = ConsistencyMode.Strong });

var etagData = await client.GetStateAndETagAsync<string>(storeName, key, ConsistencyMode.Strong);

await client.DeleteStateAsync(storeName, key, new StateOptions() { Consistency = ConsistencyMode.Strong });

Write win first and write win last

Dapr allows developers to choose two common concurrency modes when using data storage: first write wins and last write wins. First write wins is useful when you have multiple application instances, all of which write the same key at the same time.

The default mode of Dapr is the last write win.

The following example shows how to get an ETag, then use it to save the state, and then delete the state:

await client.SaveStateAsync(storeName, key, value, new StateOptions() { Concurrency = ConcurrencyMode.FirstWrite });
var firstWriteWinData = await client.GetStateAndETagAsync<string>(storeName, key);
var etag = firstWriteWinData.etag;

await client.TrySaveStateAsync(storeName, key, DateTime.Now.Ticks.ToString(), etag, new StateOptions() { Concurrency = ConcurrencyMode.FirstWrite });
var firstWriteWinDeleteSucceeded = await client.TryDeleteStateAsync(storeName, key, etag);
Console.WriteLine($"First write wins delete:{firstWriteWinDeleteSucceeded}");

firstWriteWinData = await client.GetStateAndETagAsync<string>(storeName, key);
firstWriteWinDeleteSucceeded = await client.TryDeleteStateAsync(storeName, key, firstWriteWinData.etag);
Console.WriteLine($"First write wins delete:{firstWriteWinDeleteSucceeded}");

Note: here is an example of an ETag's attempt to delete after an update fails. Finally, get a new status again to correct the ETag's deletion

Share state between different applications

To achieve state sharing, Dapr supports the following key prefix policies

  • Appid - this is the default policy. Appid prefix allows status to be managed only by applications with the specified appid. All status keys will be prefixed with appid and application scoped.
  • Name - this setting uses the name of the state storage component as a prefix. For a given state store, multiple applications can share the same state.
  • none - this setting does not use a prefix. Multiple applications share state between different state stores

For example: to specify a prefix policy, add a metadata key named keyPrefix on the status component

apiVersion: dapr.io/v1alpha1
kind: Component
  name: statestore
  namespace: production
  type: state.redis
  version: v1
  - name: keyPrefix
    value: <key-prefix-strategy>

Note: the demonstration of this example is relatively complex. The idea is to use multiple statestore.yaml, and then switch different strategies according to different storename s. Interested partners can try it on their own.

Automatically encrypt state and manage key rotation

Note: up to now, this function is a preview version. Interested partners can try it by themselves

Application state often requires static encryption to provide greater security in enterprise workloads or regulated environments. Dapr provides automatic client encryption based on AES256.

State lifetime (TTL)

Dapr sets the time to live (TTL) for each state on request. This means that the application can set the lifetime for each stored state, and these states cannot be retrieved after expiration.

Note: only some Dapr status storage components are compatible with status TTL. For supported state stores, you only need to set ttlInSeconds metadata when publishing messages. Other status stores ignore this value.

await client.SaveStateAsync(storeName, key, value, metadata: new Dictionary<string, string>() { { "ttlInSeconds", "3" } });
var ttlData = await client.GetStateAsync<string>(storeName, key);
Console.WriteLine($"TTL Data:{ttlData}");

ttlData = await client.GetStateAsync<string>(storeName, key);
Console.WriteLine($"TTL Data:{ttlData}");

Persistent state

To explicitly set the persistence state (ignoring any TTL set for the key), specify the ttlInSeconds value as - 1.

Source code of this chapter



We are moving towards a new framework and a new ecology

Our goal is free, easy to use, flexible, functional and robust.

So we are learning from the design concept of Building blocks and are making a new framework, MASA Framework. What are its characteristics?

  • The native supports Dapr and allows Dapr to be replaced by traditional communication methods
  • The architecture is unlimited, and single applications, SOA and microservices are supported
  • Support. Net native framework, reduce learning burden, and insist on not making new wheels except for concepts that must be introduced in specific fields
  • Rich ecological support, in addition to the framework, there are a series of products such as component library, permission center, configuration center, troubleshooting center, alarm center and so on
  • The unit test coverage of the core code base is 90%+
  • Open source, free, community driven
  • What else? We're waiting for you to discuss it together

After several months of production project practice, POC has been completed, and the previous accumulation is being reconstructed into a new open source project

At present, the source code has been synchronized to Github (the document site is under planning and will be gradually improved):







QQ group: 7424099

Wechat group: add technology operation wechat (MasaStackTechOps), note the purpose, and invite to join the group

Tags: C# .NET Concurrent Programming http microsoft

Posted on Fri, 12 Nov 2021 02:59:52 -0500 by ac1dsp3ctrum