SingleFlight
Deduplicate concurrent function calls by key. Inspired by Go's
singleflight package.
When multiple processes call SingleFlight.flight/3 with the same key
concurrently, only the first call executes the function. All other callers
block and receive the same result when the function completes.
This is useful for collapsing thundering-herd cache misses, deduplicating expensive API calls, or any scenario where concurrent identical work is wasteful.
Installation
Add single_flight to your list of dependencies in mix.exs:
def deps do
[
{:single_flight, "~> 0.1.0"}
]
endUsage
Add SingleFlight to your supervision tree:
children = [
{SingleFlight, name: MyApp.Flights}
]
Supervisor.start_link(children, strategy: :one_for_one)Then use it to deduplicate concurrent calls:
{:ok, user} = SingleFlight.flight(MyApp.Flights, "user:#{id}", fn ->
Repo.get!(User, id)
end)Forgetting a key
If you need to invalidate a key (e.g., after a write), call forget/2:
:ok = SingleFlight.forget(MyApp.Flights, "user:#{id}")
Existing in-flight waiters still receive the original result. Only new
callers after forget/2 trigger a fresh execution.
Error handling
If the function raises, throws, or exits, all waiting callers receive an
{:error, reason} tuple:
{:error, {%RuntimeError{message: "boom"}, _stacktrace}} =
SingleFlight.flight(MyApp.Flights, "bad", fn -> raise "boom" end)License
MIT — see LICENSE.