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


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jstimps" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jstimps/120/41062_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jstimps
                    <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="61" data-topic="61642">
<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>“Schemaless record store?” “Semi-structured”? Idk.</p>
</blockquote>
</aside>
<p>I’ve been idly thinking of a name for these types of systems since database feels the too much and storage engine feels like too little. Lately I’ve been fond of “State Engine”. The combination of MVCC storage server and serializable client-side-compute behaves conceptually as one giant GenServer and many small GenServers at the same time.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="372132" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/62">Post #61</a>
	                </div>
	            </div>
              <div id="likers-container-372132" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="372132"
                     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 #61"></div>
  </section>
</div>
    <div class="postbit" id="372134" data-post-id="372134">
  <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="onebox allowlistedgeneric" data-onebox-src="https://buttondown.com/jaffray/archive/the-two-machines/">
  <header class="source">
      <img src="https://assets.buttondown.email/images/53454160-8ef1-414e-a448-5a8762a99319.png" class="site-icon" alt="" width="690" height="525">

      <a href="https://buttondown.com/jaffray/archive/the-two-machines/" target="_blank" rel="noopener nofollow ugc">buttondown.com</a>
  </header>

  <article class="onebox-body">
    <div class="aspect-image" style="--aspect-ratio:460/609;"><img src="https://assets.buttondown.email/images/75912a43-e436-4bdd-a8db-baa577450ce2.png?w=960&amp;fit=max" class="thumbnail" alt="" width="460" height="609"></div>

<h3><a href="https://buttondown.com/jaffray/archive/the-two-machines/" target="_blank" rel="noopener nofollow ugc">The Two Machines</a></h3>

  <p>There's a joke in my friend circle that asks "is it a database?" A startup, a program, a syscall, a person good with numbers, a person with a good memory....</p>


  </article>

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

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

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="372134" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/63">Post #62</a>
	                </div>
	            </div>
              <div id="likers-container-372134" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="372134"
                     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 #62"></div>
  </section>
</div>
    <div class="postbit" id="379903" data-post-id="379903">
  <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>The native tenancy feature has met its tragic end in an <a href="https://github.com/apple/foundationdb/pull/12583" rel="noopener nofollow ugc">excruciating 100-commit PR</a>. Shoutout to the one guy at Apple who seems to be single-handedly cleaning up the FDB codebase. Honestly, I think it would be easier to rewrite the thing in a real programming language, but realistically what do I know.</p>
<p>I had long thought Apple actually <em>used</em> the tenant features, but I guess it was actually a Snowflake thing. Presumably they prefer to manage tenancy in the record layer. I do think the idea of having a sanity check for commits that cross tenant boundaries has merit, though the FDB implementation seemed excessive. They kept a tenant <code>id =&gt; keyspace</code> map on the CommitProxies <em>and</em> Storage servers and used it to validate the id directly. Perhaps just validating a shared subspace prefix would be enough.</p>
<p>The native tenancy was the closest thing FDB has ever had to actual access control, though, which is an interesting premise. RIP.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="379903" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/64">Post #63</a>
	                </div>
	            </div>
              <div id="likers-container-379903" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="379903"
                     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 #63"></div>
  </section>
</div>
    <div class="postbit" id="379904" data-post-id="379904">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jstimps" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jstimps/120/41062_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jstimps
                    <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>Man I knew it was on the chopping block but I’m sad to see it go. I suppose I have some code of my own to delete too.</p>
<p>Luckily a while ago I switched the ecto_foundationdb tenants to simply be implemented by the Directory Layer instead of the doomed managed tenants. So any ecto_fdb databases should be future proof unless they explicitly chose the experimental ManagedTenant.</p>
<p>I would have loved to see Tenant-aware sharding and encryption. It would make for a powerful security/compliance story.</p>
<p>OTOH, I share your appreciation for the project continuing to move forward in the open and clean out some of the cobwebs.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="379904" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/65">Post #64</a>
	                </div>
	            </div>
              <div id="likers-container-379904" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="379904"
                     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 #64"></div>
  </section>
</div>
    <div class="postbit" id="379920" data-post-id="379920">
  <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="jstimps" data-post="65" data-topic="61642">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jstimps/48/41062_2.png" class="avatar"> jstimps:</div>
<blockquote>
<p>So any ecto_fdb databases should be future proof unless they explicitly chose the experimental ManagedTenant.</p>
</blockquote>
</aside>
<p>Good call! I would probably have done the wrong thing here as I really did figure it was an Apple feature. In hindsight I’m sure if you tracked down the commits it was all Snowflake engineers (who are now long gone). Prime example of FDB doing exactly zero communication with their community lol. (Obvi I am not blaming the individuals; they are quite literally just doing their jobs.)</p>
<aside class="quote no-group" data-username="jstimps" data-post="65" data-topic="61642">
<div class="title">
<div class="quote-controls"></div>
<img alt="" width="24" height="24" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jstimps/48/41062_2.png" class="avatar"> jstimps:</div>
<blockquote>
<p>I would have loved to see Tenant-aware sharding and encryption.</p>
</blockquote>
</aside>
<p>It’s not clear to me from your phrasing if you’re aware of this or not, but both of these things actually did exist half-baked in the implementation.</p>
<p>Per-tenant encryption is kinda weird. For one it was at the page level and of course the database is multitenant so there’s no guarantee that pages are cleaved on tenant lines. I got the impression that pages including multiple tenants just… weren’t encrypted that way? It was weird.</p>
<p>Cleaving <em>shards</em> on tenant lines is more interesting because if you have a lot of tenants that are small (like Apple does) then it can probably be done without screwing up the shard size variance too much. The advantage would be to avoid “unlucky” tenants that get split across multiple shards, which I would imagine in practice would increase p99 for random users (persistently, until they get re-sharded) which is probably not great.</p>
<p>Overall the whole access control thing is an interesting angle. FDB’s security model is just like Erlang’s (that is: there isn’t one) and for essentially the same reasons. I have gone back and forth on whether this is a good idea or not in both cases, and honestly I’m not really sure. I’d be curious to hear what Erlang experts think as they’ve probably been debating this for 40 years lol.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="379920" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/66">Post #65</a>
	                </div>
	            </div>
              <div id="likers-container-379920" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="379920"
                     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 #65"></div>
  </section>
</div>
    <div class="postbit" id="379931" data-post-id="379931">
  <section>
    <div class="post-wrap">


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

	        <div class="thread-main">
	            <div class="post-body" data-turbo="false">
								<p>If people have deep enough access to your server then you’re f. anyway, also security is about minimizing risk, not completely avoid it, which is impossible. How are you going to stop the NSA from tapping cables.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="379931" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/67">Post #66</a>
	                </div>
	            </div>
              <div id="likers-container-379931" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="379931"
                     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 #66"></div>
  </section>
</div>
    <div class="postbit" id="380763" data-post-id="380763">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jstimps" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jstimps/120/41062_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jstimps
                    <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>Hi everyone,</p>
<p>EctoFoundationDB <a href="https://hex.pm/packages/ecto_foundationdb" rel="nofollow">v0.6.0</a> is published. The focus of this release is a new module called <a href="https://hexdocs.pm/ecto_foundationdb/EctoFoundationDB.Sync.html" rel="noopener nofollow ugc"><code>EctoFoundationDB.Sync</code></a> that represents a milestone in the read-path Sync Engine that I’ve been documenting in Livebooks over the past 9 months or so.</p>
<p>Inspired by what Phoenix.Sync has done for Postgres users, EctoFoundationDB.Sync offers a similar batteries-included syncing experience for those of us that wish to use FoundationDB for their apps ( =&gt; me!). The guiding principle is to declare the queries upfront and have the assigns automatically update, without PubSub.</p>
<p>Here’s an example LiveView showing how we can manage various syncing operations on the database as the user navigates on the page (handle_params). The “magic” auto-updating is done via careful FDB watches and LiveView’s attach_hook.</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">defmodule DemoLive do
  use Phoenix.LiveView

  alias EctoFoundationDB.Sync
  import Ecto.Query

  @query_catalog from(p in Product, order_by: p.name)
  @query_reviews from(r in Review, order_by: {:desc, r.inserted_at})

  def mount(_params, _session, socket) do
    tenant = Tenant.open!(Repo, "sync-sample")

    # :catalog drives our navigation bar, allowing the user to select a Product
    {:ok, socket
      |&gt; put_private(:tenant, tenant)
      |&gt; Sync.sync_all(Repo, :catalog, @query_catalog)}
  end

  def handle_params(%{"id" =&gt; id}, _uri, socket) do
    # When the user selects a Product, it's loaded in :product and its Reviews in :reviews
    {:noreply,
      socket
      |&gt; Sync.sync_one(Repo, :product, Product, id)
      |&gt; Sync.sync_all_by(Repo, :reviews, @query_reviews, product_id: id)}
  end

  def render(assigns) do
    # ...
  end

  # That's it! Really -- nothing more
end
</code></pre>
<p>There is yet another Livebook that demonstrates the capabilities end-to-end: <a href="https://hexdocs.pm/ecto_foundationdb/sync_module.html" rel="noopener nofollow ugc">Sync Engine III - Batteries Included</a>. (Livebook is awesome btw!)</p>
<p>The Livebook includes the LiveView shown above as well as one that is more sophisticated <a href="https://hexdocs.pm/ecto_foundationdb/sync_module.html#an-admin-page-with-dependent-syncing" rel="noopener nofollow ugc">using LiveComponents</a>.</p>
<p>I’ve really enjoyed the database / data management discussion on the forum lately. And especially the new projects that you all continue to contribute to the community. I do a lot of lurking, and not a lot of responding, so I wanted to thank all of you for sharing your thoughts and your code.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="380763" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/68">Post #67</a>
	                </div>
	            </div>
              <div id="likers-container-380763" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="380763"
                     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 #67"></div>
  </section>
</div>
    <div class="postbit" id="380810" data-post-id="380810">
  <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>Great work as always (and great docs as always)!</p>
<p>Help me understand the guarantees here. It seems like to sync a collection you add a watch on a special (per-tenant) metadata key that is updated along with any row in the “table” (using atomics presumably). So you are guaranteed to observe that an update to a collection has occurred.</p>
<p>FDB does not pass along any information about what has been updated, though, so you cannot incrementalize this. Therefore you must re-run the query as a whole? When that happens you get strict serializability guarantees so the collection will reflect the update and will be a consistent snapshot.</p>
<p>If you have multiple syncs, you have multiple watches which fire independently. However, if you re-run the query for each particular watch you will observe changes out of order (tearing). If you re-run <em>all</em> queries when any watch is triggered you can maintain a consistent snapshot, but this comes at a further performance cost because there is no incrementalization. But I <em>think</em> you’re taking the former approach based on the docs?</p>
<p>I will warn you that dealing with this tearing behavior from the application side quickly becomes maddening. I did this for a while with LiveView and PubSub and that’s how I ended up with such unreasonable opinions about consistency. It’s actually <a href="https://www.scattered-thoughts.net/writing/internal-consistency-in-streaming-systems/" rel="noopener nofollow ugc">much worse than I had thought at the time</a>, though.</p>
<p>I don’t think FDB can do better than re-running all queries every time anything changes, but that’s fine for many applications (especially since everything is tenant-scoped). I guess you could write all changes in duplicate to a versionstamped log (event sourcing!), but then you have to clean it up and that’s a whole thing.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="380810" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/69">Post #68</a>
	                </div>
	            </div>
              <div id="likers-container-380810" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="380810"
                     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 #68"></div>
  </section>
</div>
    <div class="postbit" id="380813" data-post-id="380813">
  <section>
    <div class="post-wrap">


					<div class="post-header">
		        <div class="user-avatar">
		          <img alt="jstimps" src="https://forum.elixirforum.com/user_avatar/forum.elixirforum.com/jstimps/120/41062_2.png" width="120" height="120" />
		        </div>
					
						<div class="user-details">
		          <div class="user-name">
		            <h3>
                  jstimps
                    <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 a look, and the kind words!</p>
<p>You nailed it exactly right, but I will add one point about the new watch that is created after the read.</p>
<p>When a key changes (e.g. one of the metadata keys as you mentioned for a collection), all futures corresponding to the watches on that key are resolved. So if we have 5 LiveViews active on a particular collection, we have 5 watches on a single key, and each future is resolved independently. Then it’s up to the client to decide how to retrieve the new data. In the case of EctoFDB.Sync, I chose to have each listening entity do their own new read of the data. There are no guarantees on the read version for this specific transaction.</p>
<p><strong>However, a new watch is created in the same transaction as the read</strong>. So that if the key were to change again after that read, the same entity would be notified again to perform yet another read, and yet another watch in the same transaction. Presumably, the key will eventually stop changing and each entity can cease querying the database. I believe this approach to creating a watch in the transaction does provide a guarantee[^1] that each listener will eventually (scary word!) be consistent. Each page will not get the same GRV, but they should have the same bytes from the key-values once it settles, assuming I implemented this all correctly.</p>
<p>I’m evoking eventual consistency here because in this discussion we’re including the LiveView and the browser itself as a participant in the database. I’m not sure what it would look like to have multiple pages having a truly consistent view of the data and with point-in-time guarantees on their agreement. Perhaps some coordination of a GRV across pages. Have you come up with some other approach here?</p>
<p>On the question of performance, EctoFDB.Sync is indeed relying on the database to be able to perform well having many concurrent transactions performing identical key reads. So far I’ve seen acceptable performance from FDB without having to do some sort of caching layer. FDB is supposed to live very near your application anyway, so caching would be a design mistake, IMO. But my EctoFDB use cases are very light workloads so far. Will this approach scale? Time will tell.</p>
<p>[^1] Note that if a key changes from A → B → A quickly enough, then the watch will not fire, and stays unresolved. In such a case, any listener still has the correct data anyway.</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="380813" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/70">Post #69</a>
	                </div>
	            </div>
              <div id="likers-container-380813" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="380813"
                     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 #69"></div>
  </section>
</div>
    <div class="postbit" id="380814" data-post-id="380814">
  <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>Oh I think I left some ambiguity on the table by mistake. I don’t mean multiple pages, I mean multiple queries on the same page. An example would be better.</p>
<p>Say we have a page with <code>authors</code> and <code>books</code>. We sync <em>all</em> authors and books to the client, with two <code>sync_all()</code> calls, and render them like this:</p>
<pre data-code-wrap="elixir"><code class="lang-elixir">&lt;div :for={{_id, book} &lt;- @books}&gt;
  &lt;div&gt;Title: {book.title}&lt;/div&gt;
  &lt;div&gt;Author: {List.keyfind!(@authors, book.author_id, 0).name}&lt;/div&gt;
&lt;/div&gt;
</code></pre>
<p>(There are some who will suggest that you join in the database here. Ignore them, ngmi)</p>
<p>You have probably already spotted the problem: we have no ordering/frontier guarantees here, so <code>@books</code> can update before <code>@authors</code> and tear. If a new book is added with a new author, we will crash because the author is not known when the book appears.</p>
<p>If you are smart you can imagine some simple hacks to get around this, but if you are wise then you will recognize that this is the road to hell. And I have been there.</p>
<p>What you want is for the changes to arrive in versioned batches so that you can always stitch together a consistent snapshot of a page. This property is not called eventual consistency but <em>internal consistency</em>, and I first learned its name from that article I linked. (Before I knew what it was called I had also made up a name, “externally-consistent snapshot”, which is amusingly an oxymoron.)</p>
<p>However you have neither changes nor batches and you are not incrementalizing anything because FDB won’t let you, so all you have to do is re-run the queries when something changes. But, and this is what I was getting at, you need to re-run <em>all</em> queries on the page within a transaction to get a consistent snapshot.</p>
<p>This has performance implications but it’s not the end of the world, particularly if your app naturally shards into tenants (which are conveniently disjoint in their queries).</p> 
	            </div>

	            <div class="base-line">
	                <div class="thread-counters">
	                    <span class="thread-count count-likes js-likers-trigger" title="Likes" data-post-id="380814" 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/ecto-foundationdb-an-ecto-adapter-for-foundationdb/61642/71">Post #70</a>
	                </div>
	            </div>
              <div id="likers-container-380814" 
                   class="likers-container"
                   data-first-post="false"
                   data-batch-url="/posts/batch_likers">
                   <div class="likers-placeholder" 
                     data-likers-post-id="380814"
                     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 #70"></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/61642/load_more?page=8">Load more posts (6 remaining)</a>
</div></template></turbo-stream>