Skip to content

Commit 735f05b

Browse files
authored
Merge pull request #109 from sangria-graphql/performance_tips
performance tips
2 parents cbb3f4f + 1e4ed89 commit 735f05b

File tree

1 file changed

+71
-0
lines changed

1 file changed

+71
-0
lines changed

learn.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3005,3 +3005,74 @@ You can find a parser function in `sangria.introspection.IntrospectionParser`.
30053005
### Determine a Query Operation Type
30063006
30073007
Sometimes it can be very useful to know the type of query operation. For example you need it if you want to return a different response for subscription queries. `ast.Document` exposes `operationType` and `operation` for this.
3008+
3009+
## Performance tips.
3010+
3011+
Sangria is being used by several companies on production since several years and capable of handling a lot of traffic.
3012+
3013+
Sangria is indeed a fast library. If you want to take the maximum out of it, here are some guidelines and tips.
3014+
3015+
### Compute the schema only once
3016+
3017+
Make sure that you only compute the schema once. This easiest way is to use a singleton object to define the schema:
3018+
3019+
```scala
3020+
object GraphQLSchema {
3021+
val QueryType = ???
3022+
val MutationType = ???
3023+
val schema = Schema(QueryType, Some(MutationType))
3024+
}
3025+
```
3026+
3027+
If you compute the schema at each request, you will lose a lot of performances.
3028+
3029+
### Load the schema before the first request
3030+
3031+
If you are using a web server, make sure that you load the schema before the first request.
3032+
This way, all classes will be loaded before the web server starts and the first request will not be slower than the others.
3033+
3034+
```scala
3035+
object Main extends App {
3036+
val schema = GraphQLSchema.schema
3037+
3038+
// start the server
3039+
}
3040+
```
3041+
3042+
### Use the `parasitic` ExecutionContext (expert)
3043+
3044+
Sangria uses `Future` to handle asynchronous operations. When you execute a query, you need to pass an `ExecutionContext`.
3045+
3046+
One way to improve performances is to use the `scala.concurrent.ExecutionContext.parasitic` ExecutionContext.
3047+
But be careful that this ExecutionContext will propagate everywhere, including in the `DeferredResolver` where it might not be the best option. You might be using the wrong thread pool for IO operations.
3048+
3049+
To avoid this, you can pack the ExecutionContext in your Context and use it in the `DeferredResolver`:
3050+
3051+
```scala
3052+
object DeferredReferenceResolver extends DeferredResolver[Context] {
3053+
def resolve(
3054+
deferred: Vector[Deferred[Any]],
3055+
ctx: Context,
3056+
queryState: Any
3057+
)(implicit ec: ExecutionContext): Vector[Future[Any]] =
3058+
resolveInternal(deferred, ctx)
3059+
3060+
private def resolveInternal(
3061+
deferred: Vector[Deferred[Any]],
3062+
ctx: Context
3063+
): Vector[Future[Any]] = {
3064+
// for IO, uses non-parasitic ExecutionContext
3065+
implicit val ec: ExecutionContext = ctx.executionContext
3066+
???
3067+
}
3068+
}
3069+
3070+
Object GraphQLSchema {
3071+
val schema = Schema(QueryType, deferredResolver = DeferredReferenceResolver)
3072+
def execute(ctx: Context, query: Document): Future[Json] {
3073+
// just for internal execution, uses parasitic ExecutionContext
3074+
implicit val ec = scala.concurrent.ExecutionContext.parasitic
3075+
Executor.execute(schema, query)
3076+
}
3077+
}
3078+
```

0 commit comments

Comments
 (0)