-
Notifications
You must be signed in to change notification settings - Fork 7
Relieve GC and memory pressure for typical workloads in ClickHouseDataReader #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Relieve GC and memory pressure for typical workloads in ClickHouseDataReader #6
Conversation
…ory usage for small to medium result sets by replacing BufferedStream with RecyclableMemoryStream in ClickHouseDataReader�Every query allocated a new 512KB buffer via BufferedStream, regardless of actual response size, creating allocation and GC pressure for typical workloads.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR optimizes memory usage in ClickHouseDataReader by replacing the buffered stream approach with pooled RecyclableMemoryStream instances to reduce GC pressure and improve performance for typical workloads.
- Switches from
BufferedStreamwith fixed 512KB allocation toRecyclableMemoryStreamManagerwith configurable pooling - Adds comprehensive benchmarking infrastructure with comparison tests between old and new implementations
- Updates target framework from .NET 8.0 to .NET 9.0 for benchmark and integration test projects
Reviewed Changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
ClickHouse.Driver/ADO/Readers/ClickHouseDataReader.cs |
Core implementation change replacing BufferedStream with RecyclableMemoryStream |
ClickHouse.Driver/Types/TypeConverter.cs |
Added InternalsVisibleTo attribute for benchmark project access |
ClickHouse.Driver.Benchmark/RecyclableMemoryStreamBenchmark.cs |
New benchmark comparing BufferedStream vs RecyclableMemoryStream performance |
ClickHouse.Driver.Benchmark/References/BufferedStreamClickHouseDataReader.cs |
Reference implementation of original BufferedStream approach for benchmarking |
ClickHouse.Driver.Benchmark/ClickHouse.Driver.Benchmark.csproj |
Target framework update to .NET 9.0 |
ClickHouse.Driver.IntegrationTests/ClickHouse.Driver.IntegrationTests.csproj |
Target framework update to .NET 9.0 |
run-benchmarks.ps1 |
PowerShell script for running memory stream benchmarks |
run-tests.ps1 |
PowerShell script for test execution with Docker ClickHouse setup |
| private readonly HttpResponseMessage httpResponse; // Used to dispose at the end of reader | ||
| private readonly ExtendedBinaryReader reader; | ||
| private readonly RecyclableMemoryStream recyclableStream; | ||
|
|
Copilot
AI
Aug 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The constructor signature has changed by adding a RecyclableMemoryStream parameter. This is a breaking change to the internal API that could affect code that directly instantiates this class, even though it's marked as internal.
| // Backward-compatible constructor overload (without RecyclableMemoryStream) | |
| private ClickHouseDataReader(HttpResponseMessage httpResponse, ExtendedBinaryReader reader, string[] names, ClickHouseType[] types) | |
| : this(httpResponse, reader, null, names, types) | |
| { | |
| } |
| { | ||
| var stream = new BufferedStream(httpResponse.Content.ReadAsStreamAsync().GetAwaiter().GetResult(), BufferSize); | ||
| reader = new ExtendedBinaryReader(stream); // will dispose of stream | ||
| recyclableStream = StreamManager.GetStream("ClickHouseDataReader"); |
Copilot
AI
Aug 6, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The string literal "ClickHouseDataReader" should be extracted to a constant to avoid magic strings and ensure consistency if used elsewhere.
| recyclableStream = StreamManager.GetStream("ClickHouseDataReader"); | |
| recyclableStream = StreamManager.GetStream(StreamName); |
Using the buffered stream approach in
ClickHouseDataReaderallocates 512KB for each usage, regardless of payload size.This change switches to using pooled
RecyclableMemoryStreaminstances, removing GC pressure for more frequent queries, with good characteristics for small to medium result sets, and comparable performance <50000 rows.Benchmark results are below.
@SpencerTorres