Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider eventual consisteny in TODO example #42

Closed
StarpTech opened this issue Apr 26, 2021 · 4 comments
Closed

Consider eventual consisteny in TODO example #42

StarpTech opened this issue Apr 26, 2021 · 4 comments

Comments

@StarpTech
Copy link

StarpTech commented Apr 26, 2021

After several weeks working with Cloudflare workers, I just realized how the KV storage behaves from an application perspective. As mentioned in the documentation reads and write are eventually consistent.

Due to the eventually consistent nature of Workers KV, concurrent writes from different edge locations can end up up overwriting one another. It’s a common pattern to write data via Wrangler or the API but read the data from within a worker, avoiding this issue by issuing all writes from the same location.

Note that get may return stale values -- if a given key has recently been read in a given location, changes to the key made in other locations may take up to 60 seconds to be visible. See How KV works for more on this topic.

Ref: https://developers.cloudflare.com/workers/runtime-apis/kv#reading-key-value-pairs

This means we can't write based on a previous read state. In the TODO example, we will lose data when multiple users create TODO's in the time range of the synchronization between the edges.

  1. User A creates a new TODO item.
  2. User B creates a new TODO item.
  3. User B sync list.
  4. User A sync list.
  5. TODO item from User B is missing or vice versa.

I opened this issue to communicate that circumstance a bit deeper. Durable Objects will solve it but I'm surprised that they can only hold 32KiB 😕

@StarpTech StarpTech changed the title Bad example: TODO list Consider eventual consisteny in TODO example Apr 26, 2021
@lukeed
Copy link
Owner

lukeed commented Apr 27, 2021

In the TODO example, we will lose data when multiple users create TODO's in the time range

There will be synchronization issues if UserA and UserB are writing into the same key, but that doesn't happen in this example. The example is setup in a way so that UserA only deals with UserA's todos (eg, user::UserA::todos) and UserB only cares about their own todos (eg, user::UserB::todos).

That said, if UserA adds multiple todos in close succession, then that may cause replication issues within the user::UserA::todos document. This is because the save() helper is called at the end of the insert model method, which is hard-coded to write into the same key.

This save method could be skipped entirely in favor of a new list() model method that would use a prefix match for keys. Across regions, there will still be replication delay, but there won't ever be actual data loss or (true) data inconsistency because there's no write/update into the same document. In practice, this means that users in other regions may see items missing during their KV.list result, but it's guaranteed to show up within the minute.

This updated, hypothetical list method would look like this:

export async function list(username: string): Promise<Todo['uid'][]> {
  const prefix = key_owner(username) + '::';
  const keys = await TODOS.list({ prefix, limit: 10 });

  // The current example returns Array<Todo['uid']>
  // So stopping here, but could do Promise.all(/* key lookup */)
  
  // strip prefix; eg `key_item - (key_owner + '::') = uid`
  return keys.map(key => key.substring(prefix.length));
}

Durable Objects will solve it but I'm surprised that they can only hold 32KiB

DOs are still in beta & worktop (purposefully) doesn't integrate with or support them yet. This is to avoid early/unnecessary breaking API changes while DO-things continue to finalize.

@StarpTech
Copy link
Author

Hi @lukeed thanks for the write-up. Indeed, the list is scoped to the user. Great tip with the prefix-match. In combination with the metadata, it allows storing a kind of search index next to the item to avoid loading all key values.

Do you plan to support the metadata option in the WriteOptions ?

@StarpTech
Copy link
Author

metadata is also missing on the ListOptions type.

@lukeed
Copy link
Owner

lukeed commented Apr 30, 2021

Yup. PR in the works for all metadata-stuff. Was a busy week so havent had time to finish it up yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants