feat(grpc): send query results in chunks when the client asks

What does this MR do and why?

Large query results reach GitLab Rails as one gRPC message. Rails keeps several copies of it in the Ruby heap and rejects results over 8 MiB.

With this MR, a client can ask for chunks. The server then sends the result text in ordered pieces, 512 KiB by default, and then the metadata. Clients that do not ask see no change.

Testing

  • Unit tests: chunks split on character boundaries, keep the metadata, and give no chunk for empty text. A client that leaves after the first message still counts as delivered.
  • Server integration test: over gRPC and ClickHouse, the chunks join to the same JSON as one result.
  • End to end on a GDK VM with the Rails MR: llm results are byte-identical, and raw results are the same JSON. An 11 MB result that returned 502 now returns 200.

Performance Analysis

Server memory does not change: the old path also held the text and its encoded copy. The Rails MR has the benchmark.

  • This merge request does not introduce any performance regression. If a performance regression is expected, explain why.
Agent context: protocol details and choices
  • Result text: the rendered result_json or formatted_text of a query.
  • Chunk: a ResultChunk message with a piece of the result text. Chunks split on UTF-8 character boundaries and are never empty.
  • Chunk size: grpc.result_chunk_bytes in app config, 524288 by default.
  • Field numbers: ExecuteQueryRequest.chunked_result = 6 because !2794 (closed) takes field 5. ExecuteQueryMessage.result_chunk = 5.
  • The server renders and redacts the full result before it sends the first chunk. So every query error still arrives before any chunk, and clients can set the HTTP status before they write a byte.
  • Billing and analytics: the query counts as delivered once the first message is in the stream. Before this MR, the single result message went into an empty channel, so a later client disconnect never skipped billing. The new rule keeps that behavior for chunked results.
  • stream_timeout covers the query, not the chunk transfer. The client deadline bounds the transfer.
  • Old clients (Workhorse, Rails before the client MR, orbit-cli, xtask loadtest) do not set the flag. They get one result message, as before.
  • Review: three adversarial review passes. Findings fixed: billing on a mid-stream cancel, single-chunk tests, an empty-text edge case, and a clearer non-chunked path.
Edited by Michael Angelo Rivera

Merge request reports

Loading
Loading