Skip to main content

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 modeDefaultDescription
LastWritetrueThe last write wins.
FirstWriteThe 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:

// 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:

// 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:

// 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!
tip

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 levelDescription
StrongStrong Consistency guarantees that read operations always return the value that was last written.
BoundedStalenessBounded Staleness guarantees that reads are not too out-of-date.
SessionSession 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.
ConsistentPrefixConsistentPrefix Consistency guarantees that reads will return some prefix of all writes with no gaps. All writes will be eventually be available for reads.
EventualEventual 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.