Published on

Ring Fencing Async Complexity


A couple weeks ago I spoke about how tricky async code can be.

In our case we needed the async portion as we had to be able to receive multiple web socket connections from different sources at the same time - so we could not simply switch to using sync code.

In the end, the approach that we used to resolve this was to:

  • Move the portion that had to be async out to a separate codebase.
  • Convert the code that did not need to be async to sync.
  • Use a queue to share data between the two jobs.
    • Popular queues have sync and async versions of the library.

This approach worked in our case as the async portion did not have to do any writes to a DB. Instead, when it received a message - that it needed to share- it instead placed it onto a queue.

On the sync side, we just kept checking the queue and processing messages from there.

This solved the issue as most of our race conditions were happening when we were trying to write to the DB when the code was async. This resulted in us getting dirty reads. By switching to using a sync process to write to the DB we guaranteed that we only move onto the next job once the dependent job is finished which eliminated this issue.