This article mainly explains the core design concept of Akka Persistence and the typical application of CQRS (Command Query Responsibility Segregation) architecture design. Let’s take a look at why Akka Persistence uses the CQRS architecture.

CQRS

Most of the time when we were in the processing of the business needs of the high concurrency, often can application layer to the code optimization is very good, such as cache, current limiting, balanced load, etc., but it is hard to avoid a problem is data persistence, so that the performance of the database are likely to be the bottleneck of system performance, the article also talked about in front of me, If we use the database to ensure the CRUD of the record, under the condition of high concurrency, let the database perform so many transaction operations, many database operations will time out, the connection pool is not enough, resulting in a large number of requests failed, the error rate of the system increased and the load performance decreased.

In that case, can we borrow the idea of read-write separation? If the write operation is separated from the same operation, even for different tables, database operation, then we can greatly reduce the database bottleneck, so that the whole system performance is greatly improved. So what does CQRS do?

Let’s start with the common way:

acid

We can see that our requests for data are directly operated on the database through the corresponding interface, which will definitely cause great pressure on the database when the concurrency is large. Although the architecture is simple, it is inadequate in the face of high concurrency.

So what’s different about the CQRS approach? Let’s also look at how it works:

cqrs

Chad looks at it, it seems to be no different from the ordinary way, isn’t it just an Event and DB storage, in fact, it is not, the small change is the transformation of the core concept, first of all, we can see that there will be an Event in the CQRS architecture, then what does it mean? In fact, students who have read the previous article can easily understand that Event is a domain model obtained by our system according to the request processing, such as an Event to modify the balance operation. Of course, only critical data will be saved in this Event.

Many students have questions again, this is not like the ordinary reading and writing separation, is it also hidden what secret? Let’s compare the differences:

1. Single database mode
  • Write operations generate mutex, which degrades performance.
  • Even with optimistic locking, there are a lot of failures in the case of a lot of writes;
2. Read/write separation
  • Read/write separation is increased by physical servers, which increases the load.
  • Read/write separation applies to scenarios where read operations are larger than write operations.
  • Read/write separation is difficult in the face of a large number of write operations;
3.CQRS
  • Regular data persistence and Event persistence can use the same database;
  • Architectural design can keep read and write operations as separate as possible.
  • Operations that can support a large number of writes;
  • Supports asynchronous persistence of data to ensure data consistency.

As can be seen from the respective characteristics of the three methods, single database mode has a great performance bottleneck in the case of a large number of read and write operations, but simple read and write separation is still inadequate in the face of a large number of write operations, such as the most common inventory modification query scenario:

common-action

We can see that writing the database in this mode can be very stressful, and there are problems with data synchronization, data latency, and so on.

So what if we design with CQRS architecture:

cqrs-action

First of all, we can separate the business model, separate different queries, and carry out asynchronous persistence for unavoidable data segments in the same interval, so as to improve the throughput of the system while ensuring data consistency. With this design we have less transaction contention, and we can use in-memory databases (of course the fastest for in-memory operations) to improve data writes. (The above database can be distributed database, do not worry about stand-alone downtime)

So how does CRQS ensure data consistency?

From the above we can see that a write operation we will after the preliminary processing system to generate a field events, such as a user for the xx items 1, 2 b users buy xx commodities, etc., in accordance with the normal way we directly is sure to order operation, inventory modification operations were put inside a transaction to database operation, performance, With CQRS way, after the first system in the field of persistent corresponding events and modify memory in the inventory (after this processing very quickly) can be immediately to respond to the user, the details of the real lasting can asynchronously, if, of course, when in the process of information persistence what went wrong, system can resume the right data, of course, Since our domain events have been persisted successfully, we can recover real data according to domain events during system recovery. Of course, in order to prevent data loss and data duplication caused by data recovery, we need to formulate corresponding principles, such as assigning corresponding ids to domain events.

Using CQRS brings performance improvements, but it also has its drawbacks:

  • Make the system more complex, do some extra design;
  • CQRS guarantees ultimate consistency and may only be applicable to specific business scenarios;

Application of CQRS in Akka Persistence

Through the above explanation, I believe you have a certain understanding of CQRS. Now let’s take a look at the specific application of CQRS in Akka Persistence. Here I will combine the example of LotteryCmd in the last article, for example, it is a write operation command. LuckyEvent is then persisted, and the balance of the lottery in memory is modified to return the corresponding result. Here, we can feed back the result to the user at the same time, and conduct asynchronous persistence of the result. The process is as follows:

cqrs-example

It can be seen that the principle of Akka Persistence is completely designed based on the ARCHITECTURE of CQRS. In addition, Persistence Actor also stores a memory state, equivalent to an in Memory database, which can be used to provide storage and query of key data, such as the inventory mentioned above. Balance and other data, the design of this part depends on the specific business scenario.

The core of Akka Persistence is several Persistence methods in the PersistentActor interface, such as the

def persist[A](event: A)(handler: AUnit) :Unit

def persistAll[A](events: immutable.Seq[A])(handler: AUnit) :UnitCopy the code

Methods, they all have two parameters, one is the persistence of events, one is a persistent after the subsequent processing logic, we can modify the Actor in the subsequent handler internal state, send a message to the outside operations, such as the model is based on CQRS architecture, modify state event driven, Akka also can be in the system when something goes wrong, Restore the state of the Actor with the corresponding event.

conclusion

Overall, CQRS architecture is a kind of different CRUD architecture, so you enjoy it brings high performance at the same time may encounter some strange questions, of course, these are all can be solved, it is important to change in thinking, such as event driven, the concept of the domain model, but believe when you understand and master it, you will love it.