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

  1. Increase vtgate's max_memory_rows (and query_server_max_memory_rows) to accommodate the workload.
  2. Rewrite the query so ORDER BY can be pushed down (sort column part of the routing/shard key or covered by an index).
  3. Reduce the result set with tighter WHERE filters or pagination.
  4. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/fb4a9b20e9885a00. Report an issue: GitHub.