Can we use `blockConcurrencyWhile` inside `fetch` method of a Durable Object?

Scenario; There is a single instance of a durable object responsible only for registering users. This durable object puts user data into a KV namespace. Only this durable object writes to this KV namespace. First, it reads KV if the user already exists and then writes. To avoid race, can I use blockConcurrencyWhile inside fetch? I know that durable objects already have transactional storage but I need to write data to KV namespace, that is used by other workers as read-only. I only see blockConcurrencyWhile used in durable object initialization that's why I ask.
16 Replies
Hello, I’m Allie!
Yes, like this:
export class DOClass implements DurableObject {
private readonly state: DurableObjectState;
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch() {
await this.state.blockConcurrencyWhile(() => {
// Do KV stuff here.
});
}
}
export class DOClass implements DurableObject {
private readonly state: DurableObjectState;
constructor(state: DurableObjectState) {
this.state = state;
}
async fetch() {
await this.state.blockConcurrencyWhile(() => {
// Do KV stuff here.
});
}
}
Alp
Alp9mo ago
Thank you for your quick answer and code snippet 🙂 . So we can have consistent writes to KV. The only down side is, it is not good for high traffic routes, but for user registration it is ok I think.
Isaac McFadyen
Isaac McFadyen9mo ago
Keep in mind KV is not consistent. Even if you insert non-concurrently (with blockConcurrencyWhile) it's still possible other Workers will read the old value for up to 60s after the insert. The only thing blockConcurrencyWhile does is ensure that two writes don't overwrite one another. It doesn't ensure reads are always up to date.
Hello, I’m Allie!
And it will only prevent overwriting if you aren't reading from KV too
Isaac McFadyen
Isaac McFadyen9mo ago
AFAIK reads won't ever overwrite a write? Like, if you read a value it might be stale but the write will still win eventually.
Hello, I’m Allie!
No, I mean that if your operation depends on mutating the state of a value(like addition), then a read-then-write model with KV won't be consistent, blockConcurrencyWhile or no
Isaac McFadyen
Isaac McFadyen9mo ago
Ah, I see. Yeah, that's true.
Hello, I’m Allie!
If it is just replacing a value, then it doesn't matter, but then you wouldn't need a DO for it at all
Isaac McFadyen
Isaac McFadyen9mo ago
Yeah. The only way around that would be to use DO's consistent key-value store to store the counter and then update in KV every time you write it.
Alp
Alp9mo ago
I see that, what I think about reading is workers will try to get user from KV, and on fail, it will ask to this durable object as second try. Kv is read only for login purposes.
Isaac McFadyen
Isaac McFadyen9mo ago
So yeah, that would work well as long as you don't ever update a user based on the user's last value. The reason the write-after-read won't work well is because: - DO does a write, which might not be visible for a while - DO does a read and then another write based on that read, but the read might have been stale - so you just wrote a value based on an incorrect read
Hello, I’m Allie!
Should be fine, as long as your DO never reads from KV, only writes
Isaac McFadyen
Isaac McFadyen9mo ago
If you are always doing a write, with no read, then you should be good.
Alp
Alp9mo ago
What I understand is, after DO shutdown, DO may spawn on another location where KV is not synced yet. So for my purposes this will not work actually 😬
Isaac McFadyen
Isaac McFadyen9mo ago
Yeah, that's possible but very unlikely. Could you perhaps use the key-value API DO provides? That is consistent. The reason DO migration is unlikely is because DOs are pinned to the datacenter that created them unless there is some sort of shutdown or disaster in that DC, in which case they will migrate to a nearby one (still very close but different KV cache indeed).
Alp
Alp9mo ago
The best solution looks like using DOs storage as it is designed for this. My concern is that using only one instance DO for user authentication may cause slow login around the world but I think it is not a big problem since for further requests I can use session data that is written to KV. Thank you all again for going into detail about the subject. Love you ❤️ I've never thought of this and it's a good idea. My biggest concern is registering two users with the same username. This may solve this issue; I can spawn durable object using the username, and if the object doesn't have stored data, it means username is available. Thank you for the idea