TransactionalDOWrapper suggestions thread

Cool idea 👍 ! I recommend checking out do-transactional-outbox for an even stronger model of storage consistency. For your example in the blog post, I'm not sure if by using await you potentially eliminate (some of) the benefits of caching state in memory, let me explain. I recommend taking a look at state.blockConcurrencyWhile: call it in your constructor, no need to await it (you couldn't anyways, since constructors have to be sync), but it blocks further requests until you finish with your hydrate function. Then, you can always be sure that this.name and others will have been populated whenever your request comes in via fetch, so you can simplify your code further - eliminating another potential concurrency issue, where you're awaiting the hydrate promise, and another event handler starts executing, calling hydrate again, and potentially reading from storage once more as this.hydrated may not have been set to true yet (knock knock, race condition, who's there type situation), and then you overwrite name from the second call. Basically, use blockConcurrencyWhile in the constructor, move hydrate to within that call and remove it from fetch. Oh, small note, you could reduce boilerplate by exporting a function instead of a class, which applies the argument as the static class and returns that as the DO class for the runtime workers
8 Replies
Unknown User
Unknown User16mo ago
Message Not Public
Sign In & Join Server To View
DaniFoldi
DaniFoldi16mo ago
Yup, when you await a promise, it gets put into the microtask queue, and if there was another task in there, it will be executed first. See https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide/In_depth#tasks_vs_microtasks , warning it gets technical or boring, depending on your level of nerdiness. The way I like to think about it is async is just a label to show something returns a promise, and await wraps the rest of the code in a big .then() that is hidden from you for code readability reasons. But execution pauses when you await something, for example fetch() something, or read from DO storage. Other requests may be executed and even return before the first one does.
In depth: Microtasks and the JavaScript runtime environment - Web A...
When debugging or, possibly, when trying to decide upon the best approach to solving a problem around timing and scheduling of tasks and microtasks, there are things about how the JavaScript runtime operates under the hood that may be useful to understand.
Unknown User
Unknown User16mo ago
Message Not Public
Sign In & Join Server To View
kenton
kenton16mo ago
I'm not sure if I understand the question. The input gate only opens when you await a non-storage external I/O event, like a fetch(). The input gate will not open if you await a promise that resolves without waiting for any external I/O. The microtask queue will run, but external I/O will not arrive. if there are other tasks in the microtask queue, they may run, yes. However, new events triggered from the outside are prevented from being delivered to the microtask queue. So anything in the queue is something you must have put there.
DaniFoldi
DaniFoldi16mo ago
Doesn't awaiting on a promise that's not the storage operation itself, for example something from crypto also open the input gate?
Unknown User
Unknown User16mo ago
Message Not Public
Sign In & Join Server To View
kenton
kenton16mo ago
No. Only awaiting external I/O counts. Crypto promises are fake, the crypto is actually done synchronously and the promise completes immediately. (If we ever decided to offload crypto to another thread we would make sure it doesn't open the input gate to avoid breaking assumptions.) no, this will not open the input gate, because hydrate() does not do any I/O other than stroage.
Unknown User
Unknown User16mo ago
Message Not Public
Sign In & Join Server To View