@@ -24,6 +24,10 @@ internal class JsonLinesReader(HttpResponseMessage message, IMemoryArenaSource a
2424internal class JsonLinesReader( HttpResponseMessage message ) : IAsyncEnumerable < OperationResult >
2525#endif
2626{
27+ #if FUSION
28+ private const int MaxSingleSpanRecordLength = 16 * 1024 ;
29+
30+ #endif
2731 private static readonly StreamPipeReaderOptions s_options = new (
2832 pool : MemoryPool < byte > . Shared ,
2933 bufferSize : 4096 ,
@@ -98,9 +102,37 @@ public async IAsyncEnumerator<OperationResult> GetAsyncEnumerator(
98102#if FUSION
99103 private static SourceResultDocument ParseDocument ( IMemoryArena arena , ReadOnlySequence < byte > lineBuffer )
100104 {
101- // Each record is one newline-delimited line. Its content bytes are filled once, directly into
102- // the arena's geometric segments using the same per-record mechanic as the SSE reader, then
103- // parsed in place via ParseFilled so no bytes are copied a second time.
105+ var length = lineBuffer . Length ;
106+
107+ // A record whose length fits within MaxSingleSpanRecordLength is filled once into a single
108+ // exact-length arena chunk and parsed in place as one span. This skips the geometric ramp fill
109+ // and the multi-segment reader it produces. The record length is known from newline framing, so
110+ // this does not depend on a Content-Length header.
111+ //
112+ // The threshold is capped at 16 KB because the request-scoped arena is shared by all concurrent
113+ // subgraph fetches and lives until the response is written. A large exact-length rent that does
114+ // not fit the current page strands the whole remaining page tail for the rest of the request,
115+ // while the geometric ramp's small leading chunks can fill those tails. Small records, which
116+ // cover all realistic traffic, get the single-span win. Large records keep the tail-filling ramp.
117+ if ( length > 0 && length <= MaxSingleSpanRecordLength )
118+ {
119+ var lineLength = ( int ) length ;
120+ var buffer = arena . Rent ( lineLength ) ;
121+ lineBuffer . CopyTo ( buffer . Span ) ;
122+
123+ var segments = arena . RentSegmentTable ( 1 ) ;
124+ segments [ 0 ] = buffer ;
125+
126+ return SourceResultDocument . ParseFilled (
127+ arena ,
128+ segments ,
129+ usedChunks : 1 ,
130+ lastLength : lineLength ) ;
131+ }
132+
133+ // Fallback: larger records (and the degenerate empty line) are filled across the geometric data
134+ // chunk schedule and parsed as a multi-segment sequence, using the same per-record mechanic as
135+ // the SSE reader.
104136 var chunks = arena . RentSegmentTable ( 64 ) ;
105137 var chunkIndex = 0 ;
106138 var chunkSize = SourceResultDocument . GetDataChunkSize ( chunkIndex ) ;
0 commit comments