The reactive manifesto states that modern systems must be reactive to offer the expected level of service. In order to achieve those objectives, those need to be event-driven, message based designs. And that those key design decisions help have them scalable, resilient and reactive.
If you are a regular reader, event-driven and message based should definitely ring a bell here!
I am going to ask you to make a leap of faith, the one I made several years ago while trying to solve the problems with locks.
I will ask you to abandon notions you were accustomed to, to give up trying to reach certain objectives and to renounce to familiar entities. If this looks far-fetched, do not worry: it is still easier than taking the FP road.
Renounce to locks. If you have read me so far, you must be ready for this. I do not expect you to stop using them overnight, but consider them for what they are: hacks.
Renounce to mutable state sharing. If something is worth to be shared, male it immutable before sharing it. If you need to do data parallelism, use map reduce or some equivalent.
Embrace asynchronous programing. Accept that methods may not return or execute synchronously with the caller. This one is costly, but this is also the one that will free you the most.
Reevaluate your assumptions regarding simultaneity. It does not exist! Consider event ordering instead.
Once you make that leap, you can benefit from:
Code that is provably deadlock free. Hey, you have no lock nor wait.
A drastic reduction of potential race conditions thanks to using immutable shared state
A scalable application! Using those rules, and a dedicated library (IA+ threading), we have implemented services that scales from one to 32 cores. And I am confident this will go on up to 128 cores (beyond that figure we would need to migrate our thread pool implementation).
Stronger implementations, because you just freed yourself from major Heisenbug sources.
For me, that list was like my three wishes granted. Wait, make it four actually. This is the tool I was lacking during those endless hours trying to understand where was the race conditions, straighten up design to remove deadlocks, weeding useless locks to free up performance.
If you do not share those concerns, I am happy for you, and it probably means you are in a different environment. But if you do, I will raise my offer by adding to the mix:
– the ability to tune your architecture between scalability,latency and throughput – the ability to get metrics to assist you in that process
So far, I have listing the flaws of locks as an attempt to push you, my dear readers, out of this dead-end.
But so far, I have been avoiding the solution space, purposely. This post will end that!
I like immutability and message passing, therefore I suggest you look into retlang/jetlang (C#/java). Based on the actor model, it offers a nice API helping the implementation of scalable services. It has been around for a few years now, and it clearly provides value.
It turns out the framework I designed working here has similarities with it, but it uses a different approach, offering extra services without the obligation of the actor model.
Here is one abstraction that definitely outlived its usefulness Threads
They aim at abstracting a processing unit. They were very useful when processing units were a scarce resources, to the point there usually was only of them. Their usage was to allow sharing of that scarce resource.
But at a time where we may have them by tens or hundreds (or even more for the most radical proposition), they are just an impediment we need to ge rid of.
Object Oriented Development is still the dominant programming model. It has replaced Structured Development something like 20 years ago. New languages made interesting breakthrough recently (Erlang, Python), but none of them is in a position to get the throne: Functional programming is powerful, but hard to master, and dynamic typing is expressive but leads to code mess.
Its success mainly comes from the accessibility of C++. Many interesting proposals were available at the time, including Smalltalk, but C++ ruled them all simply by being pragmatic.
Thanks to OOD, developers made significant progress towards the Holy Grail: code reuse. To that effect, it offered three major paradigms:
inheritance: reuse of code by having a class inherit from other(s). Permit changes done on the super class to trickle to the derived classes
encapsulation: the concept of interface: some code can manipulate entities thanks to some contract. The implementation details can change without impacting the code (as long as the contract does not change)
polymorphism: those contracts can be implemented by many classes, each of them having various side effects
Initially, there was a strong emphasis on inheritance, but, years of experience as taught us that the value lied in encapsulation and polymorphism, because they permit low coupling. Inheritance turned out to be more an embarrassment than anything else and is often used by opponents.
What about concurrency?
Concurrency is orthogonal to OOD. And as OOD implied linear execution, concurrency wreaks havoc in that perfect world. Methods are transactions that change or consume the state of the object (this/self). If two methods are executed simultaneously, the object sate will shift unexpectedly, e.g: the key found is some internal dictionary may be removed by a delete command, there is an error message but there is no error code, etc…
The traditional workaround for this is to rely on exclusive access, i.e. using synchronized in Java and locks for other languages.
But, we know that locks contain so many pitfalls that you cannot expect to produce durable code using them.
That pushed many highly skilled developers to reject OOP altogether and embrace a more radical approach: functional programing. Clojure is their flagship, allowing to implement easily scalable code thanks to a share nothing paradigm.
I have a different view on this, I strongly believe that any solution will a steep learning curve will fail, as most of the developers would be kept out of it. That being said, I am very strong supporter of immutability, message passing and asynchronous programming. It is just that I am sure you can combine this with good old OOD.
Well, I have been doing that for more than 7 years now.