graphene-sqlalchemy: N + 1 round trip problem
Does this library handle nested models (joins) in a single query from the server to the DB? For example
user {
id
posts {
id
}
}
About this issue
- Original URL
- State: open
- Created 7 years ago
- Reactions: 2
- Comments: 25 (7 by maintainers)
Another option is to use a lazy-loader for SQLAlchemy that is more intelligent about bulk loading. We wrote a custom loader that will inspect the DB session and bulk-load all relations on similar models whenever a relation is lazy-loaded. The result should be the equivalent to using
subqueryloadon all relations upfront, but without needing any custom code to do so. The loader we wrote is available here in case it’s helpful to anyone else: https://github.com/operator/sqlalchemy_bulk_lazy_loaderThank you for that blog post @yfilali, that was really helpful.
Your
optimize_resolvemethod works pretty well for me. By usingjoinedloadinstead ofsubqueryload, I was able to reduce a deeply nested GraphQL query into a single database query. I had to add all paths, however, not only the leaves (simply removed theif not p.isLeafpart when assembling the joins).Another small problem is that in
resolve_relatedyou need to translate the field names from GraphQL (camel case) to SQLAlchemy (snake case) withfield = to_snake_case(field), otherwise it doesn’t work with table columns with underscores in them. You can import theto_snake_casemethod fromgraphene.utils.str_converters.I feel something like this should be really integrated into graphene-sqlalchemy. Or at least it should provide some helper functions which make it easy to implement such optimized querying, with various loading strategies as options. After all, one of the main advantages of GraphQL over REST is that it solves the N+1 problem. This advantage would be greater if it existed also on the level of the database, not only on the network level.
A bit late to the party. Also: shameless self-promotion. But I’ve just published an article about this subject which includes a solution where you specify what and how to eagerly load related data in a node’s metadata on a per-field basis: https://blurringexistence.net/graphene-sqlalchemy-n-plus-1.html
(SQLAlchemy 1.4.40)
I’ve activated
batching = Trueon all my graphene-sqlalchemy translated models, and whenever I try to access some relationships, the graphql resolver is failing with “future is attached to a different loop”This is the GraphQL Query:
I get an error on the
partresolver (but also in several other places in the system, basically it’s not working correctly in most places)This is the traceback:
This query is happening from the GraphQL Playground from a FastAPI app
@mekhami I think it’s valuable and I can’t access it too. I read it from google cache several months ago and I do think it’s worth to read. But the catch itself is 404 now, maybe because the original page is down for too long. I saved the cache just in case: GraphQL_Yacine.zip (github don’t allow .htm file so I compress it)
@yfilali I will delete the cache page if you are not ok with this.