Single Flight is used to prevent the duplicate execution of expensive operations in concurrent environments.
The Single Flight pattern is a concurrency control mechanism that ensures expensive operations (such as database queries, API calls, or complex computations) are executed only once per key when multiple threads request the same resource concurrently.
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)
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
Maven:
<dependency>
<groupId>io.github.danielliu1123</groupId>
<artifactId>single-flight</artifactId>
<version>latest</version>
</dependency>Gradle:
implementation "io.github.danielliu1123:single-flight:<latest>"Used as source:
mkdir -p singleflight && curl -L -o singleflight/SingleFlight.java https://raw.githubusercontent.com/DanielLiu1123/single-flight/refs/heads/main/single-flight/src/main/java/singleflight/SingleFlight.java// 1. Using the global instance
User user = SingleFlight.runDefault("user:123", () -> {
return userService.loadUser("123");
});
// 2. Using a dedicated instance
// 2-1. Using default options
SingleFlight<String, User> userSingleFlight = new SingleFlight<>();
// 2-2. Using custom options
SingleFlight<String, User> userSingleFlight = new SingleFlight<>(
SingleFlight.Options.builder()
.cacheException(true)
.build()
);
User user = userSingleFlight.run("123", () -> {
return userService.loadUser("123");
});Single Flight is particularly effective in the following scenarios:
- Database queries with high cache miss rates
- External API calls that are expensive or rate-limited
- Complex computations that are CPU-intensive
- Cache warming operations to prevent cache stampedes
Single Flight should be avoided in these situations:
- Operations that must always execute independently (e.g., logging, metrics collection)
- Very fast operations where coordination overhead exceeds the benefits
- Operations with side effects that need to occur for each individual call
The MIT License.