{"record":{"id":"fb4a9b20e9885a00","repo":"vitessio/vitess","slug":"in-memory-row-count-exceeded-allowed-limit-of-d-fb4a9b","errorCode":null,"errorMessage":"in-memory row count exceeded allowed limit of %d","messagePattern":"in-memory row count exceeded allowed limit of (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/vt/vtgate/engine/memory_sort.go","lineNumber":99,"sourceCode":"\tsorter := &evalengine.Sorter{\n\t\tCompare: ms.OrderBy,\n\t\tLimit:   count,\n\t}\n\n\tvar mu sync.Mutex\n\terr = vcursor.StreamExecutePrimitive(ctx, ms.Input, bindVars, wantfields, func(qr *sqltypes.Result) error {\n\t\tmu.Lock()\n\t\tdefer mu.Unlock()\n\t\tif len(qr.Fields) != 0 {\n\t\t\tif err := cb(&sqltypes.Result{Fields: qr.Fields}); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tfor _, row := range qr.Rows {\n\t\t\tsorter.Push(row)\n\t\t}\n\t\tif vcursor.ExceedsMaxMemoryRows(sorter.Len()) {\n\t\t\treturn fmt.Errorf(\"in-memory row count exceeded allowed limit of %d\", vcursor.MaxMemoryRows())\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn cb(&sqltypes.Result{Rows: sorter.Sorted()})\n}\n\n// GetFields satisfies the Primitive interface.\nfunc (ms *MemorySort) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) {\n\treturn ms.Input.GetFields(ctx, vcursor, bindVars)\n}\n\n// Inputs returns the input to memory sort\nfunc (ms *MemorySort) Inputs() ([]Primitive, []map[string]any) {\n\treturn []Primitive{ms.Input}, nil\n}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/vitessio/vitess/blob/01a25a7d176f94613b8d59d799f438380a8760e4/go/vt/vtgate/engine/memory_sort.go#L81-L117","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nvtgate --max_memory_rows=100000\n// after\nvtgate --max_memory_rows=10000000 --query_server_max_memory_rows=10000000","handlingStrategy":"try-catch","validationCode":"// before executing, estimate result size\nif len(whereClauseFilters) == 0 && hasOrderBy {\n    log.Warn(\"unbounded ORDER BY query may exceed max_memory_rows\")\n}","typeGuard":null,"tryCatchPattern":"err := vcursor.Execute(ctx, primitive, bindVars)\nif err != nil && strings.Contains(err.Error(), \"in-memory row count exceeded allowed limit\") {\n    // fall back to paginated execution or tune max_memory_rows\n}","preventionTips":["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."],"tags":["vtgate","memory-limit","sort","resource-exhaustion"],"backgroundTag":"memory-limit-exceeded","analyzedSha":"01a25a7d176f94613b8d59d799f438380a8760e4","analyzedAt":"2026-09-01T17:28:30.605Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}