Skip to content

feat(csharp): implement rented message polling#3250

Open
lukaszzborek wants to merge 4 commits into
masterfrom
feat/dotnet-memory-usage
Open

feat(csharp): implement rented message polling#3250
lukaszzborek wants to merge 4 commits into
masterfrom
feat/dotnet-memory-usage

Conversation

@lukaszzborek
Copy link
Copy Markdown
Contributor

@lukaszzborek lukaszzborek commented May 13, 2026

Rationale

Polling path allocated fresh byte[] per message payload + another per raw user-headers blob. On high-throughput consumers that's dominant allocation source. Goal: cut allocs by reusing pooled buffers, without breaking existing safe API.

What changed

  • Underlying poll now reads server bytes into MemoryPool.Shared.Rent(...) buffer instead of new byte[len]. Whole batch shares one rented buffer; payloads + raw headers are slices into it.
  • New rented polling path: IggyConsumer.ReceiveRentedAsync() yields ReceivedRentedMessage. Slices stay alive until caller disposes. Saves 1–2 allocs per message vs old path.
  • Typed variant: IggyConsumer.ReceiveDeserializedAsync() deserializes whole batch eagerly, returns buffer to pool immediately. Caller disposes nothing.
  • New client method: IIggyClient.PollMessagesRentedAsync(...) → PolledMessagesRental.
  • IDeserializer.Deserialize signature: byte[] → ReadOnlyMemory.
  • Old ReceiveMessagesAsync / MessageResponse API unchanged on surface, but internally also reads into pooled buffer now and materializes byte[] only after mapping — saves 1 allocation per poll (the big receive buffer).

What's next

  • Move encryption/decryption into IggyClient so it stops re-allocating (TODO in IggyConsumer.Rented.cs:158).
  • Add rented / Memory / Span API on send path.
  • Make header mapping lazy after decryption move.
  • Update docs in iggy website repo.

Benchmark in comment

@codecov
Copy link
Copy Markdown

codecov Bot commented May 13, 2026

Codecov Report

❌ Patch coverage is 78.57143% with 123 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.88%. Comparing base (56112a2) to head (858433e).

Files with missing lines Patch % Lines
...n/csharp/Iggy_SDK/Consumers/IggyConsumer.Rented.cs 62.31% 43 Missing and 9 partials ⚠️
...SDK/IggyClient/Implementations/TcpMessageStream.cs 82.73% 9 Missing and 20 partials ⚠️
foreign/csharp/Iggy_SDK/Consumers/IggyConsumer.cs 38.46% 13 Missing and 3 partials ⚠️
...reign/csharp/Iggy_SDK/Contracts/MessageResponse.cs 68.96% 9 Missing ⚠️
...csharp/Iggy_SDK/Consumers/ReceivedRentedMessage.cs 86.66% 3 Missing and 1 partial ⚠️
...oreign/csharp/Iggy_SDK/IggyClient/IIggyConsumer.cs 0.00% 4 Missing ⚠️
foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs 95.55% 1 Missing and 3 partials ⚠️
...reign/csharp/Iggy_SDK/Consumers/IggyConsumerOfT.cs 94.87% 0 Missing and 2 partials ⚠️
...csharp/Iggy_SDK/Contracts/RentedMessageResponse.cs 94.11% 0 Missing and 1 partial ⚠️
.../csharp/Iggy_SDK/Extensions/IggyClientExtension.cs 0.00% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3250      +/-   ##
============================================
+ Coverage     73.85%   73.88%   +0.03%     
  Complexity      943      943              
============================================
  Files          1193     1197       +4     
  Lines        108970   109328     +358     
  Branches      85986    86047      +61     
============================================
+ Hits          80478    80780     +302     
- Misses        25757    25782      +25     
- Partials       2735     2766      +31     
Components Coverage Δ
Rust Core 74.90% <ø> (+<0.01%) ⬆️
Java SDK 60.14% <ø> (ø)
C# SDK 70.29% <78.57%> (+0.85%) ⬆️
Python SDK 81.43% <ø> (ø)
Node SDK 91.44% <ø> (ø)
Go SDK 39.80% <ø> (ø)
Files with missing lines Coverage Δ
...reign/csharp/Iggy_SDK/Consumers/ReceivedMessage.cs 100.00% <100.00%> (+16.66%) ⬆️
.../csharp/Iggy_SDK/Contracts/PolledMessagesRental.cs 100.00% <100.00%> (ø)
...DK/IggyClient/Implementations/HttpMessageStream.cs 72.02% <100.00%> (+0.29%) ⬆️
...csharp/Iggy_SDK/Contracts/RentedMessageResponse.cs 94.11% <94.11%> (ø)
.../csharp/Iggy_SDK/Extensions/IggyClientExtension.cs 0.00% <0.00%> (ø)
...ggy_SDK/JsonConverters/MessageResponseConverter.cs 70.90% <87.50%> (+2.90%) ⬆️
...reign/csharp/Iggy_SDK/Consumers/IggyConsumerOfT.cs 96.49% <94.87%> (+96.49%) ⬆️
...csharp/Iggy_SDK/Consumers/ReceivedRentedMessage.cs 86.66% <86.66%> (ø)
...oreign/csharp/Iggy_SDK/IggyClient/IIggyConsumer.cs 50.00% <0.00%> (-50.00%) ⬇️
foreign/csharp/Iggy_SDK/Mappers/BinaryMapper.cs 91.84% <95.55%> (-0.88%) ⬇️
... and 4 more

... and 12 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@hubcio
Copy link
Copy Markdown
Contributor

hubcio commented May 14, 2026

/ready

@github-actions github-actions Bot added the S-waiting-on-review PR is waiting on a reviewer label May 14, 2026
@lukaszzborek lukaszzborek force-pushed the feat/dotnet-memory-usage branch from a28b2fb to 6cc8c2a Compare May 14, 2026 16:44
@lukaszzborek lukaszzborek force-pushed the feat/dotnet-memory-usage branch from 6cc8c2a to 1405d89 Compare May 14, 2026 16:50
@lukaszzborek lukaszzborek force-pushed the feat/dotnet-memory-usage branch from 1405d89 to 858433e Compare May 14, 2026 16:57
@lukaszzborek
Copy link
Copy Markdown
Contributor Author

Some benchmark:

Method 0.8.0 Mean Current Mean Δ Mean 0.8.0 Gen0 Current Gen0 0.8.0 Gen1 Current Gen1 0.8.0 Gen2 Current Gen2 0.8.0 Allocated Current Allocated Δ Allocated
PollMessagesAsync 22.52 ms 20.26 ms -10.03% 312.5000 187.5000 312.5000 31.2500 312.5000 - 3.91 MB 2.97 MB -24.04%
PollMessagesRentedAsync 22.52 ms* 22.56 ms +0.18% 312.5000* 125.0000 312.5000* - 312.5000* - 3.91 MB* 1.93 MB -50.64%
ConsumerReceiveAsync 24.15 ms 22.57 ms -6.54% 312.5000 187.5000 312.5000 31.2500 312.5000 - 3.97 MB 3.02 MB -23.93%
ConsumerReceiveRentedAsync 24.15 ms* 23.32 ms -3.44% 312.5000* 125.0000 312.5000* - 312.5000* - 3.97 MB* 2 MB -49.62%
TypedConsumerReceiveAsync 24.31 ms 22.57 ms -7.16% 312.5000 125.0000 312.5000 31.2500 312.5000 - 4.02 MB 2 MB -50.25%

* Compared against the 0.8.0 non-rented equivalent (PollMessagesAsync / ConsumerReceiveAsync) as requested.

It was tested with a 1 KB message. Polling messages were performed in batches of 100 until 1,000 processed messages were obtained (deserialising the messages via MessagePack).

@lukaszzborek lukaszzborek requested review from numinnex and spetz May 19, 2026 21:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-review PR is waiting on a reviewer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants