Handling concurrency – Aggregate Pattern and EF Core

Introduction

In this post I would like to discuss a frequently overlooked, though in some circumstances very important topic – concurrency handling in context of protection of the so-called Domain Invariants.

In other words, the question is the following: how to ensure that even in a multi-threaded environment we are able to always guarantee the immediate consistency for our business rules?

It’s best to describe this problem with an example…

Problem

Let’s assume that our domain has the concept of Order and Order Line. Domain expert said that:

One Order can have a maximum of 5 Order Lines.

In addition, he said that this rule must be met at all times (without exception). We can model it in the following way:

Conceptual Model
Conceptual Model

The Aggregate

One of the main goals of our system is to enforce business rules. One way to do this is to use the Domain-Driven Design tactical building block – an Aggregate. The Aggregate is a concept created to enforce business rules (invariants). Its implementation may vary depending on the paradigm we use, but In object-oriented programming, it is an object-oriented graph as Martin Fowler describes it:

A DDD aggregate is a cluster of domain objects that can be treated as a single unit.

And Eric Evans in DDD reference describes:

Use the same aggregate boundaries to govern transactions and distribution. Within an aggregate boundary, apply consistency rules synchronously

Back to the example. How can we ensure that our Order will never exceed 5 Order Lines? We must have an object that will take care of it. To do this, it must have information about the current number of Order Lines. So it must have a state and based on this state its responsibility is to decide whether invariant is broken or not.

In this case, the Order seems to be the perfect object for this. It will become root of our Aggregate, which will have Order Lines. It will be his responsibility to add the order line and check that the invariant has not been broken.

Order Aggregate
Order Aggregate

Let’s see how the implementation of such a construct may look like:

Everything’s fine right now, but this is only a static view of our model. Let’s see what the typical system flow is when invariant is not broken and when is broken:

Process of adding Order Line - one thread
Process of adding Order Line – success
Process of adding Order Line - rule broken
Process of adding Order Line – rule broken

Simplified implementation bellow:

Concurrency problem

Everything works nice and clean if we operate in a not heavy loaded environment. However, let’s see what can happen when 2 threads almost at the same time want to perform our operation:

Process of adding Order Line - business rule broken
Process of adding Order Line – business rule broken

As you can see in the diagram above, 2 threads load exactly the same aggregate at the same time. Let’s assume that the Order has 4 Order Lines. The aggregate with 4 Order Lines will be loaded in both the first and second threads. The exception will not be thrown, because of 4 < 5. Finally, depending on how we persist the aggregate, the following scenarios are possible:

a) If we have a relational database and a separate table for Order Lines then 2 Order Lines will be added giving a total of 6 Order Lines – the business rule is broken.

b) If we store aggregate in one atomic place (for example in document database as a JSON object), the second thread will override the first operation and we (and the User) won’t even know about this.

The reason for this behavior is that the second thread read data from the database (point 2.1) before the first one committed it (point 1.4.3).

Let’s see how we can solve this problem.

Solution

Pessimistic concurrency

The first way to ensure that our business rule will not be broken is to use Pessimistic concurrency. In that approach, we allow only one thread to process a given Aggregate. This leads to the fact that the processing thread must block the reading of other threads by creating a lock. Only when the lock is released, the next thread can get the object and process it.

Pessimistic concurrency
Pessimistic concurrency

The main difference from the previous approach is that the second thread waits for the previous one to finish (time between points 2.1 and 1.3). This approach causes us to lose performance because we can process transactions only one after the other. Moreover, it can lead to deadlocks.

How can we implement this behavior using EntityFramework Core and SQL server?

Unfortunately, EF Core does not support Pessimistic Concurrency. However, we can do it easily by ourselves using raw SQL and the query hint mechanism of the SQL Server engine.

Firsty, the database transaction must be set. Then the lock must be set. This can be done in two ways – either read data with query hint (XLOCK, PAGELOCK) or by updating the record at the beginning:

In this way, the transaction on the first thread will receive a Exclusive Lock (on write) and until it releases it (through the transaction commit) no other thread will be able to read this record. Of course, assuming that our queries operate in at least read committed transaction isolation level to avoid the so-called dirty reads.

Optimistic concurrency

An alternative, and most often preferred solution is to use Optimistic Concurrency. In this case, the whole process takes place without locking the data. Instead, the data in the database is versioned and during the update, it is checked – whether there was a change of version in the meantime.

Optimistic Concurrency
Optimistic Concurrency

What does the implementation of this solution look like? Most ORMs support Optimistic Concurrency out of the box. It is enough to indicate which fields should be checked when writing to the database and it will be added to the WHERE statement. If it turned out that our statement has updated 0 records, it means that the version of the record has changed and we need to do a rollback. Though often the current fields are not used to check the version and special column called “Version” or “Timestamp” is added.

Back to our example, does adding a column with a version and incrementing it every time the Order entity is changed solve the problem? Well no. The aggregate must be treated as a whole, as a boundary of transaction and consistency.

Therefore, the version incrementation must take place when we change anything in our aggregate. If we have added the Order Line and we keep it in a separate table, ORM support for optimistic concurrency will not help us because it works on updates and here there are inserts and deletes left.

How can we know that the state of our Aggregate has changed? If you follow this blog well, you already know the answer – by Domain Events. If a Domain Event was thrown from the Aggregate, it means that the state has changed and we need to increase Aggregate version.

The implementation can look like this.

First, we add a _version field to each Aggregate Root and the method to increment that version.

Secondly, we add a mapping for the version attribute and indicate that it is a EF Concurrency Token:

The last thing to do is incrementing the version if any Domain Events have been published:

With this setup, all updates will execute this statement:

For our example, for second thread no record will be updated ( @@ ROWOCOUNT = 0 ), so EntityFramework will throw the following message:

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

and our Aggregate will be consistent – the 6th Order Line will not be added. The business rule is not broken, mission accomplished.

Summary

In summary, the most important issues here are:

  • The Aggregate’s main task is to protect invariants (business rules, the boundary of immediate consistency)
  • In a multi-threaded environment, when multiple threads are running simultaneously on the same Aggregate, a business rule may be broken
  • A way to solve concurrency conflicts is to use Pessimistic or Optimistic concurrency techniques
  • Pessimistic Concurrency involves the use of a database transaction and a locking mechanism. In this way, requests are processed one after the other, so basically concurrency is lost and it can lead to deadlocks.
  • Optimistic Concurrency technique is based on versioning database records and checking whether the previously loaded version has not been changed by another thread.
  • Entity Framework Core supports Optimistic Concurrency. Pessimistic Concurrency is not supported
  • The Aggregate must always be treated and versioned as a single unit
  • Domain events are an indicator, that state was changed so Aggregate version should be changed as well
  • GitHub sample repository

    Especially for the needs of this article, I created a repository that shows the implementation of 3 scenarios:

    – without concurrency handling
    – with Pessimistic Concurrency handling
    – with Optimistic Concurrency handling

    Link: https://github.com/kgrzybek/efcore-concurrency-handling

    Related Posts

    Handling Domain Events: Missing Part
    Domain Model Encapsulation and PI with Entity Framework 2.2
    Attributes of Clean Domain Model

Cache-Aside Pattern in .NET Core

Introduction

Often the time comes when we need to focus on optimizing the performance of our application. There are many ways to do this and one way is cache some data. In this post I will describe briefly the Cache-Aside Pattern and its simple implementation in .NET Core.

Cache-Aside Pattern

This pattern is very simple and straightforward. When we need specific data, we first try to get it from the cache. If the data is not in the cache, we get it from the source, add it to the cache and return it. Thanks to this, in the next query the data will be get from the cache. When adding to the cache, we need to determine how long the data should be stored in the cache. Below is an algorithm diagram:

First implementation

Implementation of this pattern in .NET Core is just as easy as its theoretical part. Firstly, we need register IMemoryCache interface:

Afterwards, we need add Microsoft.Extensions.Caching.Memory NuGet package.

And that’s all. Assuming that we want to cache basic information about our users, the implementation of the pattern looks as follows:

First of all, we are injecting .NET Core framework IMemoryCache interface implementation. Then in line 18 we check whether the data is in the cache. If it is not in the cache, we get it from the source (i.e. database), add to cache and return.

Code smells

This way of implementation you can find on MSDN site. I could finish this post at this point, but I must admit that there are a few things that I do not like about this code.

First of all, I think the interface IMemoryCache is not abstract enough. It suggests that the data is kept in application memory but the client code should not care where it is stored. Moreover, if we want to keep the cache in the database in the future, the name of this interface will not be correct.

Secondly, client code should not be responsible for logic of the naming cache key. It is Single Responsibility Principle violation. It should only provide data to create this key name.

Lastly, client code should not care about cache expiration. It should be configured in other place – application configuration.

In next section I will show how we can eliminate these 3 code smells.

Improved implementation

The first and most important step is to define a new, more abstract interface: ICacheStore

Then we need to define interface for our cache key classes:

This interface has CacheKey string property which is used during resolving cache key in our MemoryCacheStore implementation:

Finally, we need to configure IoC container to resolve MemoryCacheStore instance as ICacheStore together with expiration configuration taken from application configuration:

This is how new implementation looks like:

After this set up we can finally use this implementation in our client code. For each new object that we want to store in cache we need:

1) Add expiration configuration

2) Class that defines the cache key

Finally, the new client code looks like this:

In the code above we use more abstract ICacheStore interface, don’t care about creation of cache key and expiration configuration. It is more elegant solution and less error-prone.

Summary

In this post I described Cache-Aside Pattern and its primary implementation in .NET Core. I proposed also augmented design to achieve more elegant solution with a small amount of work. Happy caching! 🙂

UPDATE 2019-26-02: I updated my sample codebase so if you would like to see full, working example – check my GitHub repository.