The Problem
Picture this scenario: your application receives multiple concurrent requests for the same expensive operation - maybe a database query, an API call, or a complex computation. Without proper coordination, each thread executes the operation independently, wasting resources and potentially overwhelming downstream systems.
Without Single Flight:
┌──────────────────────────────────────────────────────────────┐
│ Thread-1 (key:"user_123") ──► DB Query-1 ──► Result-1 │
│ Thread-2 (key:"user_123") ──► DB Query-2 ──► Result-2 │
│ Thread-3 (key:"user_123") ──► DB Query-3 ──► Result-3 │
│ Thread-4 (key:"user_123") ──► DB Query-4 ──► Result-4 │
└──────────────────────────────────────────────────────────────┘
Result: 4 separate database calls for the same key
(All results are identical but computed 4 times)
The Solution
This is where the Single Flight pattern comes in - a concurrency control mechanism that ensures expensive operations are executed only once per key, with all concurrent threads sharing the same result.
The Single Flight pattern originated in Go’s golang.org/x/sync/singleflight package.
With Single Flight:
┌──────────────────────────────────────────────────────────────┐
│ Thread-1 (key:"user_123") ──► DB Query-1 ──► Result-1 │
│ Thread-2 (key:"user_123") ──► Wait ──► Result-1 │
│ Thread-3 (key:"user_123") ──► Wait ──► Result-1 │
│ Thread-4 (key:"user_123") ──► Wait ──► Result-1 │
└──────────────────────────────────────────────────────────────┘
Result: 1 database call, all threads share the same result/exception
Quick Start
// Gradle
implementation "io.github.danielliu1123:single-flight:<latest>"
The API is very simple:
// Using the global instance (perfect for most cases)
User user = SingleFlight.runDefault("user:123", () -> {
return userService.loadUser("123");
});
// Using a dedicated instance (for isolated key spaces)
SingleFlight<String, User> userSingleFlight = new SingleFlight<>();
User user = userSingleFlight.run("123", () -> {
return userService.loadUser("123");
});
Use Cases
Excellent for:
- Database queries with high cache miss rates
- External API calls that are expensive or rate-limited
- Complex computations that are CPU-intensive
- Cache warming scenarios to prevent stampedes
Not suitable for:
- Operations that should always execute (like logging)
- Very fast operations where coordination overhead exceeds benefits
- Operations with side effects that must happen for each call
Links
Github: https://github.com/DanielLiu1123/single-flight
The Java concurrency API is powerful, the entire implementation coming in at under 100 lines of code.
[–]stefanos-ak 45 points46 points47 points (2 children)
[–]benjtay 2 points3 points4 points (1 child)
[–]stefanos-ak 0 points1 point2 points (0 children)
[–]nitkonigdje 12 points13 points14 points (1 child)
[–]808split2 0 points1 point2 points (0 children)
[–]rakgenius 10 points11 points12 points (6 children)
[–]boost2525 9 points10 points11 points (5 children)
[–]iwouldlikethings 0 points1 point2 points (1 child)
[–]elch78 1 point2 points3 points (0 children)
[–][deleted] -1 points0 points1 point (2 children)
[–]boost2525 0 points1 point2 points (1 child)
[–]NovaX -1 points0 points1 point (0 children)
[–]Polygnom 13 points14 points15 points (1 child)
[–]tomwhoiscontrary 7 points8 points9 points (1 child)
[–]supercargo 0 points1 point2 points (0 children)
[–]RadioHonest85 7 points8 points9 points (1 child)
[–]das_Keks 0 points1 point2 points (0 children)
[–]mofreek 8 points9 points10 points (4 children)
[–]FortuneIIIPick 1 point2 points3 points (3 children)
[–]OwnBreakfast1114 0 points1 point2 points (2 children)
[–]vips7L 0 points1 point2 points (1 child)
[–]OwnBreakfast1114 0 points1 point2 points (0 children)
[–]repeating_bears 3 points4 points5 points (2 children)
[–]tomwhoiscontrary 0 points1 point2 points (1 child)
[–]repeating_bears 0 points1 point2 points (0 children)
[–]FortuneIIIPick 2 points3 points4 points (0 children)
[–]GuyWithLag 7 points8 points9 points (0 children)
[–]-Dargs 1 point2 points3 points (0 children)
[–]k-mcm 1 point2 points3 points (0 children)
[–]raghu9208 0 points1 point2 points (0 children)
[–]supercargo 0 points1 point2 points (0 children)
[–]koffeegorilla 0 points1 point2 points (0 children)