Should I use Disruptor (LMAX) with a large model in memory and CQRS?

Our system has a structured model (about 30 different objects with several types of relationships), fully stored in memory (about 10 GB) for performance reasons. On this model, we have to do 3 types of operations:

  • update one or more objects
  • for specific data (usually required to read thousands of objects).
  • get statistics (how much memory is used, how many queries for the view, etc.).

Currently, the architecture is pretty standard, with a thread pool for servlets that use a common model. There are many parallel collections inside the model, but still there are many expectations, because some objects are hotter and mostly threads want to read and write them. Also note that queries are usually much more complex and time consuming than records.

I am exploring the possibility of switching to the Disruptor architecture, supporting the model in one thread, moving everything possible (validation, auditing, etc.) from the model to individual consumers.

The first question, of course, is: does it make sense?

The second question: ideally, write requests should take precedence over read requests. What is the best way to take precedence in a destroyer? I thought about 2 buffers with rings, and then I tried to read with high priority more often than with low priority.

To clarify the issue, more architectural than the actual LMAX Disruptor code.

Update details

Data is a complex area, with many entities (> 100k) from many different types (~ 20), interconnected in a tree structure with many different collections.

Typically, queries involve moving thousands of objects to find the right data. Updates are often found, but rather limited, as 10 objects in time, so in general the data does not change very much (for example, 20% per hour).

I did some preliminary tests, and it seems that the benefits of parallel parallel running of the parallel version outweigh the random write lock delays.

+6
source share
2 answers

"Ideally, write requests should take precedence over read requests."

Why? The fastest locks, such as C # ReaderWriterLockSlim, do the opposite. A write should block all reads to avoid partial reads. Thus, such locks allow many simultaneous readings, hoping that everything will turn out "perfectly", and then record .. (The recording is performed with its number in the queue, but most likely, many readings that appeared after they were processed before they are locked).

Writing prioritization is a good way to kill concurrency ..

Is concurrency / CQRS option possible?

+2
source

LMAX may be appropriate ..

LMAX people first implemented traditional ones, then they realized participants (with bursts) and found that the actors spent most of their time in bursts. Then they switched to single-threaded architecture. Now the destroyer is not the key to architecture, the key is a single-threaded BL. With 1 author (one stream) and small objects, you get a high cache hit and no complaints. To do this, they need to move all the long code from the Business level (including IO). Now they use a destroyer for this, basically it's just a ring buffer with one script, which was used for some time in the device driver code, but on a huge message scale.

At first I have one disagreement with this, LMAX is an acting system. Where you have 1 actor for all BLs (and destroyers connect other actors) .. They could significantly improve the actors' system instead of jumping to 1 actor for BL, namely

  • You do not have a large number of services / actors, try using commonly used components in one service. (this appears again and again in SOA / distributed systems).
  • When communicating between subjects, not many to 1. use dots from point to point. (Like all service buses!)
  • When you have queues of points in a point, make sure the tail is a pointer to a separate area of ​​memory. With 2 and 3, now you can use blocked queues, and in queues / threads there is only 1 writer (and you can even use not temporary 256, but the YMM bit writes to the queue). However, the system now has more threads (and if you correctly made 1 relatively small number of messages between the participants). Queues are similar to disruptors and can process many entries and can use the ring buffer style.

With these actors, you have a more modular (and therefore main table) system (and the system can run more actors to process queues - 1 writer entry!)

In your case, I think that 20% of the changes per hour are huge ... Are queries always included in memory objects? Do you have hash tables / memory indices? Can you use read-only collections? Does it matter if your data is old, for example, Ebay uses an update for 1 hour in its collection of elements, so the collection of elements itself is static. With a static collection and static element instructions, they have a static index, and you can quickly find and find elements in memory. It is restored every hour, and when it is completed (it may take several minutes to recover), the system switches to new data. Please note that the elements themselves are not static.

In your case with a huge domain, a single thread can get a low cache level. It is different from LMAX, which has a small domain for each message that needs to be transmitted.

An agent-based system can be better, namely because a group of entities can be grouped and therefore has a high cache hit. But I need to know more. for example, validation validation, auditing, logging, etc. are probably a good plan. Less code = smaller objects = higher cache hit and LMAX objects were small.

Hope this quick reset helps, but it's hard to just look.

+2
source

Source: https://habr.com/ru/post/927916/


All Articles