perf(entgql): avoid COUNT(*) for pageInfo-only connection queries#625
perf(entgql): avoid COUNT(*) for pageInfo-only connection queries#625tutranngoc wants to merge 1 commit intoent:masterfrom
Conversation
|
@tutranngoc fyi, you can use entcache package to cache query result of count(). So it wont hit database twice. |
@giautm Thank you for sharing, however, my preference is to eliminate the need for a |
|
I don't think this PR handles all edge cases for the pagination. If you create a query that looks like this: variants(first: $firstVariant, after: $afterVariant) {
pageInfo {
hasNextPage
endCursor
}
}then we will not populate the pageInfo object correctly. If we request pagination info without any edges, we still need to do the count. I solved the same problem using an external template, where I used this logic instead: ignoredEdges := !hasCollectedField(ctx, edgesField)
needTotalCount := hasCollectedField(ctx, totalCountField)
needPageInfo := hasCollectedField(ctx, pageInfoField)
hasPagination := after != nil || first != nil || before != nil || last != nil
if (needTotalCount && hasPagination) || (ignoredEdges && (needTotalCount || needPageInfo)) {
c := {{ $r }}.Clone()
{{- /* Clear the selection fields before counting to avoid generating invalid queries. */}}
c.ctx.Fields = nil
if conn.TotalCount, err = c.Count(ctx); err != nil {
return nil, err
}
conn.PageInfo.HasNextPage = first != nil && conn.TotalCount > 0
conn.PageInfo.HasPreviousPage = last != nil && conn.TotalCount > 0
} |
Thanks for your post, I’ll give it a try. |
PR Description: Avoid unnecessary COUNT(*) query when resolving pageInfo
When querying a connection with
pageInfo, we always execute an extra SQL query (SELECT COUNT(*)). This is not required for computing pageInfo and adds avoidable datThe
build()call in:already computes cursor fields (
startCursor,endCursor) and correctly determines pagination flags (hasNextPage,hasPreviousPage) based on the paging arguments and returned nodes.Currently we also derive flags using
TotalCount, which forces aCOUNT(*):This makes every request that includes
pageInfopay for aCOUNT(*), even when callers don’t requesttotalCountand we don’t actually need it.What this PR changes:
Why it matters
Example query affected: