Advanced state options
State stores offer some advanced configuration in case your application requires stricter control on concurrency and consistency.
Concurrency mode
About concurrency modes
Since most state operations are related to a single task and task related state, state stores have by default a relaxed concurrency mode LastWrite, where the last write wins. LastWrite optimizes high-throughput writes, where data consistency is less important and performance is a priority.
In cases when multiple processes are writing to the same state entries (eg. global entries), it is possible that concurrent operations write conflicting changes to the state. State stores support Optimistic Concurrency Control (OCC), OCC protects your data from accidentally overwriting changes that were made by others (or your self).
In most state functions, it is possible to specify the concurrency mode using the StateConcurrencyMode. It is required to work with ETags to implement the FirstWrite mode.
| Concurrency mode | Default | Description |
|---|---|---|
LastWrite | true | The last write wins. |
FirstWrite | The first write wins. |
Sample using FirstWrite mode and ETags
The following example simulates a concurrent update of a coupon. Notice the Etag of each state version.
First a coupon is created:
- C#
// Get state client
var stateClient = taskurai.GetStateClient();
var coupon = new
{
Code = "BLACKFRIDAY",
Value = 10.0
};
// Add a coupon
var originalCouponState = stateClient.SaveState(
"DefaultStateStore",
new StateInput($"webshop::coupons::blackfriday")
{
Value = new
{
Code = "BLACKFRIDAY",
Value = 10.0
}
});
Console.WriteLine(JsonSerializer.Serialize(originalCouponState, serializerOptions));
Next, run the console application to validate:
dotnet run
Sample output:
{
"id": "isolation::dd10b001-addd-45c2-94a6-932282b7515c::state::webshop::coupons::blackfriday",
"partitionKey": "isolation::dd10b001-addd-45c2-94a6-932282b7515c::state::webshop::coupons::blackfriday",
"created": "2026-04-01T10:47:12.7380919+00:00",
"createdBy": "d8e9cb3f-ffeb-4915-bb15-6681a3bcd175",
"modified": "2026-04-01T10:47:12.7380919+00:00",
"modifiedBy": "d8e9cb3f-ffeb-4915-bb15-6681a3bcd175",
"tags": {},
"stateStoreName": "DefaultStateStore",
"isolationMode": true,
"isolationKey": "dd10b001-addd-45c2-94a6-932282b7515c",
"ttl": -1,
"etag": "\u0022310186ba-0000-0d00-0000-69ccf7b00000\u0022",
"value": {
"code": "BLACKFRIDAY",
"value": 10
},
"stateReference": {
"stateStoreName": "DefaultStateStore",
"id": "isolation::dd10b001-addd-45c2-94a6-932282b7515c::state::webshop::coupons::blackfriday"
}
}
Next a concurrent process updates the same coupon and changes the discount, using the default LastWrite mode:
- C#
// Simulate a new update to the coupon code
var updatedCouponState = stateClient.SaveState(
"DefaultStateStore",
new StateInput($"webshop::coupons::blackfriday")
{
Value = new
{
Code = "BLACKFRIDAY",
Value = 20.0 // Increase the discount
}
});
Console.WriteLine(JsonSerializer.Serialize(updatedCouponState, serializerOptions));
Next, run the console application to validate:
dotnet run
Sample output:
{
"id": "isolation::dd10b001-addd-45c2-94a6-932282b7515c::state::webshop::coupons::blackfriday",
"partitionKey": "isolation::dd10b001-addd-45c2-94a6-932282b7515c::state::webshop::coupons::blackfriday",
"created": "2026-04-01T12:29:14.9709456+00:00",
"createdBy": "d8e9cb3f-ffeb-4915-bb15-6681a3bcd175",
"modified": "2026-04-01T12:29:14.9709456+00:00",
"modifiedBy": "d8e9cb3f-ffeb-4915-bb15-6681a3bcd175",
"tags": {},
"stateStoreName": "DefaultStateStore",
"isolationMode": true,
"isolationKey": "dd10b001-addd-45c2-94a6-932282b7515c",
"ttl": -1,
"etag": "\u00223a01d07e-0000-0d00-0000-69cd0f9a0000\u0022",
"value": {
"code": "BLACKFRIDAY",
"value": 20
},
"stateReference": {
"stateStoreName": "DefaultStateStore",
"id": "isolation::dd10b001-addd-45c2-94a6-932282b7515c::state::webshop::coupons::blackfriday"
}
}
Now the first process updates the original coupon, using the original ETag and the FirstWrite mode:
- C#
// Update the original coupon using FirstWrite concurrency mode and ETags
try
{
var concurrentUpdateState = stateClient.SaveState(
"DefaultStateStore",
new StateInput($"webshop::coupons::blackfriday")
{
Value = new
{
Code = "BLACKFRIDAY",
Value = 15.0
},
Etag = originalCouponState.Etag, // Original ETag
ConcurrencyMode = StateConcurrencyMode.FirstWrite
});
Console.WriteLine(JsonSerializer.Serialize(concurrentUpdateState, serializerOptions));
}
catch (RequestFailedException ex) when (ex.Status == (int)System.Net.HttpStatusCode.PreconditionFailed)
{
Console.WriteLine($"Precondition ETag failed!");
}
Next, run the console application to validate:
dotnet run
Sample output:
Precondition ETag failed!
To handle concurrency issues, catch the RequestFailedException and check for the HTTP status code 412 (Precondition Failed).
It is the application's responsibility to address concurrency conflicts. This may involve reloading the resource to retrieve the latest updates and retrying the save operation with the updated state.
Consistency level
State stores support the following consistency levels:
| Consistency level | Description |
|---|---|
Strong | Strong Consistency guarantees that read operations always return the value that was last written. |
BoundedStaleness | Bounded Staleness guarantees that reads are not too out-of-date. |
Session | Session Consistency guarantees monotonic reads (you never read old data, then new, then old again), monotonic writes (writes are ordered) and read your writes (your writes are immediately visible to your reads) within any single session. |
ConsistentPrefix | ConsistentPrefix Consistency guarantees that reads will return some prefix of all writes with no gaps. All writes will be eventually be available for reads. |
Eventual | Eventual Consistency guarantees that reads will return a subset of writes. All writes will be eventually be available for reads. |
Default state store settings:
- Internal state stores: default
Session. - Dedicated state stores: default
Session, it is possible to configure a default consistency level on the database account.
In most state functions, it is possible to override the default consistency level using the StateConsistencyLevel to override the consistency level for a single call.