vitessio/vitess · error
in-memory row count exceeded allowed limit of %d
Error message
in-memory row count exceeded allowed limit of %d
What it means
MemorySort aborts execution when the number of rows buffered in memory for an in-memory sort exceeds the vcursor's max memory rows setting. Vitess enforces this to protect vtgate from OOM when a sort cannot be pushed down to the underlying tablets. The configured limit is exposed via vcursor.MaxMemoryRows().
Source
Thrown at go/vt/vtgate/engine/memory_sort.go:99
sorter := &evalengine.Sorter{
Compare: ms.OrderBy,
Limit: count,
}
var mu sync.Mutex
err = vcursor.StreamExecutePrimitive(ctx, ms.Input, bindVars, wantfields, func(qr *sqltypes.Result) error {
mu.Lock()
defer mu.Unlock()
if len(qr.Fields) != 0 {
if err := cb(&sqltypes.Result{Fields: qr.Fields}); err != nil {
return err
}
}
for _, row := range qr.Rows {
sorter.Push(row)
}
if vcursor.ExceedsMaxMemoryRows(sorter.Len()) {
return fmt.Errorf("in-memory row count exceeded allowed limit of %d", vcursor.MaxMemoryRows())
}
return nil
})
if err != nil {
return err
}
return cb(&sqltypes.Result{Rows: sorter.Sorted()})
}
// GetFields satisfies the Primitive interface.
func (ms *MemorySort) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) {
return ms.Input.GetFields(ctx, vcursor, bindVars)
}
// Inputs returns the input to memory sort
func (ms *MemorySort) Inputs() ([]Primitive, []map[string]any) {
return []Primitive{ms.Input}, nil
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Increase vtgate's max_memory_rows (and query_server_max_memory_rows) to accommodate the workload.
- Rewrite the query so ORDER BY can be pushed down (sort column part of the routing/shard key or covered by an index).
- Reduce the result set with tighter WHERE filters or pagination.
- If streaming is acceptable, use ExecuteStream so rows are not fully buffered.
Example fix
// before vtgate --max_memory_rows=100000 // after vtgate --max_memory_rows=10000000 --query_server_max_memory_rows=10000000
Defensive patterns
Strategy: try-catch
Validate before calling
// before executing, estimate result size
if len(whereClauseFilters) == 0 && hasOrderBy {
log.Warn("unbounded ORDER BY query may exceed max_memory_rows")
} Try / catch
err := vcursor.Execute(ctx, primitive, bindVars)
if err != nil && strings.Contains(err.Error(), "in-memory row count exceeded allowed limit") {
// fall back to paginated execution or tune max_memory_rows
} Prevention
- Size max_memory_rows to your largest expected sorted result set.
- Keep ORDER BY columns covered by shard-local indexes so sorts push down.
- Use LIMIT/pagination on large scans instead of sorting everything in vtgate.
When it happens
Trigger: Executing (non-streaming) a query with an ORDER BY that must be materialized in vtgate, where sorter.Len() after pushing all rows exceeds the --max_memory_rows threshold.
Common situations: Large unsorted result sets with ORDER BY on a non-indexed column; default max_memory_rows too small for production workloads; queries on shards lacking ordering indexes forcing vtgate-side sort.
Related errors
- in-memory row count exceeded allowed limit of %d
- cell mode should be handled by the gateway, not the balancer
- both the dry-run mode and actual buffering is enabled. To av
- unix sockets are not supported on windows
- Binary.ReverseMap: keyspaceId is nil
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/fb4a9b20e9885a00.
Report an issue: GitHub.