I have a problem implementing the following flow using Elixir and Phoenix:
- Request from User A, 3rd party API cache is empty
- Initiate 3rd party API fetch via HTTP
- Fetch not finished yet, Request from User B comes in
- User B waits for fetch to complete
- Fetch finishes, write fetched data to cache (e.g. Redis)
- Serve all waiting users with the cached data
Different routes or route parameters should use different queues. Requests that come in while the 3rd party API data is still fetching should in no case trigger additional fetches with the same parameters. The waiting part (2.2.) is crucial to me.
From what I read so far, this problem seems to be solvable using standard Elixir / Erlang / OTP features.
Yes, this can be done quite easily in Elixir/Erlang compared to most other languages. Here's one way to do this with in memory caching. The thing to note here if you've used GenServer before but not
GenServer.reply/2
, is that we store thefrom
parameter of incominghandle_call
requests and when the request is complete, we respond to each of them. I'm not handling errors in a good way in this POC code but it handles the most interesting part, which is 2.2, correctly:Here's a short piece of code to test this:
Output:
Notice that using
GenServer.reply
andTask.start
, a single GenServer is able to handle more than 1 parallel requests while keeping the user facing API fully synchronous. Depending on how much load you want to handle, you might want to look into using a pool of GenServers.