Discussion: "Some thoughts on asynchronous API design in a post-async/await world"

I may have commited the sin of using a fancy word when a plain one would do :slight_smile:. All I mean is: suppose f and g are two operations with side-effects. If I write f(); g();, then f’s side-effects should all happen before any of g’s side-effects. Or in other words: if I write f before g, then f should happen before g :-).

It’s probably easiest to explain with examples:

  • f is “make an edit to a document”, g is “share the document with other people”. Whoops, maybe you just sent the old version of your document, with the embarrassing mistake you thought you already corrected!

  • f is “send this data”, g is “terminate the program”. Whoops, maybe the data is never sent! In fact this works with practically anything in the f slot, which is why it’s so difficult to properly shut down a program using twisted or asyncio.

  • f is “write the new version to disk”, g is “delete the old version”. Doing it in this order guarantees you won’t lose data if the system crashes, right? Well, maybe not, if your system reorders it so g happens first. (Bonus: unless your test suite simulates random power outages, the only way you will discover this bug is because it destroyed production data.)

  • f is “send data to the remote peer”, g is “generate more data to send”. If the peer stops reading, you want to stop generating new data. Twisted/asyncio protocols don’t enforce this, which is why innocent-looking Twisted/asyncio programs are so prone to memory DoS.

1 Like