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
Use grpc-dotnet instead of grpc #9149
Conversation
@@ -3,6 +3,7 @@ | |||
using System.Collections.Generic; | |||
using System.Threading.Tasks; | |||
using Grpc.Core; |
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.
This isn't the old package reference still being used via the Tools dep is it?
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.
I think so, i will remove it
Co-authored-by: Fraser Waters <fraser@pulumi.com>
private static GrpcChannel? _engineChannel = null; | ||
private readonly object _engineChannelLock = new object(); |
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.
Why is _engineChannel
static? Should _engineChannelLock
be static too? (Same questions for GrpcMonitor
below).
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.
@justinvp According to the docs, creating a GrpcChannel
is expensive whereas creating the gRPC client that uses the channel is not: hence I was keeping a single instance of the channel to be used across client instances to avoid recreating the channels every time.
As for _engineChannelLock
it doesn't need to be static since it is only used for locking.
The lock however is no longer needed! I refactored the code to use a (concurrent) dictionary to keep track of the channels created by different Engine and Monitor addresses. This dictionary is thread-safe and doesn't need special locking
using Pulumirpc; | ||
|
||
namespace Pulumi | ||
{ | ||
internal class GrpcEngine : IEngine | ||
{ | ||
private readonly Engine.EngineClient _engine; | ||
// Using a static dictionary to keep track of and re-use gRPC channels | ||
// According to the docs, creating GrpcChannels is expensive so we keep track of a bunch of them here |
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.
// According to the docs, creating GrpcChannels is expensive so we keep track of a bunch of them here | |
// According to the docs(https://docs.microsoft.com/en-us/aspnet/core/grpc/performance?view=aspnetcore-6.0#reuse-grpc-channels), creating GrpcChannels is expensive so we keep track of a bunch of them here |
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.
Added links to the docs, thanks!
public GrpcMonitor(string monitor) | ||
{ | ||
// maxRpcMessageSize raises the gRPC Max Message size from `4194304` (4mb) to `419430400` (400mb) | ||
var maxRpcMessageSize = 400 * 1024 * 1024; | ||
var grpcChannelOptions = new List<ChannelOption> { new ChannelOption(ChannelOptions.MaxReceiveMessageLength, maxRpcMessageSize)}; | ||
this._client = new ResourceMonitor.ResourceMonitorClient(new Channel(monitor, ChannelCredentials.Insecure, grpcChannelOptions)); | ||
if (!_monitorChannels.ContainsKey(monitor)) |
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.
Wonder if we should have a "GrpcPool" class to share between GrpcEngine and GrpcMonitor.
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.
i think for this use case it is simple enough to keep it as imo
using Pulumirpc; | ||
|
||
namespace Pulumi | ||
{ | ||
internal class GrpcEngine : IEngine | ||
{ | ||
private readonly Engine.EngineClient _engine; | ||
// Using a static dictionary to keep track of and re-use gRPC channels | ||
// According to the docs (https://docs.microsoft.com/en-us/aspnet/core/grpc/performance?view=aspnetcore-6.0#reuse-grpc-channels), creating GrpcChannels is expensive so we keep track of a bunch of them here | ||
private static ConcurrentDictionary<string, GrpcChannel> _engineChannels = new ConcurrentDictionary<string, GrpcChannel>(); |
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.
Nit: readonly
if (!_engineChannels.ContainsKey(engineAddress)) | ||
{ | ||
// Allow for insecure HTTP/2 transport (only needed for netcoreapp3.x) | ||
// https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-6.0#call-insecure-grpc-services-with-net-core-client | ||
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); | ||
// Inititialize the engine channel once for this address | ||
_engineChannels[engineAddress] = GrpcChannel.ForAddress(new Uri($"http://{engineAddress}"), new GrpcChannelOptions | ||
{ | ||
MaxReceiveMessageSize = maxRpcMessageSize, | ||
MaxSendMessageSize = maxRpcMessageSize, | ||
Credentials = Grpc.Core.ChannelCredentials.Insecure, | ||
}); | ||
} | ||
|
||
this._engine = new Engine.EngineClient(_engineChannels[engineAddress]); |
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.
Should we use GetOrAdd
?
var engineChannel = _engineChannels.GetOrAdd(engineAddress, address =>
{
// maxRpcMessageSize raises the gRPC Max Message size from `4194304` (4mb) to `419430400` (400mb)
var maxRpcMessageSize = 400 * 1024 * 1024;
// Allow for insecure HTTP/2 transport (only needed for netcoreapp3.x)
// https://docs.microsoft.com/en-us/aspnet/core/grpc/troubleshoot?view=aspnetcore-6.0#call-insecure-grpc-services-with-net-core-client
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
// Inititialize the engine channel once for this address
return GrpcChannel.ForAddress(new Uri($"http://{address}"), new GrpcChannelOptions
{
MaxReceiveMessageSize = maxRpcMessageSize,
MaxSendMessageSize = maxRpcMessageSize,
Credentials = Grpc.Core.ChannelCredentials.Insecure,
});
});
_engine = new Engine.EngineClient(engineChannel);
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.
That would be another way of implementing it. GetOrAdd
does exactly the same as the above: checks if it exists and return it or if the key does not exist, adds to the dictionary. Do you want me to update the code with GetOrAdd
instead?
Description
This PR addresses the following issues:
First of all, replaces
Grpc
package which is now in maintenance mode (until 2022 May) withgrpc-dotnet
, specifically using theGrpc.Net.Client
package instead.We are still using the
Grpc.Tools
package as the main way to generate C# types and clients from our proto files. Though updated this one from 2.37.0 to 2.44.0 (latest)Using a local copy of updated
Pulumi
nuget package and a local, updated version ofPulumi.Aws
package, I've managed to successfully deploy resources to AWS usingpulumi up
and a console application also running onnet6.0
target framework on my M1 machine.Checklist