Skip to content

Instantly share code, notes, and snippets.

@bion
Last active February 24, 2021 21:19
Show Gist options
  • Save bion/66979b02e2ff93e0ba61a79baaf6376a to your computer and use it in GitHub Desktop.
Save bion/66979b02e2ff93e0ba61a79baaf6376a to your computer and use it in GitHub Desktop.
Threading in Play
// See https://www.playframework.com/documentation/2.8.x/ThreadPools for more info
//
// Applicant-accessible code paths should never block on network I/O.
// If you use CompleteableFuture#join() be very careful it is not on an applicant-accessible code path.
class ThreadingDemo {
private final HttpExecutionContext httpExecutionContext;
private final DatabaseExecutionContext databaseExecutionContext;
private final DemoService demoService;
@Inject
ThreadingDemo(HttpExecutionContext httpExecutionContext,
DatabaseExecutionContext DatabaseExecutionContext,
DemoService demoService) {
this.httpExecutionContext = checkNotNull(httpExecutionContext);
this.databaseExecutionContext = checkNotNull(DatabaseExecutionContext);
this.demoService = checkNotNull(demoService);
}
// Code that makes synchronous calls to the database should always run on the database
// dispatcher thread pool i.e. DatabaseExecutionContext.
public CompletionStage<Applicant> databaseInteraction() {
return supplyAsync(() -> {
Applicant applicant = new Applicant();
applicant.id = 1L;
applicant.refresh(); // synchronous DB call
return applicant;
}, databaseExecutionContext);
}
// Using HttpExecutionContext when resuming from an async call ensures that
// callbacks executed when the completion stage is redeemed have the correct ClassLoader
public CompletionStage<FinalResultType> singleAsyncDependency() {
return demoService.getDemoThing().thenApplyAsync((DemoThing demoThing) -> {
return new FinalResultType(demoThing);
}, httpExecutionContext.current());
}
// When you want to do work that depends on multiple async calls that do not depend
// on eachother, use CompletableFuture.allOf
// This can allow the network I/O to run in parallel.
public CompletionStage<FinalResultType> multipleAsyncDependencies() {
// initiate async calls for the dependencies
CompletableFuture<Applicant> applicantCompletableFuture = databaseInteraction().toCompletableFuture();
CompletableFuture<DemoThing> demoThingCompleteableFuture = demoService.getDemoThing().toCompletableFuture();
CompletableFuture<OtherDemoThing> otherDemoThingCompleteableFuture = demoService.getOtherDemoThing().toCompletableFuture();
// create a CompletionStage that completes when all dependencies have completed
return CompletableFuture.allOf(applicantCompletableFuture, demoThingCompleteableFuture, otherDemoThingCompleteableFuture)
// register a callback on the current execution context for when the depencies are ready
.thenApplyAsync(
() -> {
Applicant applicant = applicantCompletableFuture.join();
DemoThing demoThing = demoThingCompleteableFuture.join();
OtherDemoThing otherDemoThing = otherDemoThingCompleteableFuture.join();
return new FinalResultType(applicant, demoThing, otherDemoThing);
},
httpExecutionContext.current());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment