Skip to content
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

Merged
merged 10 commits into from Mar 11, 2022
Merged

Use grpc-dotnet instead of grpc #9149

merged 10 commits into from Mar 11, 2022

Conversation

Zaid-Ajaj
Copy link
Contributor

@Zaid-Ajaj Zaid-Ajaj commented Mar 9, 2022

Description

This PR addresses the following issues:

First of all, replaces Grpc package which is now in maintenance mode (until 2022 May) with grpc-dotnet, specifically using the Grpc.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 of Pulumi.Aws package, I've managed to successfully deploy resources to AWS using pulumi up and a console application also running on net6.0 target framework on my M1 machine.

Checklist

  • I have added tests that prove my fix is effective or that my feature works
  • Yes, there are changes in this PR that warrants bumping the Pulumi Service API version

@Zaid-Ajaj Zaid-Ajaj marked this pull request as ready for review March 9, 2022 10:03
@Zaid-Ajaj Zaid-Ajaj requested a review from Frassle March 9, 2022 10:03
CHANGELOG_PENDING.md Outdated Show resolved Hide resolved
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Grpc.Core;
Copy link
Member

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?

Copy link
Contributor Author

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

Comment on lines 15 to 16
private static GrpcChannel? _engineChannel = null;
private readonly object _engineChannelLock = new object();
Copy link
Member

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).

Copy link
Contributor Author

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
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// 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

Copy link
Contributor Author

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))
Copy link
Member

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.

Copy link
Contributor Author

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

@Zaid-Ajaj Zaid-Ajaj merged commit 2acb9db into master Mar 11, 2022
@pulumi-bot pulumi-bot deleted the feature/use-grpc-dotnet branch March 11, 2022 17:23
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>();
Copy link
Member

Choose a reason for hiding this comment

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

Nit: readonly

Comment on lines +25 to +39
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]);
Copy link
Member

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);

Copy link
Contributor Author

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants