The Ack problem — Part 4

The acks you get from other APIs

Philippe Detournay
Xendit Engineering
3 min readJun 6, 2023

--

The tri-state boolean

As we all know, booleans can have three values: true, false, or file-not-found.

The Ack problem forces us to reconsider what are the possible outcome of any API call. Looking at a typical REST call:

  • The request was performed: any 2xx or 3xx reply;
  • The request was denied and hence not performed: any 4xx reply;
  • The request status is undefined, it may or may not have been performed: most of 5xx replies or other technical/network errors.

However, contrary to a DB or message broker, most services will typically not guarantee atomicity. This means that an “undefined” state doesn’t mean “done or not done”: the call may have been partially executed (more on this on the next post).

Done? Not done? Somewhat done? Somewhat not done?

In summary:

A 5xx or other network/technical error from an API does not mean it has not been completed, nor does it mean it has been completed. It also does not mean it is one of these two states: it may have been partially completed.

The path to recovery

Ideally, any API that is not inherently idempotent (REST POST calls, obviously, but this applies to any other type of API such as DB etc.) should clearly document the expected caller’s course of action in case of unexpected conditions.

There can be different approaches:

  • Offer an API to discover what actions, if any, got performed during the previously failed call. This is by far the most typical approach for Web-Application APIs (i.e. refresh the browser and check your basket’s status);
  • Offer a way to “roll-forward” a previous call by allowing a subsequent call of the same API with the same parameters. This is by far the most typical approach for pure Application-to-application APIs;
  • There are other less common approaches, such as to offer a different set of API to either “roll-forward” or even “undo” any partial execution, etc.

No matter what options are documented, it is the responsibility of the API caller to execute them in case of unexpected failure. And this is not easy: in many cases the caller may not even be aware that a previous execution happened at all, for instance in case of an unexpected server shutdown.

In a subsequent post, we’ll discuss the typical patterns and approaches that can be implemented in an API client with regards to failed call recovery.

I’ll fix that later…

So far, we’ve seen that, similar to the “letterbox” issue we depicted on the first article, calling an API (being a DB, a message broker or anything else) is not as easy as it originally looked. It seems as if calling any API was an almost impossible feast!

In the next article, we’ll look at the problem from the opposite side: from the point of view of the API call receiver. From there, we’ll be able to start thinking about potential solutions.

--

--