<turbo-stream action="append" target="posts_list"><template>    <div class="postbit" id="371280" data-post-id="371280">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jallum" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/120/38850_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jallum
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="10" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>I have been looking into this, it’s definitely not easy. A storage engine benefits from a low-level language like C/Zig/Rust for a number of reasons. Erlang also apparently lacks direct I/O support, which I found pretty surprising (even <em>Node</em> supposedly supports <code>O_DIRECT</code>, though I honestly wonder if anyone has ever used it). But maybe the mandatory aligned buffers are a problem, I don’t know. I’ve been meaning to make a thread about that.</p>
</blockquote>
</aside>
<p>Yeah, the closest you get in erlang is :raw, which just isn’t the same – you still have the OS buffer-cache in the mix.</p>
<p>Another big issue is the copy-on-write semantics of the BEAM itself, and the fact that it’s hard to <em>share</em> any type of data between processes (that isn’t a binary &gt;= 64 bytes) without <em>copying it</em>… so, an in-memory index structure would have to be designed so that one process can own the structure for a shard, find a binary page quickly and hand that off to other processes to finish the search.</p>
<aside class="quote no-group" data-username="garrison" data-post="10" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>For the record, Apple seems to be spending quite a bit of dev time trying to replace Redwood with RocksDB if you pay attention to the commits, though of course in typical FDB fashion they have said absolutely nothing about why. I think Redwood might have been a Snowflake project and it doesn’t seem like they contribute anymore. Apple is probably storing considerably more data in FDB than Snowflake so if I had to guess I’d say they want to cut down SSD costs with an LSM as Btrees can be faster but are more wasteful (SSDs are consumables at scale).</p>
</blockquote>
</aside>
<p>I’d noticed them quietly doing this, too. I saw <a href="https://forums.foundationdb.org/t/is-the-rocksdb-storage-engine-ready-for-production-use/4594/8" rel="noopener nofollow ugc">one thread</a> talking about some of the reason being the ability to just ship around files instead of always having to copy shards through <em>protocol</em>. Nothing stops anyone from just making a rocksdb-based storage server implementation on Bedrock. It’d be pretty easy.</p>
<p>LSM trees have their own design issues, though. Compaction is greedy for space, and worst-case, you need 2gb free to compact two,1gb levels. Since compaction proceeds head-to-tail, and there’s no way to truncate a file from the <em>head</em>, you basically have to keep the old files until you’ve completely built the new one.</p>
<aside class="quote no-group" data-username="garrison" data-post="10" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>Redwood is a pretty recent storage engine. It might actually be the <em>most</em> recent Btree storage engine out there, at least of those operating at any appreciable scale.</p>
</blockquote>
</aside>
<p>It <em>is</em>, and b-trees are fantastic. No argument. They’re just not the only game in town. There are LSM trees (as mentioned) and then there’s Aerospike which takes a very different approach and uses <em>neither</em>. <a href="https://www.usenix.org/system/files/conference/fast16/fast16-papers-lu.pdf" rel="noopener nofollow ugc">WiscKey</a> is another interesting take on optimizing for SSDs.</p>
<p>The BEAM definitely has some weaknesses that make direct implementation of things like b-trees problematic <em>anyway</em> (no good way to do in-place rewrites of the pages in memory is the big one)… but the BEAM has some <em>strengths</em>, too. The enforced immutability makes it possible to safely use some pretty extreme structure-sharing between versions of an in-memory index… and it also allows for some ridiculous levels of memory safe parallelism. I think that with the shift to SSDs, the ability to issue 30+ parallel reads may be an interesting way to offset. The WiscKey paper has some really good numbers on this.</p>
<p>The <em>other</em> thing to think about is that reading from the disk (ssd) isn’t always the bottleneck anymore. Pushing things across the network is. So, if we can pull the storage closer to the processes that are using it, we could see some gains that more than offset.</p>
<aside class="quote no-group" data-username="garrison" data-post="10" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>Do you have more info about this? I don’t know much about TiDB.</p>
</blockquote>
</aside>
<aside class="onebox allowlistedgeneric" data-onebox-src="https://docs.pingcap.com/tidb/stable/titan-overview/">
  <header class="source">
      <img src="https://docs.pingcap.com/static/c565fb7871c56153a32359ea1b1b284c/favicon-48x48.ico" class="site-icon" alt="" width="48" height="48">

      <a href="https://docs.pingcap.com/tidb/stable/titan-overview/" target="_blank" rel="noopener nofollow ugc">docs.pingcap.com</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:690/362;"><img src="https://docs-download.pingcap.com/media/images/opengraph.jpg" class="thumbnail" alt="" width="690" height="362"></div>

<h3><a href="https://docs.pingcap.com/tidb/stable/titan-overview/" target="_blank" rel="noopener nofollow ugc">Titan Overview</a></h3>

  <p>Learn the overview of the Titan storage engine.</p>


  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>
<aside class="onebox allowlistedgeneric" data-onebox-src="https://aws.amazon.com/blogs/storage/how-pingcap-transformed-tidb-into-a-serverless-dbaas-using-amazon-s3-and-amazon-ebs/">
  <header class="source">
      <img src="https://a0.awsstatic.com/main/images/site/fav/favicon.ico" class="site-icon" alt="" width="16" height="16">

      <a href="https://aws.amazon.com/blogs/storage/how-pingcap-transformed-tidb-into-a-serverless-dbaas-using-amazon-s3-and-amazon-ebs/" target="_blank" rel="noopener nofollow ugc" title="08:02AM - 14 November 2023">Amazon Web Services – 14 Nov 23</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:680/356;"><img src="https://d2908q01vomqb2.cloudfront.net/e1822db470e60d090affd0956d743cb0e7cdf113/2019/10/07/Amazon-EBS-Featured-Image.jpeg" class="thumbnail" alt="" width="680" height="356"></div>

<h3><a href="https://aws.amazon.com/blogs/storage/how-pingcap-transformed-tidb-into-a-serverless-dbaas-using-amazon-s3-and-amazon-ebs/" target="_blank" rel="noopener nofollow ugc">How PingCAP transformed TiDB into a serverless DBaaS using Amazon S3 and...</a></h3>

  <p>PingCAP, an AWS Partner Network (APN) Partner, is the company behind TiDB, an advanced open-source, distributed SQL database for building modern applications. TiDB is widely used and trusted by technologists around the world. In July 2023, PingCAP...</p>


  </article>

  <div class="onebox-metadata">
    
    
  </div>

  <div style="clear: both"></div>
</aside>

<aside class="quote no-group" data-username="garrison" data-post="10" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>I think LSM compaction is generally outside the critical path, though, which is why you get into so much trouble when they fall behind. There is actually one exception I’m aware of: Tigerbeetle’s LSM deterministically interleaves compaction with writes, which is very clever.</p>
</blockquote>
</aside>
<p>As it is generally implemented, yeah. Systems like TiDB don’t <em>do</em> compaction on the storage nodes, though, they write new levels, and at the same time, push those to other machines for compaction. When newly compacted levels are available, they pull them back and just delete the intermediates. It’s a clever way to throw more iron at the problem and it works into their replication/persistence strategy.</p>
<p>(Tigerbeetle <em>is</em> pretty slick!)</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371280" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/12">Post #11</a>
	                </div>
	            </div>
              <div id="likers-container-371280" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371280"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #11"></div>
  </section>
</div>
    <div class="postbit" id="371315" data-post-id="371315">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="jallum" data-post="12" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>the reason being the ability to just ship around files instead of always having to copy shards through <em>protocol</em>.</p>
</blockquote>
</aside>
<p>Yeah, that’s another reason. The btrees (Redwood) actually take data movement as an opportunity to defragment the tree so they would lose that by physically copying shards. LSMs have a natural anti-entropy mechanism (the compaction) which makes that less of an issue. Another advantage is that you can easily push the MVCC down into the LSM and then GC old keys during compaction. You can do this with a btree as well but it’s a little less natural and there would be a lot more space-amp. In practice you can <a href="https://www.usenix.org/system/files/login/articles/login_oct15_05_bender.pdf" rel="noopener nofollow ugc">design btrees with log-structured nodes</a>, though, so there’s a spectrum at play here.</p>
<aside class="quote no-group" data-username="jallum" data-post="12" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>I think that with the shift to SSDs, the ability to issue 30+ parallel reads may be an interesting way to offset. The WiscKey paper has some really good numbers on this.</p>
</blockquote>
</aside>
<p>Oh yes absolutely. WiscKey’s learnings were <a href="https://github.com/facebook/rocksdb/wiki/BlobDB" rel="noopener nofollow ugc">eventually incorporated into RocksDB</a>. I’ve always loved that paper not only because it’s great research but also because it has a <em>fantastic</em> LSM tree overview in the introduction.</p>
<p>Btrees benefit hugely from parallel reads as well, of course. One of the big wins of Redwood iirc was keeping a much deeper queue than the old SQLite btree.</p>
<p>Btw, another recent btree is CedarDB’s storage engine. Here’s <a href="https://cedardb.com/blog/optimistic_btrees/" rel="noopener nofollow ugc">a good article</a> about it. You can <a href="https://umbra-db.com/" rel="noopener nofollow ugc">find their papers here</a>, really good stuff.</p>
<aside class="quote no-group" data-username="jallum" data-post="12" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>The <em>other</em> thing to think about is that reading from the disk (ssd) isn’t always the bottleneck anymore. Pushing things across the network is.</p>
</blockquote>
</aside>
<p>Strangely I think network bandwidth has exceeded disk again in recent years (800Gb ethernet). But latency still matters a lot in practice, of course, so it’s never that simple.</p>
<aside class="quote no-group" data-username="jallum" data-post="12" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>Systems like TiDB don’t <em>do</em> compaction on the storage nodes, though, they write new levels, and at the same time, push those to other machines for compaction.</p>
</blockquote>
</aside>
<p>That is very interesting, thanks. I guess they’re doing the “disaggregated storage on S3” thing so it doesn’t really matter where the tables get compacted. Interesting implications for a multitenant system!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371315" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/13">Post #12</a>
	                </div>
	            </div>
              <div id="likers-container-371315" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371315"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #12"></div>
  </section>
</div>
    <div class="postbit" id="371758" data-post-id="371758">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="PaulOlukayode" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/PaulOlukayode/120/38640_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  PaulOlukayode
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Nice! I am also building a scalable messaging protocol, and we are about 90% complete. The Elixir supervisor tree is fantastic, and OTP is a real game-changer. We built this on top of Elixir Cowboy due to its legacy support for low-level TCP transport.</p>
<p>This application is one of the most unique systems I have ever built. Elixir gives me the flexibility to combine multiple GenServers to manage child-level dynamic supervisors and a mother-level supervisor.</p>
<p>I will come share my experience one day on the forum. The application root folder is named after me and includes a special ID called <code>eid_epoi_id</code>. It can be clustered using the Horde dependency, which allows the use of a global registry. The global registry helps scale the application for load balancing and consistency.</p>
<p>Check it out here: <a href="https://github.com/olukayodepaul/epoi-socket.git" class="inline-onebox" rel="noopener nofollow ugc">GitHub - olukayodepaul/epoi-socket · GitHub</a></p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371758" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/14">Post #13</a>
	                </div>
	            </div>
              <div id="likers-container-371758" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371758"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #13"></div>
  </section>
</div>
    <div class="postbit" id="371780" data-post-id="371780">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Asd" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Asd
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi, thanks for the library, it’s really nice. Finally, some good and well documented Elixir to read! I love how Finalization is written <img src="https://forum.elixirforum.com/images/emoji/apple/heart.png?v=15" title=":heart:" class="emoji" alt=":heart:" loading="lazy" width="20" height="20"></p>
<p>I have some of comments:</p>
<ol>
<li>It seems that you spawn a <code>TransactionBuilder</code> server on every transaction (and every operation has to be made in transaction explicitly) and it is passed like a pid. Wouldn’t it make more sense to just make it a structure instead of a gen_server? Every operation with transaction builder is a just a mutation of it’s internal state which is accumulated until commit happens, so it can be just a plain structure. I may think of an argument that having a server makes it possible for multiple processes to work inside one transaction, but why would anyone do this? And even if they do, they would still require to do the explicit synchronization on their side, since <code>put</code> operation is a cast to <code>TransactionBuilder</code>. You may say “batch insert operations processed in parallel”, but that’s also much easier to handover to users so they just perform batching themselves.</li>
<li>It seems that every call to put or fetch would require to call the sequencer which is a repo-wide singleton. Wouldn’t it make more sense to utilize the ets table in this case? I mean, leave the singleton, but expose the <code>known_committed_version</code> in ets. This would remove the unnecessary global ordering of every read operation.</li>
<li>Possible optimization: you can keep your <code>Tx</code> mutations sorted so that range operations would become faster (it would require to just discover the upper bound in the list and then exit the recursive function, instead of traversing the whole list)</li>
<li>It seems to me (but I am not quite sure) that it is possible to get into this situation: <code>read "x = 1" in transaction 1, put "x = 2" transction 2, commit transaction 2, range read "x = 2" in transaction 1</code> because it looks like range reads don’t account for <code>Tx</code>’s <code>reads</code> since <code>tx_visible</code> variable contains only <code>Tx.writes</code>. I see that <code>Tx.get_range</code> is not used anywhere, so I am sorry for the comment on WIP code <img src="https://forum.elixirforum.com/images/emoji/apple/sweat_smile.png?v=15" title=":sweat_smile:" class="emoji" alt=":sweat_smile:" loading="lazy" width="20" height="20"></li>
<li>It seems that I missed the functional tests like “read after write” in a single transaction from the database user perspective.</li>
<li>I don’t understand the logic behind the finalization of empty batches in CommitProxy. If we don’t have any transactions, why do we need to perform this unnecessary operation of pushing no information to log servers, etc. ?</li>
<li>I can see that you use the <code>reply_fn = fn x -&gt; GenServer.reply(from, x) end</code> pattern, but I think that it would be better to just send the <code>from</code> and call the <code>GenServer.reply</code> where it’s needed. But that’s NIT.</li>
<li>It seems like links to docs in <code>@moduledoc</code>’s are incorrect.</li>
<li>I can see that you use <code>:dets</code> as the key value storage, but <code>dets</code> comes with it’s internal limitations, like it can close without ability to recover on open (I’ve experienced it myself several times)
<blockquote>
<p><a href="https://www.erlang.org/doc/apps/stdlib/dets.html" class="inline-onebox" rel="nofollow">dets — OTP 29.0.2 (stdlib 8.0.1)</a><br>
Dets tables are not properly closed if the Erlang runtime system terminates abnormally</p>
</blockquote>
</li>
<li>I can see that you use a lot of  <code>do_something_fn = Keyword.get(opts, :do_something_fn, &amp;do_something/1)</code> in code and you change this option only in testing. I can suggest to use the library like <a href="https://hexdocs.pm/repatch" rel="noopener nofollow ugc">Repatch</a> to just patch these functions in tests, instead of slowing down your critical path code with unnecessary branching which will never be used in any production.</li>
</ol>
<p>P.S. The code was not very easy to read <img src="https://forum.elixirforum.com/images/emoji/apple/sweat_smile.png?v=15" title=":sweat_smile:" class="emoji" alt=":sweat_smile:" loading="lazy" width="20" height="20">  Imports make it difficult to navigate to the function implementation (even with LSP) and single letter variables and abuse of <code>t</code> make it hard to understand what functions do when I get to them. I can see that you tend to split single gen server code into multiple files of <code>state</code>, <code>server</code>, etc., and that’s new to me, since I am more used to OTP and Elixir code style where everything related to some gen server resides in the single module and file (no matter how big it gets).</p>
<p>And I also find it interesting that you defined helpers for <code>GenServer.cast</code>, <code>GenServer.call</code> and callback tuples like <code>def noreply(state), do: {:noreply, state}</code>. I am interested to hear what’s the story behind them <img src="https://forum.elixirforum.com/images/emoji/apple/smile.png?v=15" title=":smile:" class="emoji" alt=":smile:" loading="lazy" width="20" height="20"></p>
<p>That’s all for now, I will read some more code tomorrow. And yeah, I am preparing a small PR with fixes and NITs I found while reading (like Enum |&gt; Enum optimizations and such).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371780" data-batch-url="/posts/batch_likers">
                        3
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/15">Post #14</a>
	                </div>
	            </div>
              <div id="likers-container-371780" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371780"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #14"></div>
  </section>
</div>
    <div class="postbit" id="371782" data-post-id="371782">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>I am not the author but I am very familiar with this architecture (because I have been building something similar) so I can answer a couple of these. Note also that this project is, of course, incomplete and there are several parts of it (from what I’ve seen reading through) that are still toy implementations. One neat thing about FDB’s unbundled architecture is that it’s possible to implement it as a toy and then flesh out the services piece by piece, which is what I did and what the OP is clearly doing as well - perfectly reasonable.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>Wouldn’t it make more sense to just make it a structure instead of a gen_server? Every operation with transaction builder is a just a mutation of it’s internal state which is accumulated until commit happens</p>
</blockquote>
</aside>
<p>It would, but a pure functional API is very dangerous because if you accidentally “lose” any of the intermediate structs (even on reads) you could end up with very serious consistency violations. A struct in the process dictionary would be ideal for most users IMO.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>It seems that every call to put or fetch would require to call the sequencer which is a repo-wide singleton. Wouldn’t it make more sense to utilize the ets table in this case?</p>
</blockquote>
</aside>
<p>The Sequencer is a <em>cluster</em>-wide singleton and has to be called over the network so ets would not be appropriate. All calls to it (for read or commit versions) are aggressively batched in FDB. If that’s not the case here it may simply be because the project is still WIP.</p>
<p>Also, I don’t think it’s possible to implement an atomic get+update with concurrent ets, which would be necessary here.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>Possible optimization: you can keep your <code>Tx</code> mutations sorted so that range operations would become faster</p>
</blockquote>
</aside>
<p>I don’t understand this one but I’m curious. Can you elaborate?</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>I don’t understand the logic behind the finalization of empty batches in CommitProxy. If we don’t have any transactions, why do we need to perform this unnecessary operation of pushing no information to log servers</p>
</blockquote>
</aside>
<p>In FDB the empty batches are used to propagate metadata mutations between the commit proxies (via the resolvers). I don’t think this project has implemented much of the metadata/shard map system yet but the empty batches are needed to prevent the shard map cache from becoming too stale.</p>
<p>The empty batches also propagate new commit <em>versions</em> to the tlogs, which propagate them to the storage servers. This information has to keep flowing or the cluster would essentially stall out. The 5s transaction duration limit uses the versions as a proxy for elapsed time, so if the read versions stopped advancing all commits would fail because they would be too old.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371782" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/16">Post #15</a>
	                </div>
	            </div>
              <div id="likers-container-371782" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371782"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #15"></div>
  </section>
</div>
    <div class="postbit" id="371794" data-post-id="371794">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jallum" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/120/38850_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jallum
                    <span class="op-star" title="Thread Starter">
                      <img alt="OP" class="op-star-icon" src="/assets/thread-icons/thread-icon-thread-starter-df91e872.png" />
                    </span>
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Thanks for taking an interest!</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol>
<li>It seems that you spawn a <code>TransactionBuilder</code> server on every transaction (and every operation has to be made in transaction explicitly) and it is passed like a pid. Wouldn’t it make more sense to just make it a structure instead of a gen_server? Every operation with transaction builder is a just a mutation of it’s internal state which is accumulated until commit happens, so it can be just a plain structure. I may think of an argument that having a server makes it possible for multiple processes to work inside one transaction, but why would anyone do this? And even if they do, they would still require to do the explicit synchronization on their side, since <code>put</code> operation is a cast to <code>TransactionBuilder</code>. You may say “batch insert operations processed in parallel”, but that’s also much easier to handover to users so they just perform batching themselves.</li>
</ol>
</blockquote>
</aside>
<p>It certainly could be done that way, but there are some drawbacks. The biggest is API ergonomics - Take an operation like <code>get(key)</code>. Unlike a <code>Map</code>, the get operations here have side-effects. (The fastest storage servers are located and cached, etc.) If done in a purely functional style, the return from get would need to look like {result, tx}. Pipelining would be extremely clunky for operations like <code>put</code>, breaking the idea that Bedrock is like some really big, durable, transactional <em>map</em>.</p>
<p>There’s an argument for using the process dictionary instead of a GenServer, for similar effect, but spinning up a new process is pretty cheap with the BEAM. I have some ideas that require managing timers and timeouts that pretty much <em>require</em> the use of another process… so, we’ll see what the future holds.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="3">
<li>It seems that every call to put or fetch would require to call the sequencer which is a repo-wide singleton. Wouldn’t it make more sense to utilize the ets table in this case? I mean, leave the singleton, but expose the <code>known_committed_version</code> in ets. This would remove the unnecessary global ordering of every read operation.</li>
</ol>
</blockquote>
</aside>
<p>Here’s how it works. The <em>first</em> read gets a read version from the sequencer. All of the subsequent reads for this transaction (and all nested transactions) will use this same version. This one of the ways that Bedrock can fulfill it’s guarantee of repeatable reads and snapshot isolation: All reads for a key at a version will return the same value.</p>
<p>When a transaction is committed, if any reads were performed, the read version is used along with the keys that were read to check to see if any transactions modified those keys in the intervening time – this is <em>part</em> of what the Resolver does. If the keys that were read <em>have</em> been modified, the transaction is aborted and restarted (and it will then pull a higher read_version and so will see the newly modified data).</p>
<p>The sequencer is a <em>cluster</em> wide singleton, but it’s job is <em>extremely</em> simple. FoundationDB, the pattern on which Bedrock is based, uses the notion of “GRV” proxies, read-version proxies that will spread the load. Bedrock may introduce a similar concept that could make use of :ets tables as you describe on each node. For now, calling the Sequencer directly is simple and effective.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="4">
<li>Possible optimization: you can keep your <code>Tx</code> mutations sorted so that range operations would become faster (it would require to just discover the upper bound in the list and then exit the recursive function, instead of traversing the whole list)</li>
</ol>
</blockquote>
</aside>
<p>There are so many opportunities for optimization in front of us. Currently, the focus is on getting all of the basic operations right, increasing test coverage, getting the documentation right, and working towards getting the system into a place where people might actually dare to use it for <em>real work.</em></p>
<p>If you see something you want to improve, though, we’re open to a PR! <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="5">
<li>It seems to me (but I am not quite sure) that it is possible to get into this situation: <code>read "x = 1" in transaction 1, put "x = 2" transction 2, commit transaction 2, range read "x = 2" in transaction 1</code> because it looks like range reads don’t account for <code>Tx</code>’s <code>reads</code> since <code>tx_visible</code> variable contains only <code>Tx.writes</code>. I see that <code>Tx.get_range</code> is not used anywhere, so I am sorry for the comment on WIP code <img src="https://forum.elixirforum.com/images/emoji/apple/sweat_smile.png?v=15" title=":sweat_smile:" class="emoji" alt=":sweat_smile:" loading="lazy" width="20" height="20"></li>
</ol>
</blockquote>
</aside>
<p>The way transactions were being assembled in 0.1 left holes in the “reads” and “writes” that could slip through the Resolver if we tried to add range-reads into the mix. This only worked because 0.1 just didn’t do range operations. Take a look at develop, where 0.2 is coming together, though! I’ve recently (yesterday) overhauled the machinery for building transactions (and the transaction format as well) with an eye toward making range operations like range-reads and clears easier to implement, along with other fun possibilities.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="6">
<li>It seems that I missed the functional tests like “read after write” in a single transaction from the database user perspective.</li>
</ol>
</blockquote>
</aside>
<p>Interestingly, in this system, the read-your-writes all happens <em>entirely</em> within the transaction builder. If in a transaction I read key “a”, and get “apple”… and within that transaction write “aardvark” to “a”, then any subsequent read (or range read!) should return the “aardvark” value for “a”. The rest of the system doesn’t see any of this. When a transaction is committed, passes resolution and is pushed to the logs… any subsequent read using that commit version would see the new value for “a”. If that transaction is rolled back, <em>nothing</em> needs to be done or communicated – we just throw the process away.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="7">
<li>I don’t understand the logic behind the finalization of empty batches in CommitProxy. If we don’t have any transactions, why do we need to perform this unnecessary operation of pushing no information to log servers, etc. ?</li>
</ol>
</blockquote>
</aside>
<p>This is kind of an interesting thing that FDB does, and I shamelessly copied it! <img src="https://forum.elixirforum.com/images/emoji/apple/slight_smile.png?v=15" title=":slight_smile:" class="emoji" alt=":slight_smile:" loading="lazy" width="20" height="20"></p>
<p>So they have their famous 5s window for transactions, right? This brings us to a question: How do all of the machines involved <em>know</em> when that window advances?</p>
<p>They all have different clocks, and there could be all kinds of skew… so you can’t use local wall-clock time – synchronizing time is a hassle or requires super-expensive equipment (like Spanner). So clock synchronization is out. How do <em>all</em> the servers in the cluster <em>know</em> when that 5s window has advanced?</p>
<p>Here’s what they (and we) do (and I think it is super cool):</p>
<ul>
<li>The sequencer issues versions in microsecond increments. This is monotonic time, and so will <em>always</em> be increasing. You can think of it as <em>uptime</em> for the cluster, because versions only advance when the cluster is in a running state. So, all of these servers don’t need to track their <em>own</em> time, they can just look at the most recent version to come along, and subtract ~5 million, and <em>there’s your window</em>. There’s a catch, though: This only works if transactions are <em>happening</em>. If the system is quiescent, nothing happens, nothing advances, the window doesn’t move.</li>
<li>Commit proxies will issue an empty transaction <em>if</em> no transactions have been processed within the last 1000ms. <em>This</em> advances the version, and slides the window ever-forward. As a bonus, it serves to “tire kick” the transaction system to ensure that the sequencer/resolvers/logs/ etc. are all reachable and functioning. Any error triggers a recovery.</li>
</ul>
<p>You get this lovely three-for-one out of this: ensuring the parts are working, advancing the window, synchronization.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="8">
<li>I can see that you use the <code>reply_fn = fn x -&gt; GenServer.reply(from, x) end</code> pattern, but I think that it would be better to just send the <code>from</code> and call the <code>GenServer.reply</code> where it’s needed. But that’s NIT.</li>
</ol>
</blockquote>
</aside>
<p>indeed. <img src="https://forum.elixirforum.com/images/emoji/apple/wink.png?v=15" title=":wink:" class="emoji" alt=":wink:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="9">
<li>It seems like links to docs in <code>@moduledoc</code>’s are incorrect.</li>
</ol>
</blockquote>
</aside>
<p>Yep. Broken all over the place. It’s a big project, and it’s just me, and it’s hard to keep all of the references up-to-date as things are in flux. As the design settles down, and the number of people contributing (hopefully) grows, I imagine that things like this will be sorted out.</p>
<aside class="quote no-group quote-modified" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="10">
<li>I can see that you use <code>:dets</code> as the key value storage, but <code>dets</code> comes with it’s internal limitations, like it can close without ability to recover on open (I’ve experienced it myself several times)
<blockquote>
<p><a href="https://www.erlang.org/doc/apps/stdlib/dets.html" class="inline-onebox" rel="nofollow">dets — OTP 29.0.2 (stdlib 8.0.1)</a></p>
</blockquote>
</li>
</ol>
<blockquote>
<p>Dets tables are not properly closed if the Erlang runtime system terminates abnormally</p>
</blockquote>
</blockquote>
</aside>
<p>Yep. The storage server that’s in there now, “Basalt,” is a place-holder, an example. I used :dets in it because it’s simple and it’s built into the BEAM. It could easily be replaced with some other k/v store (like rocksdb), but this works to let people try out the system and do so with minimal fuss, even in a livebook.</p>
<p>One thing interesting thing to point out about this model, though: There doesn’t have to be <em>just one</em> kind of storage server in operation at a time. I could easily see using a <em>mix</em> of them for different properties. Maybe a memory-only one that doesn’t guarantee durability, but offers extremely fast reads? Or another that’s good at absorbing writes quickly (built around an LSM, perhaps), or maybe another that packs transactions and sends them off to AWS/GCS for disaster recovery and can answer reads, but maybe slowly. There are a lot of interesting possibilities here!</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<ol start="11">
<li>I can see that you use a lot of  <code>do_something_fn = Keyword.get(opts, :do_something_fn, &amp;do_something/1)</code> in code and you change this option only in testing. I can suggest to use the library like <a href="https://hexdocs.pm/repatch" rel="noopener nofollow ugc">Repatch</a> to just patch these functions in tests, instead of slowing down your critical path code with unnecessary branching which will never be used in any production.</li>
</ol>
</blockquote>
</aside>
<p>I considered this, and might reconsider it again at some point. Ultimately, I found that these patching tools (of which there are a few) don’t seem to play well with asynchronous tests, as different tests might need to patch things in different ways, and yet others may want to use the actual implementation.</p>
<p>I haven’t settled a on a good answer for this… and as you note, there are some small drawbacks to this approach. At this time, though, I think the focus needs to be on <em>correctness</em>. There will come a time, though, that those extra few cycles matter and I’m sure the code will change.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>P.S. The code was not very easy to read <img src="https://forum.elixirforum.com/images/emoji/apple/sweat_smile.png?v=15" title=":sweat_smile:" class="emoji" alt=":sweat_smile:" loading="lazy" width="20" height="20">  Imports make it difficult to navigate to the function implementation (even with LSP) and single letter variables and abuse of <code>t</code> make it hard to understand what functions do when I get to them. I can see that you tend to split single gen server code into multiple files of <code>state</code>, <code>server</code>, etc., and that’s new to me, since I am more used to OTP and Elixir code style where everything related to some gen server resides in the single module and file (no matter how big it gets).</p>
</blockquote>
</aside>
<p>I’ll take that into consideration.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>And I also find it interesting that you defined helpers for <code>GenServer.cast</code>, <code>GenServer.call</code> and callback tuples like <code>def noreply(state), do: {:noreply, state}</code>. I am interested to hear what’s the story behind them <img src="https://forum.elixirforum.com/images/emoji/apple/smile.png?v=15" title=":smile:" class="emoji" alt=":smile:" loading="lazy" width="20" height="20"></p>
</blockquote>
</aside>
<p>I tend to use a lot of pipelining - that’s the whole story. The macros ensure that the functions are defp, and the compiler will inline them. In the compiled code, there’s no difference between this and hard-coding the tuples, but the functions play nicely with pipelining and i don’t need to resort to <code>then(&amp;{:noreply, &amp;1})</code>.</p>
<aside class="quote no-group" data-username="Asd" data-post="15" data-topic="72038" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>That’s all for now, I will read some more code tomorrow. And yeah, I am preparing a small PR with fixes and NITs I found while reading (like Enum |&gt; Enum optimizations and such).</p>
</blockquote>
</aside>
<p>Yay! I’m really quite happy that this is generating some interest. FDB is an awesome system and the patterns and ideas really should be more widely used. It’s just good tech. Keep the questions and feedback coming!</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371794" data-batch-url="/posts/batch_likers">
                        4
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/17">Post #16</a>
	                </div>
	            </div>
              <div id="likers-container-371794" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371794"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #16"></div>
  </section>
</div>
    <div class="postbit" id="371829" data-post-id="371829">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="jallum" data-post="17" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>FoundationDB, the pattern on which Bedrock is based, uses the notion of “GRV” proxies, read-version proxies that will spread the load.</p>
</blockquote>
</aside>
<p>Note that in addition to batching the Sequencer call the proxies also batch the generation liveness check to the tlogs which is needed to prevent stale reads after a recovery. Read version batching was previously done by the CommitProxy until it was taken over by the (newer) GRV proxies. The commit proxies also used to ask <em>each other</em> for the latest version rather than reporting it back to the Sequencer after commit (but before replying to clients), but they switched to the latter to reduce tail latency.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371829" data-batch-url="/posts/batch_likers">
                        1
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/18">Post #17</a>
	                </div>
	            </div>
              <div id="likers-container-371829" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371829"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #17"></div>
  </section>
</div>
    <div class="postbit" id="371896" data-post-id="371896">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Asd" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Asd
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>Hi again, here are some more questions</p>
<ol>
<li>It looks like you encode transactions into binary format, then you pass them to commit proxy, which batches them and then it decodes them back it in the Finalization and streams the mutations. Why? It just looks like unnecessary encoding/decoding step. There is also transaction encoding after sharding for logs and then transaction decoding in log.</li>
<li>I can see that you implemented custom encoding for keys and I thought that I will see the reasoning behind this somewhere in the code, but I didn’t and it looks like <code>term_to_binary</code> would do just fine. Am I missing something?</li>
<li>It looks like documentation in the code is inconsistent, because some very simple functions are extensively documented (for example, <code>mutation_to_key_or_range</code>) and others have no doc at all and use single letter variables (for example, <code>Tx</code> module and it’s children modules). What’s the reasoning behind it?</li>
</ol>
<hr>
<aside class="quote no-group" data-username="jallum" data-post="17" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>Ultimately, I found that these patching tools (of which there are a few) don’t seem to play well with asynchronous tests</p>
</blockquote>
</aside>
<p>Yeah, I get it, 99% of these tools suck at async tests. That’s why I wrote Repatch, which does not suck in async and is built with async first in mind. I think I will rewrite some tests in Bedrock to prove my point, haha <img src="https://forum.elixirforum.com/images/emoji/apple/wink.png?v=15" title=":wink:" class="emoji" alt=":wink:" loading="lazy" width="20" height="20"></p>
<aside class="quote no-group" data-username="jallum" data-post="17" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jallum/48/38850_2.png" class="avatar"> jallum:</div>
<blockquote>
<p>The macros ensure that the functions are defp, and the compiler will inline them. In the compiled code, there’s no difference between this and hard-coding the tuples, but the functions play nicely with pipelining and i don’t need to resort to <code>then(&amp;{:noreply, &amp;1})</code>.</p>
</blockquote>
</aside>
<p>But these are not macros which generate <code>defp</code> definitions, these are imports and imports are just a syntax sugar for <code>Module.function</code> and these are always compiled into so-called remote calls which do not inline and introduce overhead (though very very little overhead).</p>
<hr>
<p>I’ve read the Finalization and I will continue reading tomorrow or on Monday. Plus I made a short PR with simple changes and NITs</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371896" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/19">Post #18</a>
	                </div>
	            </div>
              <div id="likers-container-371896" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371896"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #18"></div>
  </section>
</div>
    <div class="postbit" id="371932" data-post-id="371932">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="garrison" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  garrison
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="Asd" data-post="19" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/a/c68b51/48.png" class="avatar"> Asd:</div>
<blockquote>
<p>I can see that you implemented custom encoding for keys and I thought that I will see the reasoning behind this somewhere in the code, but I didn’t and it looks like <code>term_to_binary</code> would do just fine. Am I missing something?</p>
</blockquote>
</aside>
<p>FDB is an <em>ordered</em> key/value store, so the key encodings are designed such that keys have useful ordering properties in their encoded form. See <a href="https://apple.github.io/foundationdb/data-modeling.html" rel="noopener nofollow ugc">data modeling</a> and the <a href="https://github.com/apple/foundationdb/blob/main/design/tuple.md" rel="noopener nofollow ugc">typecode reference</a> in their internals docs.</p>
<p>Erlang’s <code>term_to_binary</code> does not provide these properties. There is <a href="https://github.com/uwiger/sext" rel="noopener nofollow ugc">an amusingly-named library</a> to encode terms in term order. However, Erlang term order is also insufficient because tuple length is compared before contents, which is the wrong way around for prefix scanning of indexes. Though I think <em>maybe</em> you could use lists?</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371932" data-batch-url="/posts/batch_likers">
                        2
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/20">Post #19</a>
	                </div>
	            </div>
              <div id="likers-container-371932" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371932"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #19"></div>
  </section>
</div>
    <div class="postbit" id="371937" data-post-id="371937">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="Asd" src="/assets/icons/user-9f439610.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  Asd
                  </h3>
		          </div>
						
						</div>
					
					</div>

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<aside class="quote no-group" data-username="garrison" data-post="20" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>FDB is an <em>ordered</em> key/value store, so the key encodings are designed such that keys have useful ordering properties in their encoded form. See <a href="https://apple.github.io/foundationdb/data-modeling.html" rel="noopener nofollow ugc">data modeling</a> and the <a href="https://github.com/apple/foundationdb/blob/main/design/tuple.md" rel="noopener nofollow ugc">typecode reference</a> in their internals docs.</p>
</blockquote>
</aside>
<p>Well, yeah, I understand that, but you use <code>dets</code> as a storage which is unordered. And <code>dets</code> can store data as erlang terms. But you still encode keys into binary. You could use <code>ets</code> with <code>tab2file</code>, for example, and it would have the ordering and you wouldn’t even have to encode keys in the first place.</p>
<p>I understand that <code>dets</code> is a temporary solution, but even if you choose any other storage, ordering of terms is a problem on the storage level. And when you allow only some keys to be encoded, it means that your abstraction is leaking and the limitation of storage level is getting exposed to the end user.</p>
<p>The better approach would be to</p>
<ol>
<li>Don’t encode keys and values during the whole pipeline</li>
<li>Introduce ordering with comparators instead of binary comparison.</li>
<li>Encode keys only when you have indexed them and you want to write them to the persistent storage</li>
</ol>
<p>One possible algorithm I can think of is using B+ tree index with append-only file structure similar to what CubDB does. It’s not the best possible algorithm, but it just shows that sortability of keys-as-binaries is not a hard requirement and is rather a limitation of the approach you are planning to take in the future</p>
<aside class="quote no-group" data-username="garrison" data-post="20" data-topic="72038">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/letter_avatar_proxy/v4/letter/g/3bc359/48.png" class="avatar"> garrison:</div>
<blockquote>
<p>However, Erlang term order is also insufficient because tuple length is compared before contents, which is the wrong way around for prefix scanning of indexes</p>
</blockquote>
</aside>
<p>I don’t quite get what you’re saying here. My best guess is that you’re talking about some algorithm which is not yet implemented and it allows to query data in the DB like by matching on the left part of tuple (and this tuple pattern has no size like <code>{^first, ^second | _}</code>). Well yeah, if someone wanted to do this, they would better have used the list as a key. Because this problem is not new, and <code>ets</code> has the same problem and the solution is also to just use lists</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="371937" data-batch-url="/posts/batch_likers">
                        0
                      </span>
                      <!-- <span class="thread-count js-solved-indicator" title="Marked as solution"></span> -->
	                </div>
	                <div class="go-to-post">
	                  <a title="Go to post" alt="Go to post" href="https://forum.elixirforum.com/t/bedrock-a-scaleable-distributed-key-value-database-with-better-than-acid-guarantees/72038/21">Post #20</a>
	                </div>
	            </div>
              <div id="likers-container-371937" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="371937"
                     data-batch-url="/posts/batch_likers">
                  <div class="post-likers"></div>
                </div>
              </div>
	        </div>
			

    </div>

    <div class="triangle-top-right type-standard-post cat-standard-post" title="Post #20"></div>
  </section>
</div>
</template></turbo-stream><turbo-stream action="replace" target="load-more-container"><template><div id="load-more-container" class="load-more-container">
    <a class="load-more-button" data-turbo-stream="true" href="/topics/72038/load_more?page=3">Load more posts (47 remaining)</a>
</div></template></turbo-stream>