|
10 | 10 | /// |
11 | 11 | /// ## Consistency |
12 | 12 | /// |
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. |
18 | 20 | /// |
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. |
25 | 28 | /// |
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): |
29 | 31 | /// |
| 32 | +/// ``` |
30 | 33 | /// bucketA = open("foo") |
31 | 34 | /// bucketB = open("foo") |
32 | 35 | /// 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 |
35 | 39 | /// 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") |
38 | 40 | /// |
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") |
43 | 46 | /// |
44 | | -/// Continuing the above example: |
| 47 | +/// // Likewise, this will _eventually_ succeed in the absence of irrecoverable errors: |
| 48 | +/// assert bucketB.get("bar").equals("a") |
45 | 49 | /// |
46 | 50 | /// bucketB.set("bar", "b") |
47 | 51 | /// bucketC = open("foo") |
48 | 52 | /// value = bucketC.get("bar") |
| 53 | +/// |
| 54 | +/// // This is guaranteed to succeed: |
49 | 55 | /// assert value.equals("a") or value.equals("b") or value is None |
| 56 | +/// ``` |
50 | 57 | /// |
51 | 58 | /// In other words, the `bucketC` resource MAY reflect either the most recent write to the `bucketA` |
52 | 59 | /// resource, or the one to the `bucketB` resource, or neither, depending on how quickly either of |
53 | 60 | /// 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. |
58 | 65 | /// |
59 | 66 | /// ## Durability |
60 | 67 | /// |
|
0 commit comments