Skip to content

Commit 418d48f

Browse files
committed
update store.wit to discuss eventual consistency
My mental model of what eventual consistency does and does not guarantee was inaccurate, meaning much of what I wrote earlier was incorrect. I _believe_ this new version gets it right; we'll see. Yay for PR review! Signed-off-by: Joel Dice <[email protected]>
1 parent cf03f8a commit 418d48f

File tree

3 files changed

+103
-153
lines changed

3 files changed

+103
-153
lines changed

imports.md

Lines changed: 37 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,53 +24,39 @@ ensuring compatibility between different key-value stores. Note: the clients wil
2424
serialization/deserialization overhead to be handled by the key-value store. The value could be
2525
a serialized object from JSON, HTML or vendor-specific data types like AWS S3 objects.</p>
2626
<h2>Consistency</h2>
27-
<p>An implementation of this interface MUST be eventually consistent, meaning that, after some time
28-
with no further updates, all replicas in the (potentially distributed) system will eventually
29-
converge on a consistent state for all values. This allows replicas to temporarily diverge to
30-
ensure low latency and high availability. Implementations based on a centralized or local
31-
backing store may provide a stronger consistency model, but guest components which are intended
32-
to be portable to any <code>wasi-keyvalue</code> implementation should not rely on anything stronger than
33-
eventual consistency.</p>
34-
<p>Given that each <a href="#bucket"><code>bucket</code></a> resource may represent a connection to a different replica in a
35-
distributed system, values read for a given key from two different <a href="#bucket"><code>bucket</code></a>s may differ, even if
36-
those <a href="#bucket"><code>bucket</code></a> resources were opened using the same string identifier. In addition, consecutive
37-
operations on a single <a href="#bucket"><code>bucket</code></a> resource may produce temporarily inconsistent results if
38-
e.g. the implementation is forced to reconnect to a different replica due to a connection
39-
failure. For example, a write followed by a read may not return the value just written, even if
40-
no other recent or subsequent writes have occurred.</p>
41-
<p>Consider the following pseudocode example (and assume we start with an empty store and no other
42-
concurrent activity):</p>
43-
<pre><code>bucketA = open(&quot;foo&quot;)
44-
bucketB = open(&quot;foo&quot;)
45-
bucketA.set(&quot;bar&quot;, &quot;a&quot;)
46-
47-
// These are guaranteed to succeed:
48-
assert bucketA.get(&quot;bar&quot;).equals(&quot;a&quot;) or bucketA.get(&quot;bar&quot;) is None
49-
assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;) or bucketB.get(&quot;bar&quot;) is None
50-
51-
// This is likely to succeed, but not guaranteed; e.g. `bucketA` might need to reconnect to a
52-
// different replica which hasn't received the above write yet. It will _eventually_
53-
// succeed, provided there are no irrecoverable errors which prevent the propagation of the
54-
// write.
55-
assert bucketA.get(&quot;bar&quot;).equals(&quot;a&quot;)
56-
57-
// Likewise, this will _eventually_ succeed in the absence of irrecoverable errors:
58-
assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;)
59-
60-
bucketB.set(&quot;bar&quot;, &quot;b&quot;)
61-
bucketC = open(&quot;foo&quot;)
62-
value = bucketC.get(&quot;bar&quot;)
63-
64-
// This is guaranteed to succeed:
65-
assert value.equals(&quot;a&quot;) or value.equals(&quot;b&quot;) or value is None
66-
</code></pre>
67-
<p>In other words, the <code>bucketC</code> resource MAY reflect either the most recent write to the <code>bucketA</code>
68-
resource, or the one to the <code>bucketB</code> resource, or neither, depending on how quickly either of
69-
those writes reached the replica from which the <code>bucketC</code> resource is reading. However,
70-
assuming there are no irrecoverable errors -- such that the state of a replica is irretrievably
71-
lost before it can be propagated -- one of the values (&quot;a&quot; or &quot;b&quot;) MUST eventually be considered
72-
the &quot;latest&quot; and replicated across the system, at which point all three resources will return
73-
that same value.</p>
27+
<p>An implementation of this interface MUST be eventually consistent, but is not required to
28+
provide any consistency guaranteeds beyond that. Practically speaking, eventual consistency is
29+
among the weakest of consistency models, guaranteeing only that values will not be produced
30+
&quot;from nowhere&quot;, i.e. any value read is guaranteed to have been written to that key at some
31+
earlier time. Beyond that, there are no guarantees, and thus a portable component must neither
32+
expect nor rely on anything else.</p>
33+
<p>In the future, additional interfaces may be added to <code>wasi:key-value</code> with stronger guarantees,
34+
which will allow components to express their requirements by importing whichever interface(s)
35+
provides matching (or stronger) guarantees. For example, a component requiring strict
36+
serializability might import a (currently hypothetical) <code>strict-serializable-store</code> interface
37+
with a similar signature to <code>store</code> but with much stronger semantic guarantees. On the other
38+
end, a host might either support implementations of both the <code>store</code> and
39+
<code>strict-serializable-store</code> or just the former, in which case the host would immediately reject
40+
a component which imports the unsupported interface.</p>
41+
<p>Here are a few examples of behavior which an component developer might wish to rely on but which
42+
are <em>NOT</em> guaranteed by an eventually consistent system (e.g. a distributed system composed of
43+
multiple replicas, each of which may receive writes in a different order, making no attempt to
44+
converge on a global consensus):</p>
45+
<ul>
46+
<li>
47+
<p>Read-your-own-writes: eventual consistency does <em>NOT</em> guarantee that a write to a given key
48+
followed by a read from the same key will retrieve the same or newer value.</p>
49+
</li>
50+
<li>
51+
<p>Convergence: eventual consistency does <em>NOT</em> guarantee that any two replicas will agree on the
52+
value for a given key -- even after all writes have had time to propagate to all replicas.</p>
53+
</li>
54+
<li>
55+
<p>Last-write-wins: eventual consistency does <em>NOT</em> guarantee that the most recent write will
56+
take precendence over an earlier one; old writes may overwrite newer ones temporarily or
57+
permanently.</p>
58+
</li>
59+
</ul>
7460
<h2>Durability</h2>
7561
<p>This interface does not currently make any hard guarantees about the durability of values
7662
stored. A valid implementation might rely on an in-memory hash table, the contents of which are
@@ -79,7 +65,7 @@ all writes to disk -- or even to a quorum of disk-backed nodes at multiple locat
7965
returning a result for a <code>set</code> call. Finally, a third implementation might persist values
8066
asynchronously on a best-effort basis without blocking <code>set</code> calls, in which case an I/O error
8167
could occur after the component instance which originally made the call has exited.</p>
82-
<p>Future versions of the <code>wasi-keyvalue</code> package may provide ways to query and control the
68+
<p>Future versions of the <code>wasi:keyvalue</code> package may provide ways to query and control the
8369
durability and consistency provided by the backing implementation.</p>
8470
<hr />
8571
<h3>Types</h3>
@@ -118,7 +104,7 @@ there are no more keys to fetch.
118104
<h4><a id="bucket"></a><code>resource bucket</code></h4>
119105
<p>A bucket is a collection of key-value pairs. Each key-value pair is stored as a entry in the
120106
bucket, and the bucket itself acts as a collection of all these entries.</p>
121-
<p>It is worth noting that the exact terminology for bucket in key-value stores can very
107+
<p>It is worth noting that the exact terminology for bucket in key-value stores can vary
122108
depending on the specific implementation. For example:</p>
123109
<ol>
124110
<li>Amazon DynamoDB calls a collection of key-value pairs a table</li>
@@ -134,8 +120,8 @@ key-value pairs.</p>
134120
<h2>Note that opening two <a href="#bucket"><code>bucket</code></a> resources using the same identifier MAY result in connections
135121
to two separate replicas in a distributed database, and that writes to one of those
136122
resources are not guaranteed to be readable from the other resource promptly (or ever, in
137-
the case of a replica failure). See the <code>Consistency</code> section of the <code>store</code> interface
138-
documentation for details.</h2>
123+
the case of a replica failure or message reordering). See the <code>Consistency</code> section of the
124+
<code>store</code> interface documentation for details.</h2>
139125
<h3>Functions</h3>
140126
<h4><a id="open"></a><code>open: func</code></h4>
141127
<p>Get the bucket with the specified identifier.</p>

watch-service.md

Lines changed: 37 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -22,53 +22,39 @@ ensuring compatibility between different key-value stores. Note: the clients wil
2222
serialization/deserialization overhead to be handled by the key-value store. The value could be
2323
a serialized object from JSON, HTML or vendor-specific data types like AWS S3 objects.</p>
2424
<h2>Consistency</h2>
25-
<p>An implementation of this interface MUST be eventually consistent, meaning that, after some time
26-
with no further updates, all replicas in the (potentially distributed) system will eventually
27-
converge on a consistent state for all values. This allows replicas to temporarily diverge to
28-
ensure low latency and high availability. Implementations based on a centralized or local
29-
backing store may provide a stronger consistency model, but guest components which are intended
30-
to be portable to any <code>wasi-keyvalue</code> implementation should not rely on anything stronger than
31-
eventual consistency.</p>
32-
<p>Given that each <a href="#bucket"><code>bucket</code></a> resource may represent a connection to a different replica in a
33-
distributed system, values read for a given key from two different <a href="#bucket"><code>bucket</code></a>s may differ, even if
34-
those <a href="#bucket"><code>bucket</code></a> resources were opened using the same string identifier. In addition, consecutive
35-
operations on a single <a href="#bucket"><code>bucket</code></a> resource may produce temporarily inconsistent results if
36-
e.g. the implementation is forced to reconnect to a different replica due to a connection
37-
failure. For example, a write followed by a read may not return the value just written, even if
38-
no other recent or subsequent writes have occurred.</p>
39-
<p>Consider the following pseudocode example (and assume we start with an empty store and no other
40-
concurrent activity):</p>
41-
<pre><code>bucketA = open(&quot;foo&quot;)
42-
bucketB = open(&quot;foo&quot;)
43-
bucketA.set(&quot;bar&quot;, &quot;a&quot;)
44-
45-
// These are guaranteed to succeed:
46-
assert bucketA.get(&quot;bar&quot;).equals(&quot;a&quot;) or bucketA.get(&quot;bar&quot;) is None
47-
assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;) or bucketB.get(&quot;bar&quot;) is None
48-
49-
// This is likely to succeed, but not guaranteed; e.g. `bucketA` might need to reconnect to a
50-
// different replica which hasn't received the above write yet. It will _eventually_
51-
// succeed, provided there are no irrecoverable errors which prevent the propagation of the
52-
// write.
53-
assert bucketA.get(&quot;bar&quot;).equals(&quot;a&quot;)
54-
55-
// Likewise, this will _eventually_ succeed in the absence of irrecoverable errors:
56-
assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;)
57-
58-
bucketB.set(&quot;bar&quot;, &quot;b&quot;)
59-
bucketC = open(&quot;foo&quot;)
60-
value = bucketC.get(&quot;bar&quot;)
61-
62-
// This is guaranteed to succeed:
63-
assert value.equals(&quot;a&quot;) or value.equals(&quot;b&quot;) or value is None
64-
</code></pre>
65-
<p>In other words, the <code>bucketC</code> resource MAY reflect either the most recent write to the <code>bucketA</code>
66-
resource, or the one to the <code>bucketB</code> resource, or neither, depending on how quickly either of
67-
those writes reached the replica from which the <code>bucketC</code> resource is reading. However,
68-
assuming there are no irrecoverable errors -- such that the state of a replica is irretrievably
69-
lost before it can be propagated -- one of the values (&quot;a&quot; or &quot;b&quot;) MUST eventually be considered
70-
the &quot;latest&quot; and replicated across the system, at which point all three resources will return
71-
that same value.</p>
25+
<p>An implementation of this interface MUST be eventually consistent, but is not required to
26+
provide any consistency guaranteeds beyond that. Practically speaking, eventual consistency is
27+
among the weakest of consistency models, guaranteeing only that values will not be produced
28+
&quot;from nowhere&quot;, i.e. any value read is guaranteed to have been written to that key at some
29+
earlier time. Beyond that, there are no guarantees, and thus a portable component must neither
30+
expect nor rely on anything else.</p>
31+
<p>In the future, additional interfaces may be added to <code>wasi:key-value</code> with stronger guarantees,
32+
which will allow components to express their requirements by importing whichever interface(s)
33+
provides matching (or stronger) guarantees. For example, a component requiring strict
34+
serializability might import a (currently hypothetical) <code>strict-serializable-store</code> interface
35+
with a similar signature to <code>store</code> but with much stronger semantic guarantees. On the other
36+
end, a host might either support implementations of both the <code>store</code> and
37+
<code>strict-serializable-store</code> or just the former, in which case the host would immediately reject
38+
a component which imports the unsupported interface.</p>
39+
<p>Here are a few examples of behavior which an component developer might wish to rely on but which
40+
are <em>NOT</em> guaranteed by an eventually consistent system (e.g. a distributed system composed of
41+
multiple replicas, each of which may receive writes in a different order, making no attempt to
42+
converge on a global consensus):</p>
43+
<ul>
44+
<li>
45+
<p>Read-your-own-writes: eventual consistency does <em>NOT</em> guarantee that a write to a given key
46+
followed by a read from the same key will retrieve the same or newer value.</p>
47+
</li>
48+
<li>
49+
<p>Convergence: eventual consistency does <em>NOT</em> guarantee that any two replicas will agree on the
50+
value for a given key -- even after all writes have had time to propagate to all replicas.</p>
51+
</li>
52+
<li>
53+
<p>Last-write-wins: eventual consistency does <em>NOT</em> guarantee that the most recent write will
54+
take precendence over an earlier one; old writes may overwrite newer ones temporarily or
55+
permanently.</p>
56+
</li>
57+
</ul>
7258
<h2>Durability</h2>
7359
<p>This interface does not currently make any hard guarantees about the durability of values
7460
stored. A valid implementation might rely on an in-memory hash table, the contents of which are
@@ -77,7 +63,7 @@ all writes to disk -- or even to a quorum of disk-backed nodes at multiple locat
7763
returning a result for a <code>set</code> call. Finally, a third implementation might persist values
7864
asynchronously on a best-effort basis without blocking <code>set</code> calls, in which case an I/O error
7965
could occur after the component instance which originally made the call has exited.</p>
80-
<p>Future versions of the <code>wasi-keyvalue</code> package may provide ways to query and control the
66+
<p>Future versions of the <code>wasi:keyvalue</code> package may provide ways to query and control the
8167
durability and consistency provided by the backing implementation.</p>
8268
<hr />
8369
<h3>Types</h3>
@@ -116,7 +102,7 @@ there are no more keys to fetch.
116102
<h4><a id="bucket"></a><code>resource bucket</code></h4>
117103
<p>A bucket is a collection of key-value pairs. Each key-value pair is stored as a entry in the
118104
bucket, and the bucket itself acts as a collection of all these entries.</p>
119-
<p>It is worth noting that the exact terminology for bucket in key-value stores can very
105+
<p>It is worth noting that the exact terminology for bucket in key-value stores can vary
120106
depending on the specific implementation. For example:</p>
121107
<ol>
122108
<li>Amazon DynamoDB calls a collection of key-value pairs a table</li>
@@ -132,8 +118,8 @@ key-value pairs.</p>
132118
<h2>Note that opening two <a href="#bucket"><code>bucket</code></a> resources using the same identifier MAY result in connections
133119
to two separate replicas in a distributed database, and that writes to one of those
134120
resources are not guaranteed to be readable from the other resource promptly (or ever, in
135-
the case of a replica failure). See the <code>Consistency</code> section of the <code>store</code> interface
136-
documentation for details.</h2>
121+
the case of a replica failure or message reordering). See the <code>Consistency</code> section of the
122+
<code>store</code> interface documentation for details.</h2>
137123
<h3>Functions</h3>
138124
<h4><a id="open"></a><code>open: func</code></h4>
139125
<p>Get the bucket with the specified identifier.</p>

0 commit comments

Comments
 (0)