Original link: https://teddy-chen-tw.blogspot.com/2022/07/7.html
June 30 22:15~24:00; July 01 00:00~00:59
▲Figure 1: Different Aggregates do not need to be locked
foreword
Suppose two users get the same Tag at the same time, rename it and save it, how can the system avoid data conflict? This is a Concurrency Control problem. In the Event Sourcing system, optimistic locking (Optimistic Locking) or called optimistic concurrency control (Optimistic Concurrency Control) is generally used to solve this problem.
Today, I will introduce how to implement optimistic locking in the context of Domain-Driven Design (DDD) and Event Sourcing.
***
optimistic locking
The concept of optimistic locking is very simple. Take the above two users getting the same Tag and renaming it as an example. First, the Tag saves a version number in the database, and the Tag read from the database or carries this version number. , and then compare whether the version number in the database is the same as the version number of the Tag stored at this time when saving. If they are the same, it means that after the Tag was read from the database, no one else has modified the Tag, so it can be written directly. After writing, the version number of the Tag in the database will be incremented by 1 to indicate that the data has been updated. If the version number of the tag is different from the version number in the database when writing, it means that someone has changed its data after the tag is read, so the writing fails (the newer data cannot be overwritten with the old data). With the exception of optimistic locking failure, the user must reload the new data, modify it and save it again.
***
Implement Aggregate and Repository to support optimistic locking
Please refer to Figure 2. The version attribute (line 16) on the AggregateRoot category of ezKanban is used to support optimistic locking. Its initial value is -1, indicating that it has not been written to the database.
▲Figure 2: AggregateRoot supports optimistic locking
When an Aggregate is written to and read from the database, the Repository is responsible for setting the version field on it. The relevant code has been seen when I introduced the Repository implementation before, but Teddy did not explain it at the time. Now look again, please refer to Figure 3, first see the save method of GenericEventSourcingRepository, after line 52 stores aggregateRootData through eventSourcingStore, eventSourcingStore will update the version field on aggregateRootData. On line 53 then update the version field on aggregate.
Then look at the findById method. After the aggregate is generated on line 36, line 37 sets its version field.
▲Figure 3: GenericEventSourcingRepository updates the version field of Aggregate
At the database level, EventStoreDB itself supports optimistic locking. Recall the code in Figure 4. Lines 36~36 set expectedRevision, so that EventStoreDBClient will start optimistic locking checking when writing data.
▲Figure 4: Set optimistic locking code when EventStoreDB is written
As for the Message DB implemented by PostgreSQL, it also supports optimistic locking, but if Message DB is used as an Outbox, in addition to storing domain events, ORM data needs to be stored, and ORM data also has the problem of optimistic locking. In ezKanban, when PostgreSQL is used as an Outbox, the ORM’s optimistic locking mechanism is used, and the optimistic locking check is not performed for writing domain events.
Finally, to see how the ORM sets optimistic locking, please refer to Figure 5. Add the long version attribute to TagData (line 28), and then paste it with the @Version annotation (line 26), so that the optimistic locking mechanism of the database can be activated.
▲Figure 5: TagData can automatically start optimistic locking by adding @Version to version
***
test
I talked about how optimistic locking works for a long time? Write a test case to know, please refer to Figure 6. Lines 106 and 107 read the same Tag and place them in the tagV1 and tagV2 variables respectively. Line 108 modifies the name of tagV1, and then stores tagV1. At this time, the version of the tag in the database has been incremented by 1, but the version number of tagV2 in the memory is still old. So storing tagV2 on line 112 throws an optimistic lock failure exception. This test case is injected into the Event Sourcing and Outbox repository respectively, and the execution results are passed, as shown in Figure 7.
▲Figure 6: Optimistic lock test case
▲Figure 7: Test case execution results
***
Next episode preview
The basic knowledge of Event Sourcing is almost introduced. The next episode will introduce CQRS. After the introduction of CQRS, we will discuss the advanced topics of Event Sourcing, such as snapshots and event version changes.
***
Youzo’s inner monologue: I’m finally going to enter CQRS.
This article is reprinted from: https://teddy-chen-tw.blogspot.com/2022/07/7.html
This site is for inclusion only, and the copyright belongs to the original author.