<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://domsj.info/feed.xml" rel="self" type="application/atom+xml" /><link href="https://domsj.info/" rel="alternate" type="text/html" /><updated>2019-02-20T14:08:17+00:00</updated><id>https://domsj.info/feed.xml</id><title type="html">@domsj on the web</title><entry><title type="html">Joining Fauna and the future of streamy-db</title><link href="https://domsj.info/2019/02/20/joining-fauna.html" rel="alternate" type="text/html" title="Joining Fauna and the future of streamy-db" /><published>2019-02-20T13:05:56+00:00</published><updated>2019-02-20T13:05:56+00:00</updated><id>https://domsj.info/2019/02/20/joining-fauna</id><content type="html" xml:base="https://domsj.info/2019/02/20/joining-fauna.html">&lt;p&gt;I’m happy to announce that my side project helped me land a great opportunity and that
&lt;strong&gt;I’ll be joining &lt;a href=&quot;https://fauna.com/&quot;&gt;Fauna&lt;/a&gt;&lt;/strong&gt; shortly. I think they are doing lots of great
stuff already, and will be happy help them further build out their &lt;strong&gt;operational database
(data platform) of the future&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;This does mean there won’t be much progress on &lt;a href=&quot;https://github.com/domsj/streamy-db&quot;&gt;streamy-db&lt;/a&gt; from my side.
However, since it’s open source, everyone remains welcome to take it and modify it as they see fit!&lt;/p&gt;</content><author><name></name></author><summary type="html">I’m happy to announce that my side project helped me land a great opportunity and that I’ll be joining Fauna shortly. I think they are doing lots of great stuff already, and will be happy help them further build out their operational database (data platform) of the future!</summary></entry><entry><title type="html">On ES/CQRS: the good, the bad, an alternative</title><link href="https://domsj.info/2019/01/13/on-es-cqrs-good-bad-alternative.html" rel="alternate" type="text/html" title="On ES/CQRS: the good, the bad, an alternative" /><published>2019-01-13T11:00:01+00:00</published><updated>2019-01-13T11:00:01+00:00</updated><id>https://domsj.info/2019/01/13/on-es-cqrs-good-bad-alternative</id><content type="html" xml:base="https://domsj.info/2019/01/13/on-es-cqrs-good-bad-alternative.html">&lt;p&gt;Recently I saw yet another &lt;a href=&quot;https://dev.to/skurfuerst/event-sourcing-and-cqrs-4mo0&quot;&gt;blog post&lt;/a&gt; explaining and
advocating for ES/CQRS, and of course also &lt;a href=&quot;https://www.merriam-webster.com/dictionary/rationalize&quot;&gt;rationalising&lt;/a&gt;
and trying to work around its shortcomings (no transactions, eventual consistency).&lt;/p&gt;

&lt;p&gt;Fwiw, the post is well written, and I don’t want to pick on it. It just happens to be the latest one I encountered.&lt;/p&gt;

&lt;p&gt;I don’t intend to explain ES/CQRS here. I’m not even going to write out the acronyms.
The post assumes you know a bit about the subject already.&lt;/p&gt;

&lt;h2 id=&quot;the-good&quot;&gt;The good&lt;/h2&gt;

&lt;p&gt;ES/CQRS comes with a set of nice properties:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;allows building scalable systems&lt;/li&gt;
  &lt;li&gt;all changes are captured in the event log, so history can be audited, reinstated, …&lt;/li&gt;
  &lt;li&gt;allows separate query models/databases&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-bad&quot;&gt;The bad&lt;/h2&gt;

&lt;p&gt;Unfortunately there are also a few downsides:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;eventual consistency for the query components&lt;/li&gt;
  &lt;li&gt;lack of transactions involving multiple entities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some of that last point is also known as the 
&lt;a href=&quot;https://duckduckgo.com/?q=event+sourcing+set+validation&amp;amp;t=canonical&amp;amp;ia=web&quot;&gt;ES set validation&lt;/a&gt; problem.
There’s basically a lot of talk about ways to work around the lack of transactions. They boil down to the following:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;choose your aggregates differently (bigger?)&lt;/li&gt;
  &lt;li&gt;rethink your business domain&lt;/li&gt;
  &lt;li&gt;use sagas&lt;/li&gt;
  &lt;li&gt;“but but … why do you want to do this, there are off the shelve solutions for this”&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my opinion these are all quite weak.
(I shouldn’t have to do the first 2, number 3 is a way of building your own custom transactions,
 and that last one I barely want to go into, we should be more ambitious and use approaches that enable more use cases!)&lt;/p&gt;

&lt;p&gt;It seems to me that ES/CQRS proponents believe that transactions are not scalable,
and from that position they want to argue that you don’t need them.&lt;/p&gt;

&lt;p&gt;For me however it’s quite clear: transactions are a huge convenience,
and if you’re using a system without them you’re making life needlessly hard on yourself.
If you’re not convinced about that, go read
FoundationDBs &lt;a href=&quot;https://apple.github.io/foundationdb/transaction-manifesto.html&quot;&gt;Transaction Manifesto&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://cloud.google.com/spanner/&quot;&gt;Also&lt;/a&gt;
by &lt;a href=&quot;https://www.yugabyte.com/&quot;&gt;now&lt;/a&gt; it
&lt;a href=&quot;https://fauna.com/&quot;&gt;has&lt;/a&gt;
&lt;a href=&quot;https://github.com/pingcap/tidb&quot;&gt;become&lt;/a&gt;
&lt;a href=&quot;https://www.foundationdb.org/&quot;&gt;clear&lt;/a&gt;
&lt;a href=&quot;https://github.com/domsj/streamy-db&quot;&gt;that&lt;/a&gt;,
&lt;a href=&quot;https://www.da-platform.com/streaming-ledger&quot;&gt;transactions&lt;/a&gt;
&lt;a href=&quot;https://dgraph.io/&quot;&gt;do&lt;/a&gt;
&lt;a href=&quot;https://www.cockroachlabs.com/&quot;&gt;scale&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;an-alternative&quot;&gt;An alternative&lt;/h2&gt;

&lt;p&gt;So what should one do if one wants the upsides of ES/CQRS, yet have transactions available?&lt;/p&gt;

&lt;p&gt;The route I would choose is starting with a scalable transactional database (e.g. one of the above)
that has built-in &lt;a href=&quot;https://www.flydata.com/blog/what-change-data-capture-cdc-is-and-why-its-important/&quot;&gt;change data capture (CDC)&lt;/a&gt;.
(Note: I haven’t verified if all of the above allow CDC.)
Built-in CDC basically means that the database exposes a stream of (timestamped) events for all changes that are made to
the database state.&lt;/p&gt;

&lt;p&gt;Using the change data capture one can (relatively easy) build up alternative query models,
export the data into other systems, keep old versions around, …&lt;/p&gt;

&lt;p&gt;In short, I think one can get the ES/CQRS positives without throwing transactions away.&lt;/p&gt;

&lt;p&gt;(Or find yourself the ultimate database that offers all of this in a convenient package.)&lt;/p&gt;

&lt;h3 id=&quot;what-about-eventual-consistency-of-the-separate-query-components&quot;&gt;What about eventual consistency of the separate query components?&lt;/h3&gt;

&lt;p&gt;That is indeed still a problem in the above approach.&lt;/p&gt;

&lt;p&gt;I could try to argue here about whether you really need separate query components …
and probably some people have them for wrong reasons, but there are certainly some good reasons too.
Such as dumping everything into your data lake / analytical database.
Or pushing the data into a system that is good at doing full text searches.&lt;/p&gt;

&lt;p&gt;So can we solve that problem too? Yes, I believe we can!&lt;/p&gt;

&lt;p&gt;Using the watermarks concept from stream processing it should be possible to know what the propagation delay is towards
this other system. And once you can track it, you can also wait it out.&lt;/p&gt;

&lt;p&gt;My hope is to eventually do some work towards this in my
&lt;a href=&quot;https://github.com/domsj/streamy-db&quot;&gt;streamy-db project&lt;/a&gt;
(&lt;a href=&quot;https://domsj.info/2018/12/30/introducing-streamy-db.html&quot;&gt;introductory blog&lt;/a&gt;).&lt;/p&gt;</content><author><name></name></author><summary type="html">Recently I saw yet another blog post explaining and advocating for ES/CQRS, and of course also rationalising and trying to work around its shortcomings (no transactions, eventual consistency).</summary></entry><entry><title type="html">Introducing streamy-db: a deterministic database on top of a stream processing engine</title><link href="https://domsj.info/2018/12/30/introducing-streamy-db.html" rel="alternate" type="text/html" title="Introducing streamy-db: a deterministic database on top of a stream processing engine" /><published>2018-12-30T17:12:56+00:00</published><updated>2018-12-30T17:12:56+00:00</updated><id>https://domsj.info/2018/12/30/introducing-streamy-db</id><content type="html" xml:base="https://domsj.info/2018/12/30/introducing-streamy-db.html">&lt;p&gt;In this post I’ll introduce &lt;a href=&quot;https://github.com/domsj/streamy-db&quot;&gt;streamy-db&lt;/a&gt; and explain the theory behind it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: streamy-db is distributed deterministic (&lt;a href=&quot;http://cs-www.cs.yale.edu/homes/dna/papers/calvin-sigmod12.pdf&quot;&gt;Calvin&lt;/a&gt; inspired)
database (transaction processing system) implemented on top of a stream processing framework 
(apache &lt;a href=&quot;https://beam.apache.org/&quot;&gt;Beam&lt;/a&gt; / &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Flink&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Currently it’s in an early prototype phase, but the possibilities are endless.
My hope is to motivate some of you to join me on the journey!&lt;/p&gt;

&lt;p&gt;I’ll first introduce Calvin, next get to the essence of deterministic transaction processing,
then give a short description of stream processing,
and finally we’ll get to a description of how the prototype is implemented and what lays ahead.&lt;/p&gt;

&lt;h2 id=&quot;calvin-fast-distributed-transactions-for-partitioned-database-systems&quot;&gt;Calvin: Fast Distributed Transactions for Partitioned Database Systems&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://cs-www.cs.yale.edu/homes/dna/papers/calvin-sigmod12.pdf&quot;&gt;This 2012 paper&lt;/a&gt;
describes a scalable deterministic database system.&lt;/p&gt;

&lt;p&gt;Deterministic databases solve the (distributed) transaction processing problem in an interesting way.
As a first step a global ordering is determined for all transactions.
Next this globally ordered stream of transactions is processed as if all transactions were executed sequentially.&lt;/p&gt;

&lt;p&gt;Any non deterministic effects such as generating a random number or getting the current time
have to be done beforehand, and stored as part of the transaction inputs.
This has as an advantage that there could be multiple systems (in different datacenters)
processing the same transactional input stream independently, yet arrive at the same outputs.
It also makes recovering from failures easier and cheaper.&lt;/p&gt;

&lt;p&gt;For more info about these kind of systems I definitely suggest having a look at the &lt;a href=&quot;http://dbmsmusings.blogspot.com/&quot;&gt;blog&lt;/a&gt; of Daniel Abadi, he has written some interesting blogs related to the topic.&lt;/p&gt;

&lt;p&gt;Calvin splits the system in three separate layers:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;sequencing layer: determines a global order for all transactions&lt;/li&gt;
  &lt;li&gt;scheduling layer: orchestrates transaction execution (this is the tricky part)&lt;/li&gt;
  &lt;li&gt;(non transactional) storage layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next it shows how these 3 layers can each be scaled independently.&lt;/p&gt;

&lt;p&gt;In short, the sequencing layer can be scaled by having multiple streams in parallel, and periodically committing the offsets of the individual streams into a ‘master’ stream.&lt;/p&gt;

&lt;p&gt;The storage layer could be basically any scalable (non transactional) nosql key-value store.&lt;/p&gt;

&lt;p&gt;The scheduling layer calculates the result for a batch of transactions (e.g. every 10ms).
Each executor is reponsible for a shard of the key range.
Processing is done in the following phases:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;send each transaction of the batch to all involved executors&lt;/li&gt;
  &lt;li&gt;on each executor read all keys for the transactions of the current batch&lt;/li&gt;
  &lt;li&gt;send read results to other executors&lt;/li&gt;
  &lt;li&gt;all executors can now calculate result (commit/abort) for the transaction they were involved in&lt;/li&gt;
  &lt;li&gt;send output to the storage layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would call this a micro-batched approach (in contrast to a streaming approach),
in which sharding is done on the granularity of a shard/executor.
Probably I’m not explaining it entirely correctly, but instead of dwelling on that,
let us be inspired by this approach, yet take a step back.&lt;/p&gt;

&lt;h2 id=&quot;the-essence-of-deterministic-transaction-processing&quot;&gt;The essence of deterministic transaction processing&lt;/h2&gt;

&lt;p&gt;Let us consider for a moment what it means to process a transaction T at timestamp t0 in deterministic system:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;read inputs at t0&lt;/li&gt;
  &lt;li&gt;determine transaction results&lt;/li&gt;
  &lt;li&gt;write outputs at t0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Graphically this would look as follows:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/introducing-streamy-db/deterministic-transaction-v1.png&quot; alt=&quot;deterministic transaction&quot; /&gt;&lt;/p&gt;

&lt;p&gt;I’m a bit vague in the above about what ‘read at t0’ and ‘write at t0’ means. Conceptually it happens at the same time.
The easiest implementation is that all transactions are processed sequentially.
In reality latency is everywhere, and we want many transactions to be able to process concurrently.&lt;/p&gt;

&lt;p&gt;In order to allow processing multiple transactions concurrently while still ensuring correctness we must disentangle our description a bit:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;read inputs and lock outputs at logical time t0&lt;/li&gt;
  &lt;li&gt;determine transaction results&lt;/li&gt;
  &lt;li&gt;write outputs at logical time t0, releasing the earlier locks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or in picture form:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/introducing-streamy-db/deterministic-transaction-v2.png&quot; alt=&quot;deterministic transaction&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With this new view of what processing a transaction means it now becomes clear for each key we’ll need
a little state machine that allow reading the value at a certain timestamp, locking at a timestamp,
and writing a value to release an earlier lock.&lt;/p&gt;

&lt;p&gt;Let us now define what a transaction could look like on e.g. a simple key-value store:&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;class Transaction {
  Map[Key, Option[Value]] asserts
  Map[Key, Option[Value]] updates
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;A transaction expresses that assuming some current state of the database &lt;code class=&quot;highlighter-rouge&quot;&gt;Map[Key, Option[Value]] asserts&lt;/code&gt;
it should be updated with a set of writes &lt;code class=&quot;highlighter-rouge&quot;&gt;Map[Key, Option[Value]] updates&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Given this model, and the earlier description of what it means to process an transaction, and the need for a mini state machine per key (which I’ve called KeyTransactionProcessor),
what we would now like to achieve is the equivalent of the following picture:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/introducing-streamy-db/deterministic-transaction-v3.png&quot; alt=&quot;basic data flow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In which this flow is continuously ongoing for many transactions at once.&lt;/p&gt;

&lt;p&gt;How can we implement such a system (efficiently), and also how we would handle failures?&lt;/p&gt;

&lt;p&gt;To solve that problem, let us take a short look at stream processing, and the concept of event-time and watermarks.&lt;/p&gt;

&lt;h2 id=&quot;stream-processing&quot;&gt;Stream processing&lt;/h2&gt;

&lt;p&gt;The stream processing model comes from &lt;a href=&quot;https://cloud.google.com/dataflow/&quot;&gt;Google Cloud Dataflow&lt;/a&gt;, and is now known as &lt;a href=&quot;https://beam.apache.org/&quot;&gt;Apache Beam&lt;/a&gt;.
One particularly interesting implementation of this model is &lt;a href=&quot;https://flink.apache.org/&quot;&gt;Apache Flink&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Recommended literature: &lt;a href=&quot;https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-101&quot;&gt;Streaming 101&lt;/a&gt;
and &lt;a href=&quot;https://www.oreilly.com/ideas/the-world-beyond-batch-streaming-102&quot;&gt;102&lt;/a&gt;.
I’ll give a very short summary here.&lt;/p&gt;

&lt;p&gt;Stream processing is the latest incarnation of big data processing tools.
It is an improvement upon the micro-batching offered by technologies such Apache Spark
(which itself was an improvement over map-reduce style computations such as in Hadoop).&lt;/p&gt;

&lt;p&gt;Stream processing allows parallel execution of map, reduce and keyBy operations.
It has low latency, built-in failure recovery capabilities, and can do at-least-once or exactly-once processing.&lt;/p&gt;

&lt;p&gt;There is also the option of having more generic stateful operators, see &lt;a href=&quot;https://beam.apache.org/blog/2017/02/13/stateful-processing.html&quot;&gt;here&lt;/a&gt; for a good description.&lt;/p&gt;

&lt;p&gt;An important (powerful) concept in stream processing engines is the concept of event time and their associated watermarks.
Event time allows timestamping the events (data) that is be analyzed, such that the events can be grouped into e.g. fixed time windows, sliding windows, (user) sessions, …&lt;/p&gt;

&lt;p&gt;Now, if you want to group data that is arriving in a stream, that may be partially out of order, into windows, you must be able to know when you’ve seen all data. This is what watermarks are for.
Watermarks flow (at least conceptually) as part of a (parallel) datastream, and allow operators to know (have guarantees) about completeness of the input (up to a certain point).
A good explanation can be found in the &lt;a href=&quot;https://ci.apache.org/projects/flink/flink-docs-release-1.7/dev/event_time.html#watermarks-in-parallel-streams&quot;&gt;flink documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This short description is probably not enough for people unfamiliar with the concept, so I do recommend to check out some of the links.&lt;/p&gt;

&lt;p&gt;A good understanding of stateful stream processing and event-time/watermarks is probably beneficial going from this point forward.
So do click through on those latest 2 links if you feel that I’m going too fast in the next sections.&lt;/p&gt;

&lt;p&gt;That being said, let’s circle back to deterministic transaction processing.&lt;/p&gt;

&lt;h2 id=&quot;implementation-on-top-of-a-stream-processing-engine&quot;&gt;Implementation on top of a stream processing engine&lt;/h2&gt;

&lt;p&gt;First we consider the input of our deterministic system: the recording and ordering of transactions to be executed.
This can be easily done using e.g. a partitioned kafka topic which timestamps all records using log append time.
A flink job (or beam pipeline) can read and process the different partitions of the topic in parallel.
(Additionally we also add a heartbeat generator to ensure watermarks for all partitions are generated periodically, so the streaming computation keeps on making progress)&lt;/p&gt;

&lt;p&gt;Recapping from earlier, for the transaction processing we want to implement the equivalent of this picture:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/introducing-streamy-db/deterministic-transaction-v3.png&quot; alt=&quot;basic data flow&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In a more stream processy view we can draw it as follows:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/introducing-streamy-db/deterministic-transaction-stream-view.png&quot; alt=&quot;stream processing view&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We can now identify multiple streams:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;incoming transactions&lt;/li&gt;
  &lt;li&gt;read-requests and write-locks&lt;/li&gt;
  &lt;li&gt;read-results&lt;/li&gt;
  &lt;li&gt;write results&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also see that the KeyTransactionProcessor node that we introduced takes 2 inputs.
So it must union/connect 2 streams. However the read-requests and write-locks stream (2) could
be partially out of order! By default the stream processor only gives us guarantees whenever
we see a watermark. And if we mix in the write results stream (4), then we can no longer reason
about the watermarks of stream 2.&lt;/p&gt;

&lt;p&gt;To solve this problem we introduce the
&lt;a href=&quot;https://github.com/domsj/streamy-db/blob/66e014304d36ee7bf6b076bb552c869e59ad4b2f/modules/runners/flink/src/main/scala/domsj/streamy/db/flink/StreamyDb.scala#L35-L64&quot;&gt;KeyedEventTimeSorter&lt;/a&gt;.
It is a stateful keyed processing function, that use local state and watermarks (eventtime based timers)
to order stream 2.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../../images/introducing-streamy-db/deterministic-transaction-stream-view2.png&quot; alt=&quot;stream processing view 2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Now we have all components in place. The circle is a relatively simple map step.
The other components are described below.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/domsj/streamy-db/blob/66e014304d36ee7bf6b076bb552c869e59ad4b2f/modules/runners/flink/src/main/scala/domsj/streamy/db/flink/StreamyDb.scala#L66-L129&quot;&gt;KeyTransactionProcessor&lt;/a&gt;,
for each key:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;keeps state (associated value, if any)&lt;/li&gt;
  &lt;li&gt;should receive an ordered stream of timestamped read requests and write locks&lt;/li&gt;
  &lt;li&gt;receives a stream of transaction results (to complete a corresponding write lock)&lt;/li&gt;
  &lt;li&gt;for read locks (requests) wait to send out the read result until there are no more preceding write locks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/domsj/streamy-db/blob/66e014304d36ee7bf6b076bb552c869e59ad4b2f/modules/runners/flink/src/main/scala/domsj/streamy/db/flink/StreamyDb.scala#L131-L175&quot;&gt;TransactionProcessor&lt;/a&gt;,
for each transaction:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;receives all reads&lt;/li&gt;
  &lt;li&gt;receives the transaction (asserts &amp;amp; updates)&lt;/li&gt;
  &lt;li&gt;outputs the result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is 1 detail not yet described: how are the write results fed back into the KeyTransactionProcessors?
Streaming computations form a DAG (directed acyclic graph), and flink/beam seem to offer no Pipe component (that I could find).&lt;/p&gt;

&lt;p&gt;In the prototype this is solved by using a extra kafka topic that is both read from and written to in the job.
I’m sure that using a ‘Rich’, ‘Checkpointed’, ‘Sink’, ‘Source’ function (to use some flink specific terminology)
it should be possible to achieve the same in a more efficient manner, but that is work that remains to be done.&lt;/p&gt;

&lt;p&gt;If you clicked on the links above you’ll see that in less than 500 lines we were able to express the entire logic that makes our system.
Which is surprisingly little code for what it achieves (scalable transaction processing).
I’m big believer of less is more, so for me this is a beautiful result.&lt;/p&gt;

&lt;p&gt;It maximally leverages the stream processing framework to do the heavy lifting.
This has some pros and cons.&lt;/p&gt;

&lt;p&gt;cons:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;less customisable (e.g. towards quality of service for different users or which stream you would actually want it to prioritise…)&lt;/li&gt;
  &lt;li&gt;no built in range based sharding (although one could implement a tree/index as a layer on top?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;pros:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;all the stream processing pros (low latency, built in failure recovery, scalable, …)&lt;/li&gt;
  &lt;li&gt;fewer chances of bugs, as there isn’t much code&lt;/li&gt;
  &lt;li&gt;built-in &lt;a href=&quot;https://en.wikipedia.org/wiki/Change_data_capture&quot;&gt;change data capture&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;integrates perfectly with other stream processing needs you may have&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;recap-and-taking-a-step-back&quot;&gt;Recap, and taking a step back&lt;/h2&gt;

&lt;p&gt;So what have we done? How does it work?&lt;/p&gt;

&lt;p&gt;The trick lies in the KeyedEventTimeSorter. It enables sending read-requests and write-locks to the KeyTransactionProcessor
on specific (logical) timestamps, and leave the rest of the system maximal flexibility (freedom, leeway) to schedule
processing whenever it is most convenient. The KeyTransactionProcessor implements the deterministic locking scheme.&lt;/p&gt;

&lt;p&gt;Of course there’s nothing special about read-request or write-lock messages.
We could for example also send a get-next-id message, with the obvious semantics, that don’t result in locking
(aka waiting for a write result to finally arrive).
With some creativity plenty of other messages are possible too, and we can look at the KeyTransactionProcessor as a more
generic (virtual) actor.&lt;/p&gt;

&lt;h2 id=&quot;related-work&quot;&gt;Related work&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.da-platform.com/streaming-ledger&quot;&gt;Streaming ledger&lt;/a&gt; from da-platform (aka data artisans), the main developers of apache flink.
They don’t say how it works, but I can only guess that it’s quite similar to this approach.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://fauna.com/&quot;&gt;FaunaDB&lt;/a&gt; is based on Calvin, so also a deterministic database. However theirs is (as far as I understood) not a streaming implementation.&lt;/p&gt;

&lt;h2 id=&quot;whats-next&quot;&gt;What’s next&lt;/h2&gt;

&lt;p&gt;Well, there are plenty of existing databases on the market already… and I’m just a 1 man army.
But I really believe this approach has much potential. And I hope I’ve been able to convince at least some of you of that.&lt;/p&gt;

&lt;p&gt;If other people want to jump in, then there’s many things we can do together.&lt;/p&gt;

&lt;h3 id=&quot;short-term-more-practical-improvements&quot;&gt;Short term more practical improvements&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;push (part of) this documentation-by-blog-post into the repository&lt;/li&gt;
  &lt;li&gt;build simple key-value rest API to allow submitting transactions, reading keys. show example usage&lt;/li&gt;
  &lt;li&gt;build a better pipe mechanism that doesn’t require an extra kafka topic (to feed back write results to the KeyTransactionProcessors)&lt;/li&gt;
  &lt;li&gt;implement model where KeyTransactionProcessors send all read results to each other
(for transactions with few keys this is probably an optimisation,
 it’s also closer to the original Calvin description)&lt;/li&gt;
  &lt;li&gt;package the code for easy distribution (jar on maven, image on dockerhub, …)&lt;/li&gt;
  &lt;li&gt;tests! (correctness &amp;amp; performance)&lt;/li&gt;
  &lt;li&gt;expose flink queryable state&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;longer-term-more-dreamy-and-inspiring-goals&quot;&gt;Longer term more dreamy and inspiring goals&lt;/h3&gt;

&lt;blockquote&gt;
  &lt;p&gt;If you want to build a ship, don’t drum up the men to gather wood, divide the work, and give orders. Instead, teach them to yearn for the vast and endless sea.&lt;/p&gt;

  &lt;p&gt;– &lt;cite&gt;Antoine de Saint-Exupéry&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I guess these only make sense after doing most of the short term goals,
but it’s important to already start thinking about them now.
The descriptions may be a bit terse, but that’s intentional.
I want to leave plenty of space for other people to join in with their ideas.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;implement ‘layers’ (e.g. document, graph, sql) on top of the key-value model&lt;/li&gt;
  &lt;li&gt;virtual actors model (a la &lt;a href=&quot;https://dotnet.github.io/orleans/&quot;&gt;orleans&lt;/a&gt; or &lt;a href=&quot;https://github.com/Microsoft/AMBROSIA&quot;&gt;AMBROSIA&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;multiversioned database backend to allow strongly consistent reads at arbitrary point in the past&lt;/li&gt;
  &lt;li&gt;keep state mainly in a separate backend (e.g. the multiversioned one from the previous bullet point),
and only load state into the stream processor for keys participating in transactions?&lt;/li&gt;
  &lt;li&gt;… ???&lt;/li&gt;
  &lt;li&gt;profit :)?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;closing-words&quot;&gt;Closing words&lt;/h2&gt;

&lt;p&gt;Come and join me at
&lt;a href=&quot;https://github.com/domsj/streamy-db&quot;&gt;https://github.com/domsj/streamy-db&lt;/a&gt;,
we’ll have fun together!&lt;/p&gt;</content><author><name></name></author><summary type="html">In this post I’ll introduce streamy-db and explain the theory behind it.</summary></entry></feed>