denoland / denoland/deploy_feedback
[KV Feedback]: Consistency with interdependent `get`s
- Dominant language
- No language data
- Stars
- 79
- Forks
- 5
- PR merge metrics
- No merged PRs in 30d
Description
### 🔍
- [x] Did you search for existing issues?
### Type of feedback
Documentation improvement
### Description
What's the best way to maintain consistency when `get`ing two interdependent values?
For example, let's store a book and its author.
```js
const db = await Deno.openKv(":memory:");
const authorKey = ["authors", 11];
await db.set(authorKey, { name: "William Writer" });
const bookKey = ["books", 1];
await db.set(bookKey, { title: "Tale of Tales", authorId: 11 });
```
The book stores the id of the author, such that given only the id of a book, we can read the book and its author.
```js
const bookId = 1;
const book = await db.get(["books", bookId]);
const authorId = book.value.authorId;
const author = await db.get(["authors", authorId]);
```
But this isn't atomically consistent. If a concurrent write happens in between the two `get` calls, we might not get what we expected. How can we ensure consistency here?
It seems there are two types of consistency here, "backwards" and "forwards" consistency. A concurrent write in between the two `get` calls could either change the book after we read it, or the author before we read it. Ideally, we want "batch consistency" like with `getMany`, where we get both how they are at a given time.
It seems we can ensure "backwards" consistency using `atomic().check(...)`. This way, if a concurrent write changes the book after we read it but before we read its author, we can abort and retry to get the updated book.
```js
const bookId = 1;
let author;
let book;
let check = { ok: false };
while (!check.ok) {
book = await db.get(["books", bookId]);
const authorId = book.value.authorId;
author = await db.get(["authors", authorId]);
check = await db
.atomic()
.check(author)
.check(book)
.commit();
}
// assuming the loop terminates, we should have consistent `book` and `author` here…
```
How can we ensure "forward" consistency? How can we detect if a concurrent write changes the author after we read the book but before we read its author? Maybe we don't need to. It could be just a very recent ("last minute", or rather millisecond) update to author, similar to if we had updated it yesterday. It would only be an issue, if there would be a second write to book to match the new author inconsistently afterwards. But assuming all writes are done consistently, a change to author without a change to book means it was just to author, and it's fine if it was yesterday or one millisecond ago.
Is this the right way to think about this?
Filing this as "documentation improvement" since I couldn't find this covered anywhere.
### Steps to reproduce (if applicable)
_No response_
### Expected behavior (if applicable)
_No response_
### Possible solution (if applicable)
_No response_
### Additional context
_No response_
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the Deno KV entry points shown in the report: get, getMany, and atomic().check(). Review the existing documentation and API behavior for interdependent reads, then document the supported consistency model and a clear example of the recommended approach.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- deno, javascript
- Domain
- database, documentation
- Issue type
- Documentation
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 28/100