Managing Process Efficiently: Intro to the Disruptor Pattern
This was a blog post I wrote for my employer's blog back when I was a "consultant".
The Disruptor is, essentially, a scheduling strategy builder for multithreaded code. It stands out in the world of concurrent programming because it offers both great execution speed and easily readable and debuggable code. Yes, it does have a weird name. According to the original whitepaper, it was coined "Disruptor" because
"it had elements of similarity for dealing with graphs of dependencies to the concept of “Phasers” in Java 7..."
Of course, it is much more than just a Star Trek joke. The pattern was developed by the LMAX exchange to build a competitive, low-latency trading platform that could handle millions of transactions per second. Luckily for us developers, they have opened the source code to the public. The reference implementation is written in Java, but there is a C# implementation as well.
Under the hood, the Disruptor is a pre-allocated array used as a ring buffer where a process is applied each element in the buffer in order. The process is expressed as an acyclic dependency graph (DAG) of Event Processors. Each Event Processor should be thought of as a separate task or thread, since they are designed to be executed in parallel. After an Event Processor finishes the work associated with an Event, it will advance its Sequence Barrier past that Event.
Fig. 1: C depends on B, B depends on A.
This allows other Event Processors which depend on it to continue processing, up to the barrier. Since there is a single writer for each sequence barrier, no locks or CAS operations are required. A similar edict applies to Events themselves: each Event Processor must only write to its own properties on an Event, or to events that it has been assigned to, making it a single writer for those events or those properties.
The Disruptor was intended to be a foundation for an extremely fast interactive application whose database fits in RAM, But in my experience, it can also be used as a good way to organize and optimize an asynchronous process involving external services and slow disks. It is best used in conjunction with event streams or message queues rather than a traditional 3-Tier architecture like MVC with a SQL backend.
To get started with the Disruptor in .NET, download the NuGet package. My next post will demonstrate basic setup of a Disruptor in C# and show how the Disruptor can help create a high-perfomance stream processing system.