Skip to content

THRIFT-5830: THttpTransport can now be used in interleaved async calls - #3930

Closed
birschick-bq wants to merge 1 commit into
apache:masterfrom
birschick-bq:dev/birschick-bq/thrift-5830
Closed

birschick-bq wants to merge 1 commit into
apache:masterfrom
birschick-bq:dev/birschick-bq/thrift-5830

Conversation

@birschick-bq

@birschick-bq birschick-bq commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Handles

This pull request introduces a new per-call transport mechanism for THttpTransport, allowing each logical Thrift call to use a distinct HTTP request/response cycle. This improves resource management, error isolation, and concurrency for HTTP-based Thrift clients. The implementation includes a new interface, a per-call transport class, and comprehensive tests to verify correct behavior and resource disposal.

Per-call transport implementation

  • Added the ITPerCallTransportProvider interface to define a standard method for creating per-call transports. (lib/netstd/Thrift/Transport/ITPerCallTransportProvider.cs)
  • Implemented THttpPerCallTransport, a new internal class that manages request/response streams for a single Thrift call, ensuring proper disposal and isolation from the parent transport. (lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs)
  • Updated THttpTransport to implement ITPerCallTransportProvider and provide the CreatePerCallTransportAsync method, which returns a new THttpPerCallTransport for each call. (lib/netstd/Thrift/Transport/Client/THttpTransport.cs) [1] [2]

Internal refactoring and supporting methods

  • Added an internal SendAsync method to THttpTransport to handle sending the request stream and returning the HTTP response, with robust exception handling and resource cleanup. (lib/netstd/Thrift/Transport/Client/THttpTransport.cs)

Testing

  • Added comprehensive unit tests to THttpTransportTests to verify per-call transport behavior, including resource disposal, error handling, concurrency, and protocol factory integration. (lib/netstd/Tests/Thrift.Tests/Transports/THttpTransportTests.cs)

These changes collectively improve the reliability, testability, and scalability of HTTP-based Thrift transports by supporting per-call isolation and robust resource management.

Co-authored-by: Claude Sonnet 5
Co-authored-by: GPT 5.6 Luna
Co-authored-by: Copilot Autofix powered by AI 175728472+Copilot@users.noreply.github.com


  • Did you create an Apache Jira ticket? (Request account here, not required for trivial changes)
  • If a ticket exists: Does your pull request title follow the pattern "THRIFT-NNNN: describe my issue"?
  • Did you squash your changes to a single commit? (not required, but preferred)
  • Did you do your best to avoid breaking changes? If one was needed, did you label the Jira ticket with "Breaking-Change"?
  • If your change does not involve any code, include [skip ci] anywhere in the commit message to free up build resources.

Copilot AI lite review requested due to automatic review settings September 22, 2026 20:32
@mergeable mergeable Bot added the c# Pull requests that update C# code Pull requests that update .NET code label Sep 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved critical and moderate transport lifecycle, timeout, and shared-state issues remain.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 High severity · 2 Medium severity

Open (3)
What changed in this PR

Adds per-call HTTP transports so concurrent asynchronous Thrift calls use isolated request/response state.

Changes:

  • Adds ITPerCallTransportProvider and THttpPerCallTransport.
  • Refactors HTTP request sending and resource handling.
  • Adds concurrency, cancellation, disposal, and integration tests.
File Summary
lib/​netstd/​Thrift/​Transport/​ITPerCallTransportProvider.cs Defines per-call transport creation; no findings.
lib/​netstd/​Thrift/​Transport/​Client/​THttpTransport.cs Provides per-call transports and shared sending; findings on timeout handling (critical, 1 vote), request/content disposal (moderate, 1 vote), and shared message-size state reset (moderate, 2 votes).
lib/​netstd/​Thrift/​Transport/​Client/​THttpPerCallTransport.cs Manages isolated call resources; finding on stream-acquisition failure state and response disposal (moderate, 2 votes).
lib/​netstd/​Tests/​Thrift.Tests/​Transports/​THttpTransportTests.cs Covers concurrency, cancellation, disposal, and integration behavior; no findings.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Unresolved response lifecycle, timeout, exception-mapping, and request-disposal issues need correction.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 High severity · 1 Medium severity

Open (4)
Resolved since last review (1)

Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated
@Jens-G

Jens-G commented Sep 22, 2026

Copy link
Copy Markdown
Member

Code review

Found 1 issue:

  1. The last commit switches the existing THttpTransport.FlushAsync to HttpCompletionOption.ResponseHeadersRead as well, so ConnectTimeout no longer limits how long reading the response body can take for any THttpTransport client, not only on the new per-call path. This is Copilot's new comment on line 256, and it holds up in a test. I ran a local server that answers 200 with Content-Length: 64, sends 8 bytes of the body and then nothing more, with ConnectTimeout = 1000, on .NET 10:

    • on master, FlushAsync fails with TTransportException (Interrupted) after about 1 s;
    • at 203447d, FlushAsync returns after 27 ms, and the ReadAllAsync after it, with the default token, was still waiting after 8 s. The per-call path behaves the same way.

    With ResponseContentRead in both FlushAsync and SendAsync, both paths fail after about 1 s again. Two of the new tests then need another way to make the read fail, because the body is already buffered: PerCallTransport_ReadFailure_IsDisposedByCallOwner and PerCallTransport_ReadFailureAfterFlush_DisposesOwnedResponse. If the body should stay streamed, the reads need a time limit of their own. The _sent and SendAsync changes in the same commit can stay.

var response = (await _httpClient.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken)).EnsureSuccessStatusCode();

{
response = await httpClient.SendAsync(
message,
HttpCompletionOption.ResponseHeadersRead,
cancellationToken);

Two suggestions, below the bar for an issue but verified:

  • A small formality for when you next push: CONTRIBUTING.md asks for a single commit per issue, with a Client: netstd line below the subject. None of the three commits has the Client: line, and the last two have no THRIFT-5830: prefix. Squashing them into one commit with that line covers both.

thrift/CONTRIBUTING.md

Lines 17 to 27 in 203447d

1. All pull requests should contain a single commit per issue, or we will ask you to squash it.
1. The pull request title must begin with the Jira THRIFT ticket identifier if it has an associated ticket, for example:
THRIFT-9999: an example pull request title
1. Commit messages must follow this pattern for code changes (deviations will not be merged):
THRIFT-9999: [summary of fix, one line if possible]
Client: [language(s) affected, comma separated, for example: "cpp,erl,perl"]

  • The diffhunk:// footnotes in the description look like a generated summary. If an AI tool also helped with the code, CONTRIBUTING.md recommends a Generated-by: or Co-Authored-By: line in the commit and the PR.

thrift/CONTRIBUTING.md

Lines 116 to 119 in 203447d

## AI generated content ##
The [ASF Generative Tooling Guidance](https://www.apache.org/legal/generative-tooling.html) serves as a great summary. It also outlines potential issues that come with generative tools. For the reasons set out in this document, but also to make it absolutely clear to the reviewers, we strongly recommend using either the `Generated-by:` (as recommended by the linked ASF source) or `Co-Authored-By:` clause (widely adopted common practice, e.g. [here](https://issues.apache.org/jira/browse/SPARK-25018) and [here](https://archive.kernel.org/oldwiki/git.wiki.kernel.org/index.php/CommitMessageConventions.html)) in commits and pull requests, if applicable.

And one question: the netstd generator does not use the new interface yet, so a generated client that is shared across calls still sends every call through the request and response buffers of its one THttpTransport, which is the situation THRIFT-5830 describes. adbc-drivers/hiveserver2#99 changes the generated TCLIService.cs by hand, and its description leaves open whether to keep that or to wait for the generator. Is the generator change planned as part of THRIFT-5830, or as a separate ticket?

<< indent() << "await OutputProtocol.WriteMessageEndAsync(" << CANCELLATION_TOKEN_NAME << ");" << '\n'
<< indent() << "await OutputProtocol.Transport.FlushAsync(" << CANCELLATION_TOKEN_NAME << ");" << '\n';

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings September 22, 2026 22:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Moderate issues remain around cancellation handling, response cleanup, exception consistency, and request disposal.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 Medium severity

Open (3)
Resolved since last review (3)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Response resources leak when stream acquisition fails

lib/​netstd/​Thrift/​Transport/​Client/​THttpPerCallTransport.cs:89

If ReadAsStreamAsync (including its cancellation overload) fails after SendAsync has returned, _response and _responseReadTimeout remain live and this method exits without disposing them. That leaks the response/connection on a response-body setup failure, and a retry can overwrite the previous response; wrap stream acquisition in cleanup that disposes and clears both fields before rethrowing.

Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated
Copilot AI review requested due to automatic review settings September 22, 2026 23:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Four moderate findings remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 Medium severity

Open (2)
Resolved since last review (3)

Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs Outdated
Copilot AI review requested due to automatic review settings September 22, 2026 23:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Moderate issues remain in stream positioning, cancellation mapping, and HTTP resource cleanup.

Review effort: Lite
Findings: 2 Medium severity

Open (2)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Map read cancellation to TTransportException.Interrupted

lib/​netstd/​Thrift/​Transport/​Client/​THttpPerCallTransport.cs:125

The response-read timeout is implemented by cancelling readToken, but this method lets the resulting OperationCanceledException escape unchanged. THttpTransport.ReadAsync maps the same condition to TTransportException.Interrupted; without the same catch here, callers get different timeout/cancellation behavior depending on whether the transport was created per call.

@birschick-bq
birschick-bq marked this pull request as draft September 23, 2026 00:41
@birschick-bq
birschick-bq force-pushed the dev/birschick-bq/thrift-5830 branch from d1aaf17 to 2ff30db Compare September 23, 2026 01:05
@Jens-G

Jens-G commented Sep 23, 2026

Copy link
Copy Markdown
Member

Proposed changes to the code generator can be found in this PR: feat(csharp): add proposed change for Thrift library supports of interleaved async calls

Nice, but that does not help much. We don't simply take arbitrary code from someone else's PRs filed elsewhere. We accept code legally contributed to the project (i.e. you are legally entitled to contribute it).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Address the identified lifecycle synchronization, exception consistency, and deterministic resource cleanup issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 Medium severity

Open (3)
Resolved since last review (2)

Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

A close/send race in FlushAsync misclassifies a closed transport as an unknown failure.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity · 1 Low severity

Open (3)
Resolved since last review (2)

Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Parent lifecycle cancellation is not propagated through response-stream acquisition, and child open-state reporting can race with closure.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 2 Medium severity · 1 Low severity

Open (4)
Resolved since last review (1)

Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs Outdated
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

A closed THttpTransport still reports IsOpen as true.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (4)

Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Closed transports can report successful reopening while remaining unusable.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
Resolved since last review (1)

Comment thread lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs
Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs
@birschick-bq
birschick-bq force-pushed the dev/birschick-bq/thrift-5830 branch from a0be66b to 0a4dd67 Compare September 25, 2026 22:00
@birschick-bq
birschick-bq requested a balanced review from Copilot September 25, 2026 22:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The public provider API must document caller disposal ownership to prevent retained transports and HTTP resources.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (2)

Comment thread lib/netstd/Thrift/Transport/ITPerCallTransportProvider.cs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Closing during an active flush can leave the shared output stream writable and mutate an in-flight request.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Comment thread lib/netstd/Thrift/Transport/Client/THttpTransport.cs
Client: netstd

Handles
- [THRIFT-5830: Using THttpTransport using interleaved asynchronous calls may throw exception](https://issues.apache.org/jira/browse/THRIFT-5830)

This pull request introduces a new per-call transport mechanism for `THttpTransport`, allowing each logical Thrift call to use a distinct HTTP request/response cycle. This improves resource management, error isolation, and concurrency for HTTP-based Thrift clients. The implementation includes a new interface, a per-call transport class, and comprehensive tests to verify correct behavior and resource disposal.

* Added the `ITPerCallTransportProvider` interface to define a standard method for creating per-call transports. (`lib/netstd/Thrift/Transport/ITPerCallTransportProvider.cs`)
* Implemented `THttpPerCallTransport`, a new internal class that manages request/response streams for a single Thrift call, ensuring proper disposal and isolation from the parent transport. (`lib/netstd/Thrift/Transport/Client/THttpPerCallTransport.cs`)
* Updated `THttpTransport` to implement `ITPerCallTransportProvider` and provide the `CreatePerCallTransportAsync` method, which returns a new `THttpPerCallTransport` for each call. (`lib/netstd/Thrift/Transport/Client/THttpTransport.cs`)

* Added an internal `SendAsync` method to `THttpTransport` to handle sending the request stream and returning the HTTP response, with robust exception handling and resource cleanup. (`lib/netstd/Thrift/Transport/Client/THttpTransport.cs`)

* Added comprehensive unit tests to `THttpTransportTests` to verify per-call transport behavior, including resource disposal, error handling, concurrency, and protocol factory integration. (`lib/netstd/Tests/Thrift.Tests/Transports/THttpTransportTests.cs`)

These changes collectively improve the reliability, testability, and scalability of HTTP-based Thrift transports by supporting per-call isolation and robust resource management.

Co-authored-by: Claude Sonnet 5
Co-authored-by: GPT-5.6 Luna
Co-authored-by: GPT-6 Luna
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@Jens-G

Jens-G commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

I'm not sure what the end will be of this adventure but it looks interesting. 🍿

Can I make a suggestion? I'd probably go one step back and try an spec-driven approach.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Concurrency-sensitive transport lifecycle and generator changes warrant final human and CI validation.

Review effort: Balanced
Findings: None

Resolved since last review (1)

@birschick-bq

Copy link
Copy Markdown
Contributor Author

I'm not sure what the end will be of this adventure but it looks interesting. 🍿

Can I make a suggestion? I'd probably go one step back and try an spec-driven approach.
@Jens-G

Well, It finally ended!

It is WAY more complex than when I started. It was fixing existing and new issues.

Let me know if the result is usable/maintainable to you or not. If necessary, I'll give it another go.

@Jens-G

Jens-G commented Sep 26, 2026

Copy link
Copy Markdown
Member

Code review

This is a code-level review only. It does not answer the question above about whether the approach is usable and maintainable for the project.

Re-reviewed at 12b9ce621. No blocking issues found. Checked for bugs and CLAUDE.md compliance.

The issue from the last round is fixed. With the same local server (200, Content-Length: 64, 8 body bytes, then silence) and ConnectTimeout = 1000 on .NET 10, the read after FlushAsync now fails with TTransportException (Interrupted) after about 1 s, on the default path and on the per-call path. The single commit with its Client: netstd line and AI trailers, and the generator change, cover the other points from last time.

Three suggestions, below the bar for an issue but verified:

  • The new body-read deadline takes its budget from two places. THttpTransport.FlushAsync reads the _connectTimeout field, while THttpPerCallTransport.FlushAsync reads the ConnectTimeout property, which returns HttpClient.Timeout whenever there is a client. The two differ when the caller passes in an HttpClient and sets its Timeout afterwards: ConnectTimeout then reports the new value, but the field keeps the default of 30000. I tried new THttpTransport(hc, config, uri) followed by hc.Timeout = TimeSpan.FromSeconds(1), against a server that stalls in the body. On master, FlushAsync fails with Interrupted after about 1 s. On this PR the per-call path also fails after about 1 s, but on the default path the read was still waiting after 8 s. Reading ConnectTimeout in THttpTransport.FlushAsync too gives both paths the same limit.

httpClient = _httpClient;
outputStream = _outputStream;
connectTimeout = _connectTimeout;
operationCts = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,

_request.Position = 0;
connectTimeout = _parent.ConnectTimeout;
operationCts = CancellationTokenSource.CreateLinkedTokenSource(

  • WriteAsync takes _outputStream under the lock but writes to it outside the lock, with no catch. If Close() runs on another thread in between, it disposes the stream and the caller gets a raw ObjectDisposedException, where ReadAsync, FlushAsync and SendAsync turn that into TTransportException (NotOpen). The window is narrow, since MemoryStream writes complete synchronously.

outputStream = _outputStream;
}
#if NET5_0_OR_GREATER
await outputStream.WriteAsync(buffer.AsMemory(offset, length), cancellationToken);
#else
await outputStream.WriteAsync(buffer, offset, length, cancellationToken);
#endif

  • In THttpPerCallTransport.FlushAsync, the comment says the late hand-over keeps "a subsequent retry" from leaking the previous response. A second FlushAsync on the same instance already throws, because _sent is set before the send ("Require a fresh per-call transport to retry"), so _response and _responseReadTimeout are always null at that point. The retry clause and the two ?.Dispose() calls could go.

// Only take ownership of the new response/timeout/stream once stream acquisition
// and normalization have succeeded, so a failed FlushAsync leaves no dangling
// resources behind and a subsequent retry does not leak the previous response.
lock (_parent.LifecycleLock)
{
if (_disposed || !_parent.IsLifecycleOpen)
{
throw new TTransportException(TTransportException.ExceptionType.NotOpen,
"The transport has been closed.");
}
_response?.Dispose();
_response = response;
response = null;
_responseReadTimeout?.Dispose();
_responseReadTimeout = responseReadTimeout;

🤖 Generated with Claude Code

@Jens-G

Jens-G commented Sep 26, 2026

Copy link
Copy Markdown
Member

Follow-up to the review above: I have now also run the stalled-body check on the .NET Framework, and there the new read timeout does not work. ConnectTimeout no longer bounds a stalled response body on that runtime, on the default path and on the per-call path.

The probe (200, Content-Length: 64, 8 body bytes, then silence; ConnectTimeout = 1000) ran as a net48 program against the netstandard2.0 build, on .NET Framework 4.8 (4.8.3761) under Wine 10:

  • on master, FlushAsync fails with TTransportException (Interrupted) after about 1 s;
  • at 12b9ce6, FlushAsync returns, and the read then waits until the server closes the connection, 60 s in the probe, before it fails with Unknown. Both paths behave the same.

On that runtime the pending read of the response stream does not react to the token. In the same probe, both paths fail after about 1.5 s again with this change: register the read token to dispose the stream around the read, in the non-NET5_0_OR_GREATER branch of THttpTransport.ReadAsync and in THttpPerCallTransport.ReadAsync, and report the resulting exception as Interrupted when the read token has fired. The normal and slow-header cases stay as they are.

int ret;
#if NET5_0_OR_GREATER
ret = await inputStream.ReadAsync(new Memory<byte>(buffer, offset, length), readToken);
#else
ret = await inputStream.ReadAsync(buffer, offset, length, readToken);
#endif
if (ret == -1)

{
var count = await responseStream.ReadAsync(
buffer,
offset,
length,
readToken);

🤖 Generated with Claude Code

@Jens-G

Jens-G commented Sep 27, 2026

Copy link
Copy Markdown
Member

I'd like to have some documentation or an example of how this is meant to be used. Reading the code, I don't find it intuitive enough.

What would help most:

  1. A section in lib/netstd/README.md. That file is also the README of the NuGet package, so users will look there. From what I can tell from the code, it should answer these questions:

    • Which constructor turns the per-call mode on? new Calculator.Client(httpTransport, new TBinaryProtocol.Factory()) does. new Calculator.Client(new TBinaryProtocol(httpTransport)) keeps the shared buffers, without any warning.
    • Which methods use it? The generated methods such as add() do. If you call send_add() and recv_add() yourself, they use the shared protocols.
    • How do you combine it with TMultiplexedProtocol, given that netstd has no protocol factory for it?
    • What is shared across calls (the HttpClient, its default headers, ConnectTimeout), and what does each call get for itself (the request and response buffers)?
    • Are socket transports affected? As far as I can see, they aren't.

    A short code snippet would go a long way.

  2. /// doc comments on the three new generated constructors. Choosing the constructor is the decision that matters, and people make it in IntelliSense. A sentence or two on each, saying when the per-call path is used, would do.

  3. Optionally, a small example in the netstd tutorial: one client over HTTP, a few calls in flight at once (Task.WhenAll), and one close at the end. Two things to know first: the tutorial's HTTP server does not start on master at the moment (THRIFT-6374, which has a one-line fix), and no CI job builds tutorial/netstd, so it needs a local build. Please don't build on the client's -mc option; it has a problem of its own (THRIFT-6375).

And two questions, because I'd rather not guess the intent:

  • If the THttpTransport is wrapped in a TBufferedTransport or TFramedTransport, the per-call mode is silently off, because the wrapper is not an ITPerCallTransportProvider. Is that intended, or should it throw?
  • What is the five-argument constructor (inputProtocol, outputProtocol, transport, inputProtocolFactory, outputProtocolFactory) for?

🤖 Generated with Claude Code

@Jens-G

Jens-G commented Sep 27, 2026

Copy link
Copy Markdown
Member

Update on item 3: THRIFT-6374 and THRIFT-6375 are merged, so on master the tutorial's HTTP server now starts, and -mc gives each client its own transport.

🤖 Generated with Claude Code

@birschick-bq

Copy link
Copy Markdown
Contributor Author

supplanted by #3978

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c# Pull requests that update C# code Pull requests that update .NET code compiler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants