Skip to content

Commit cf03f8a

Browse files
committed
remove read-your-writes guarantee
This removes the "read-your-writes" guarantee since several of the backing stores we wish to support either do not support it or do not support it by default. Signed-off-by: Joel Dice <[email protected]>
1 parent 94183c2 commit cf03f8a

File tree

3 files changed

+112
-87
lines changed

3 files changed

+112
-87
lines changed

imports.md

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,44 +24,53 @@ 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>Any implementation of this interface MUST have enough consistency to guarantee &quot;reading your
28-
writes&quot; for read operations on the same <a href="#bucket"><code>bucket</code></a> resource instance. Reads from <a href="#bucket"><code>bucket</code></a>
29-
resources other than the one used to write are <em>not</em> guaranteed to return the written value
30-
given that the other resources may be connected to other replicas in a distributed system, even
31-
when opened using the same bucket identifier.</p>
32-
<p>In particular, this means that a <code>get</code> call for a given key on a given <a href="#bucket"><code>bucket</code></a>
33-
resource MUST never return a value that is older than the the last value written to that key
34-
on the same resource, but it MAY get a newer value if one was written around the same
35-
time. These guarantees only apply to reads and writes on the same resource; they do not hold
36-
across multiple resources -- even when those resources were opened using the same string
37-
identifier by the same component instance.</p>
38-
<p>The following pseudocode example illustrates this behavior. Note that we assume there is
39-
initially no value set for any key and that no other writes are happening beyond what is shown
40-
in the example.</p>
41-
<p>bucketA = open(&quot;foo&quot;)
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;)
4244
bucketB = open(&quot;foo&quot;)
4345
bucketA.set(&quot;bar&quot;, &quot;a&quot;)
44-
// The following are guaranteed to succeed:
45-
assert bucketA.get(&quot;bar&quot;).equals(&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
4649
assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;) or bucketB.get(&quot;bar&quot;) is None
47-
// ...whereas this is NOT guaranteed to succeed immediately (but SHOULD eventually):
48-
// assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;)</p>
49-
<p>Once a value is <code>set</code> for a given key on a given <a href="#bucket"><code>bucket</code></a> resource, all subsequent <code>get</code>
50-
requests on that same resource will reflect that write or any subsequent writes. <code>get</code> requests
51-
using a different bucket may or may not immediately see the new value due to e.g. cache effects
52-
and/or replication lag.</p>
53-
<p>Continuing the above example:</p>
54-
<p>bucketB.set(&quot;bar&quot;, &quot;b&quot;)
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;)
5561
bucketC = open(&quot;foo&quot;)
5662
value = bucketC.get(&quot;bar&quot;)
57-
assert value.equals(&quot;a&quot;) or value.equals(&quot;b&quot;) or value is None</p>
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>
5867
<p>In other words, the <code>bucketC</code> resource MAY reflect either the most recent write to the <code>bucketA</code>
5968
resource, or the one to the <code>bucketB</code> resource, or neither, depending on how quickly either of
6069
those writes reached the replica from which the <code>bucketC</code> resource is reading. However,
61-
assuming there are no unrecoverable errors -- such that the state of a replica is irretrievably
62-
lost before it can be propagated -- one of the values (&quot;a&quot; or &quot;b&quot;) SHOULD eventually be
63-
considered the &quot;latest&quot; and replicated across the system, at which point all three resources
64-
will return that same value.</p>
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>
6574
<h2>Durability</h2>
6675
<p>This interface does not currently make any hard guarantees about the durability of values
6776
stored. A valid implementation might rely on an in-memory hash table, the contents of which are

watch-service.md

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,53 @@ 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>Any implementation of this interface MUST have enough consistency to guarantee &quot;reading your
26-
writes&quot; for read operations on the same <a href="#bucket"><code>bucket</code></a> resource instance. Reads from <a href="#bucket"><code>bucket</code></a>
27-
resources other than the one used to write are <em>not</em> guaranteed to return the written value
28-
given that the other resources may be connected to other replicas in a distributed system, even
29-
when opened using the same bucket identifier.</p>
30-
<p>In particular, this means that a <code>get</code> call for a given key on a given <a href="#bucket"><code>bucket</code></a>
31-
resource MUST never return a value that is older than the the last value written to that key
32-
on the same resource, but it MAY get a newer value if one was written around the same
33-
time. These guarantees only apply to reads and writes on the same resource; they do not hold
34-
across multiple resources -- even when those resources were opened using the same string
35-
identifier by the same component instance.</p>
36-
<p>The following pseudocode example illustrates this behavior. Note that we assume there is
37-
initially no value set for any key and that no other writes are happening beyond what is shown
38-
in the example.</p>
39-
<p>bucketA = open(&quot;foo&quot;)
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;)
4042
bucketB = open(&quot;foo&quot;)
4143
bucketA.set(&quot;bar&quot;, &quot;a&quot;)
42-
// The following are guaranteed to succeed:
43-
assert bucketA.get(&quot;bar&quot;).equals(&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
4447
assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;) or bucketB.get(&quot;bar&quot;) is None
45-
// ...whereas this is NOT guaranteed to succeed immediately (but SHOULD eventually):
46-
// assert bucketB.get(&quot;bar&quot;).equals(&quot;a&quot;)</p>
47-
<p>Once a value is <code>set</code> for a given key on a given <a href="#bucket"><code>bucket</code></a> resource, all subsequent <code>get</code>
48-
requests on that same resource will reflect that write or any subsequent writes. <code>get</code> requests
49-
using a different bucket may or may not immediately see the new value due to e.g. cache effects
50-
and/or replication lag.</p>
51-
<p>Continuing the above example:</p>
52-
<p>bucketB.set(&quot;bar&quot;, &quot;b&quot;)
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;)
5359
bucketC = open(&quot;foo&quot;)
5460
value = bucketC.get(&quot;bar&quot;)
55-
assert value.equals(&quot;a&quot;) or value.equals(&quot;b&quot;) or value is None</p>
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>
5665
<p>In other words, the <code>bucketC</code> resource MAY reflect either the most recent write to the <code>bucketA</code>
5766
resource, or the one to the <code>bucketB</code> resource, or neither, depending on how quickly either of
5867
those writes reached the replica from which the <code>bucketC</code> resource is reading. However,
59-
assuming there are no unrecoverable errors -- such that the state of a replica is irretrievably
60-
lost before it can be propagated -- one of the values (&quot;a&quot; or &quot;b&quot;) SHOULD eventually be
61-
considered the &quot;latest&quot; and replicated across the system, at which point all three resources
62-
will return that same value.</p>
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>
6372
<h2>Durability</h2>
6473
<p>This interface does not currently make any hard guarantees about the durability of values
6574
stored. A valid implementation might rely on an in-memory hash table, the contents of which are

wit/store.wit

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,58 @@
1010
///
1111
/// ## Consistency
1212
///
13-
/// Any implementation of this interface MUST have enough consistency to guarantee "reading your
14-
/// writes" for read operations on the same `bucket` resource instance. Reads from `bucket`
15-
/// resources other than the one used to write are _not_ guaranteed to return the written value
16-
/// given that the other resources may be connected to other replicas in a distributed system, even
17-
/// when opened using the same bucket identifier.
13+
/// An implementation of this interface MUST be eventually consistent, meaning that, after some time
14+
/// with no further updates, all replicas in the (potentially distributed) system will eventually
15+
/// converge on a consistent state for all values. This allows replicas to temporarily diverge to
16+
/// ensure low latency and high availability. Implementations based on a centralized or local
17+
/// backing store may provide a stronger consistency model, but guest components which are intended
18+
/// to be portable to any `wasi-keyvalue` implementation should not rely on anything stronger than
19+
/// eventual consistency.
1820
///
19-
/// In particular, this means that a `get` call for a given key on a given `bucket`
20-
/// resource MUST never return a value that is older than the the last value written to that key
21-
/// on the same resource, but it MAY get a newer value if one was written around the same
22-
/// time. These guarantees only apply to reads and writes on the same resource; they do not hold
23-
/// across multiple resources -- even when those resources were opened using the same string
24-
/// identifier by the same component instance.
21+
/// Given that each `bucket` resource may represent a connection to a different replica in a
22+
/// distributed system, values read for a given key from two different `bucket`s may differ, even if
23+
/// those `bucket` resources were opened using the same string identifier. In addition, consecutive
24+
/// operations on a single `bucket` resource may produce temporarily inconsistent results if
25+
/// e.g. the implementation is forced to reconnect to a different replica due to a connection
26+
/// failure. For example, a write followed by a read may not return the value just written, even if
27+
/// no other recent or subsequent writes have occurred.
2528
///
26-
/// The following pseudocode example illustrates this behavior. Note that we assume there is
27-
/// initially no value set for any key and that no other writes are happening beyond what is shown
28-
/// in the example.
29+
/// Consider the following pseudocode example (and assume we start with an empty store and no other
30+
/// concurrent activity):
2931
///
32+
/// ```
3033
/// bucketA = open("foo")
3134
/// bucketB = open("foo")
3235
/// bucketA.set("bar", "a")
33-
/// // The following are guaranteed to succeed:
34-
/// assert bucketA.get("bar").equals("a")
36+
///
37+
/// // These are guaranteed to succeed:
38+
/// assert bucketA.get("bar").equals("a") or bucketA.get("bar") is None
3539
/// assert bucketB.get("bar").equals("a") or bucketB.get("bar") is None
36-
/// // ...whereas this is NOT guaranteed to succeed immediately (but SHOULD eventually):
37-
/// // assert bucketB.get("bar").equals("a")
3840
///
39-
/// Once a value is `set` for a given key on a given `bucket` resource, all subsequent `get`
40-
/// requests on that same resource will reflect that write or any subsequent writes. `get` requests
41-
/// using a different bucket may or may not immediately see the new value due to e.g. cache effects
42-
/// and/or replication lag.
41+
/// // This is likely to succeed, but not guaranteed; e.g. `bucketA` might need to reconnect to a
42+
/// // different replica which hasn't received the above write yet. It will _eventually_
43+
/// // succeed, provided there are no irrecoverable errors which prevent the propagation of the
44+
/// // write.
45+
/// assert bucketA.get("bar").equals("a")
4346
///
44-
/// Continuing the above example:
47+
/// // Likewise, this will _eventually_ succeed in the absence of irrecoverable errors:
48+
/// assert bucketB.get("bar").equals("a")
4549
///
4650
/// bucketB.set("bar", "b")
4751
/// bucketC = open("foo")
4852
/// value = bucketC.get("bar")
53+
///
54+
/// // This is guaranteed to succeed:
4955
/// assert value.equals("a") or value.equals("b") or value is None
56+
/// ```
5057
///
5158
/// In other words, the `bucketC` resource MAY reflect either the most recent write to the `bucketA`
5259
/// resource, or the one to the `bucketB` resource, or neither, depending on how quickly either of
5360
/// those writes reached the replica from which the `bucketC` resource is reading. However,
54-
/// assuming there are no unrecoverable errors -- such that the state of a replica is irretrievably
55-
/// lost before it can be propagated -- one of the values ("a" or "b") SHOULD eventually be
56-
/// considered the "latest" and replicated across the system, at which point all three resources
57-
/// will return that same value.
61+
/// assuming there are no irrecoverable errors -- such that the state of a replica is irretrievably
62+
/// lost before it can be propagated -- one of the values ("a" or "b") MUST eventually be considered
63+
/// the "latest" and replicated across the system, at which point all three resources will return
64+
/// that same value.
5865
///
5966
/// ## Durability
6067
///

0 commit comments

Comments
 (0)