grpc / grpc/grpc-dotnet

Calling a blocking unary rpc from multiple threads

Open
#2,347 14 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C#
Stars
4.5k
Forks
836
Avg merge
6d 3h
Merged PRs (30d)
7

Description

Hello,
I have an older app which I am porting from .Net framework to .Net and grpc. I have created a simple app to demonstrate how it works.
A blocking unary call is invoked from multiple threads. That does't work as it should. The question is, how should I use it correctly?

### What version of gRPC and what language are you using?
2.59.0

### What operating system (Linux, Windows,...) and version?
Windows 10 21H2 build 19044.3086

### What runtime / compiler are you using (e.g. .NET Core SDK version `dotnet --info`)
Host:
Version: 8.0.0
Architecture: x64
Commit: 5535e31a71

.NET SDKs installed:
8.0.100

### What did you do?
Blocking unary call is invoked from multiple threads in client:
**Server service:**
```c#
namespace Server
{
public class RetrierService : Retrier.RetrierBase
{
public override async Task DeliverPackage(Package request, ServerCallContext context)
{
// simulate some work
await Task.Delay(2000);

return new Response() { Message = $"+ {request.Name}" };
}
}
}
```
**Asp.Net core configuration**
```c#
using Server;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();

app.MapGrpcService();
app.Run();
```
**Client**
```c#
using Grpc.Core;
using Grpc.Net.Client;
using Retry;

using var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Retrier.RetrierClient(channel);

object printLock = new();
Action printMessage = (message, color) =>
{
lock (printLock)
{
Console.ForegroundColor = color;
Console.Write(message);
Console.WriteLine();
Console.ResetColor();
}
};

string[] itemList =
{
"Secrets of Silicon Valley",
"The Busy Executive's Database Guide",
"Emotional Security: A New Algorithm",
"Prolonged Data Deprivation: Four Case Studies",
"Cooking with Computers: Surreptitious Balance Sheets",
"Silicon Valley Gastronomic Treats",
"Sushi, Anyone?",
"Fifty Years in Buckingham Palace Kitchens",
"But Is It User Friendly?",
"You Can Combat Computer Stress!",
"Is Anger the Enemy?",
"Life Without Fear",
"The Gourmet Microwave",
"Onions, Leeks, and Garlic: Cooking Secrets of the Mediterranean",
"The Psychology of Computer Cooking",
"Straight Talk About Computers",
"Computer Phobic AND Non-Phobic Individuals: Behavior Variations",
"Net Etiquette"
};

while (true)
{
foreach (var item in itemList)
{
new Thread(() =>
{
printMessage($"taskStarted: - {System.Environment.CurrentManagedThreadId}", ConsoleColor.Yellow);

try
{
string result = result = client.DeliverPackage(new Package { Name = item }, deadline: DateTime.UtcNow.AddSeconds(5)).Message;
printMessage($"Success {result}, {System.Environment.CurrentManagedThreadId}", ConsoleColor.Green);
}
catch (RpcException ex)
{
printMessage($"Rpc Exception : {ex.ToString()} - ThreadID : {System.Environment.CurrentManagedThreadId}", ConsoleColor.Red);
}
catch (Exception ex)
{
printMessage($"{ex.Message} - ThreadID : {System.Environment.CurrentManagedThreadId}", ConsoleColor.Red);
}
}).Start();
}
}

```
**Proto file**
```proto
syntax = "proto3";

package retry;

service Retrier {
rpc DeliverPackage (Package) returns (Response);
}

message Package {
string name = 1;
}

message Response {
string message = 1;
}
```

### What did you expect to see?
Successfully call server and return result to client.

### What did you see instead?
The server is called with dealy or it ends up with an error.

Server side:
```
Microsoft.AspNetCore.Server.Kestrel[32]
Connection id "0HN0BIROR9S9P", Request id "0HN0BIROR9S9P:0000001B": the application completed without reading the entire request body.
fail: Grpc.AspNetCore.Server.ServerCallHandler[6]
Error when executing service method 'DeliverPackage'.
System.Threading.Tasks.TaskCanceledException: The request was aborted
---> Microsoft.AspNetCore.Connections.ConnectionAbortedException: The HTTP/2 stream was reset by the application with error code CANCEL.
at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
at System.IO.Pipelines.Pipe.GetReadAsyncResult()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2MessageBody.ReadAsync(CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.Http2MessageBody.ReadAsync(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder`1.StateMachineBox`1.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
at Grpc.AspNetCore.Server.Internal.PipeExtensions.ReadSingleMessageAsync[T](PipeReader input, HttpContextServerCallContext serverCallContext, Func`2 deserializer)
at Grpc.AspNetCore.Server.Internal.CallHandlers.UnaryServerCallHandler`3.HandleCallAsyncCore(HttpContext httpContext, HttpContextServerCallContext serverCallContext)
at Grpc.AspNetCore.Server.Internal.CallHandlers.ServerCallHandlerBase`3.g__AwaitHandleCall|8_0(HttpContextServerCallContext serverCallContext, Method`2 method, Task handleCall)
```

Client side:
```
Grpc.Core.RpcException: Status(StatusCode=\"DeadlineExceeded\", Detail=\"\")
at Grpc.Net.Client.Internal.HttpClientCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
at Grpc.Core.Interceptors.InterceptingCallInvoker.b__3_0[TRequest,TResponse](TRequest req, ClientInterceptorContext`2 ctx)
at Grpc.Core.ClientBase.ClientBaseConfiguration.ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse](TRequest request, ClientInterceptorContext`2 context, BlockingUnaryCallContinuation`2 continuation)
at Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request)
at Retry.Retrier.RetrierClient.DeliverPackage(Package request, CallOptions options) in C:\\Users\\jsokol\\Desktop\\grpc-dotnet\\examples\\Retrier\\Client\\obj\\Debug\\net8.0\\RetryGrpc.cs:line 102
at Retry.Retrier.RetrierClient.DeliverPackage(Package request, M..."
```

### Anything else we should know about your project / environment?

Contributor guide

Open the contributing guide

Research direction

The report provides inline client and server snippets plus client and server stack traces, but no repository files or tests. Start by reproducing the blocking unary calls from the shown client loop and compare deadline and cancellation behavior with the expected successful response; done means the intended usage and verified behavior are established.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
api, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.