Skip to content

Commit b0dd65d

Browse files
committed
Revert "Add a fallback to filling buffers in DeserializeAsyncEnumerable. (dotnet#104635)"
This reverts commit 7946e6f.
1 parent d41663d commit b0dd65d

File tree

2 files changed

+1
-110
lines changed

2 files changed

+1
-110
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/ReadBufferState.cs

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,6 @@ internal struct ReadBufferState : IDisposable
2020
private bool _isFirstBlock;
2121
private bool _isFinalBlock;
2222

23-
// An "unsuccessful read" in this context refers to a buffer read operation that
24-
// wasn't sufficient to advance the reader to the next token. This occurs primarily
25-
// when consuming large JSON strings (which don't support streaming today) but is
26-
// also possible with other token types such as numbers, booleans, or nulls.
27-
//
28-
// The JsonSerializer.DeserializeAsyncEnumerable methods employ a special buffering
29-
// strategy where rather than attempting to fill the entire buffer, the deserializer
30-
// will be invoked as soon as the first chunk of data is read from the stream.
31-
// This is to ensure liveness: data should be surfaced on the IAE as soon as they
32-
// are streamed from the server. On the other hand, this can create performance
33-
// problems in cases where the underlying stream uses extremely fine-grained buffering.
34-
// For this reason, we employ a threshold that will revert to buffer filling once crossed.
35-
// The counter is reset to zero whenever the JSON reader has been advanced successfully.
36-
//
37-
// The threshold is set to 5 unsuccessful reads. This is a relatively conservative threshold
38-
// but should still make fallback unlikely in most scenaria. It should ensure that fallback
39-
// isn't triggered in null or boolean tokens even in the worst-case scenario where they are
40-
// streamed one byte at a time.
41-
private const int UnsuccessfulReadCountThreshold = 5;
42-
private int _unsuccessfulReadCount;
43-
4423
public ReadBufferState(int initialBufferSize)
4524
{
4625
_buffer = ArrayPool<byte>.Shared.Rent(Math.Max(initialBufferSize, JsonConstants.Utf8Bom.Length));
@@ -67,7 +46,6 @@ public readonly async ValueTask<ReadBufferState> ReadFromStreamAsync(
6746
// make all updates on a copy which is returned once complete.
6847
ReadBufferState bufferState = this;
6948

70-
int minBufferCount = fillBuffer || _unsuccessfulReadCount > UnsuccessfulReadCountThreshold ? bufferState._buffer.Length : 0;
7149
do
7250
{
7351
int bytesRead = await utf8Json.ReadAsync(
@@ -86,7 +64,7 @@ public readonly async ValueTask<ReadBufferState> ReadFromStreamAsync(
8664

8765
bufferState._count += bytesRead;
8866
}
89-
while (bufferState._count < minBufferCount);
67+
while (fillBuffer && bufferState._count < bufferState._buffer.Length);
9068

9169
bufferState.ProcessReadBytes();
9270
return bufferState;
@@ -128,7 +106,6 @@ public void AdvanceBuffer(int bytesConsumed)
128106
{
129107
Debug.Assert(bytesConsumed <= _count);
130108

131-
_unsuccessfulReadCount = bytesConsumed == 0 ? _unsuccessfulReadCount + 1 : 0;
132109
_count -= bytesConsumed;
133110

134111
if (!_isFinalBlock)

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/Stream.DeserializeAsyncEnumerable.cs

Lines changed: 0 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5-
using System.Diagnostics;
6-
using System.Globalization;
75
using System.IO;
86
using System.Linq;
97
using System.Text.Json.Serialization.Metadata;
@@ -335,47 +333,6 @@ await Assert.ThrowsAsync<TaskCanceledException>(async () =>
335333
});
336334
}
337335

338-
[Theory]
339-
[InlineData(5, 1024)]
340-
[InlineData(5, 1024 * 1024)]
341-
public static async Task DeserializeAsyncEnumerable_SlowStreamWithLargeStrings(int totalStrings, int stringLength)
342-
{
343-
var options = new JsonSerializerOptions
344-
{
345-
Converters = { new StringLengthConverter() }
346-
};
347-
348-
using var stream = new SlowStream(GenerateJsonCharacters());
349-
string expectedElement = stringLength.ToString(CultureInfo.InvariantCulture);
350-
IAsyncEnumerable<string?> asyncEnumerable = JsonSerializer.DeserializeAsyncEnumerable<string>(stream, options);
351-
352-
await foreach (string? value in asyncEnumerable)
353-
{
354-
Assert.Equal(expectedElement, value);
355-
}
356-
357-
IEnumerable<byte> GenerateJsonCharacters()
358-
{
359-
// ["xxx...x","xxx...x",...,"xxx...x"]
360-
yield return (byte)'[';
361-
for (int i = 0; i < totalStrings; i++)
362-
{
363-
yield return (byte)'"';
364-
for (int j = 0; j < stringLength; j++)
365-
{
366-
yield return (byte)'x';
367-
}
368-
yield return (byte)'"';
369-
370-
if (i < totalStrings - 1)
371-
{
372-
yield return (byte)',';
373-
}
374-
}
375-
yield return (byte)']';
376-
}
377-
}
378-
379336
public static IEnumerable<object[]> GetAsyncEnumerableSources()
380337
{
381338
yield return WrapArgs(Enumerable.Empty<int>(), 1, DeserializeAsyncEnumerableOverload.JsonSerializerOptions);
@@ -424,48 +381,5 @@ private static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> sourc
424381
}
425382
return list;
426383
}
427-
428-
private sealed class SlowStream(IEnumerable<byte> byteSource) : Stream, IDisposable
429-
{
430-
private readonly IEnumerator<byte> _enumerator = byteSource.GetEnumerator();
431-
private long _position;
432-
433-
public override bool CanRead => true;
434-
public override int Read(byte[] buffer, int offset, int count)
435-
{
436-
Debug.Assert(buffer != null);
437-
Debug.Assert(offset >= 0 && count <= buffer.Length - offset);
438-
439-
if (count == 0 || !_enumerator.MoveNext())
440-
{
441-
return 0;
442-
}
443-
444-
_position++;
445-
buffer[offset] = _enumerator.Current;
446-
return 1;
447-
}
448-
449-
public override bool CanSeek => false;
450-
public override bool CanWrite => false;
451-
public override long Position { get => _position; set => throw new NotSupportedException(); }
452-
public override long Length => throw new NotSupportedException();
453-
public override void Flush() => throw new NotSupportedException();
454-
public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
455-
public override void SetLength(long value) => throw new NotSupportedException();
456-
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
457-
void IDisposable.Dispose() => _enumerator.Dispose();
458-
}
459-
460-
private sealed class StringLengthConverter : JsonConverter<string>
461-
{
462-
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
463-
{
464-
Debug.Assert(!reader.ValueIsEscaped && !reader.HasValueSequence);
465-
return reader.ValueSpan.Length.ToString(CultureInfo.InvariantCulture);
466-
}
467-
468-
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => throw new NotImplementedException();
469-
}
470384
}
471385
}

0 commit comments

Comments
 (0)