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

Incorrect version of Grpc is being used (package version ignored) #4527

Open
SamuelDebruyn opened this issue Jun 4, 2019 · 7 comments

Comments

Projects
None yet
4 participants
@SamuelDebruyn
Copy link

commented Jun 4, 2019

I am using the NuGet package Google.Cloud.Firestore which relies on some other packages where type forwarding is used. The forwarded types are not found and result in a MissingMethodException.

The issue occurs when debugging locally in VS Code on macOS. I haven't deployed this yet.

The same code works in a .NET Core console app.

A lot more details at the original issue: googleapis/google-cloud-dotnet#3101

Investigative information

  • Timestamp: Monday June 3th
  • Function App version: 2.0

Repro steps

See original issue above

Expected behavior

Type forwarding is working and no MissingMethodException

Actual behavior

MissingMethodException when this calls into a forwarded type.

Known workarounds

?

Related information

  • Programming language used: C#
  • Links to source: email me for this (sam@debruyn.dev)
  • Bindings used: QueueTrigger

Csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AzureFunctionsVersion>v2</AzureFunctionsVersion>
    <RootNamespace>myappname</RootNamespace>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="3.0.4"/>
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24"/>
    <PackageReference Include="PuppeteerSharp" Version="1.17.1"/>
    <PackageReference Include="Google.Cloud.Firestore" Version="1.0.0-beta20"/>
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="google.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

@msftbot msftbot bot added the Needs: Triage 🔍 label Jun 4, 2019

@jskeet

This comment has been minimized.

Copy link

commented Jun 7, 2019

Note: the same issue occurs using the emulator from Windows and VS2019.

@jskeet

This comment has been minimized.

Copy link

commented Jun 7, 2019

I have a simpler repro now - there's no need for the Firestore dependency.

Project file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AzureFunctionsVersion>v2</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Grpc.Auth" Version="1.21.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

Function source:

using Google.Apis.Auth.OAuth2;
using Grpc.Auth;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

namespace FunctionApp1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            var tokenAccess = new DummyTokenAccess();
            // This succeeds
            tokenAccess.ToCallCredentials();
            // This fails
            tokenAccess.ToChannelCredentials();

            return new OkObjectResult("Passed");
        }
    }

    class DummyTokenAccess : ITokenAccess
    {
        public Task<string> GetAccessTokenForRequestAsync(string authUri = null, CancellationToken cancellationToken = default)
        {
            throw new System.NotImplementedException();
        }
    }
}

Exception:

System.MissingMethodException
  HResult=0x80131513
  Message=Method not found: 'Grpc.Core.ChannelCredentials Grpc.Core.ChannelCredentials.Create(Grpc.Core.ChannelCredentials, Grpc.Core.CallCredentials)'.
  Source=Grpc.Auth
  StackTrace:
   at Grpc.Auth.GoogleGrpcCredentials.ToChannelCredentials(ITokenAccess googleCredential)
   at FunctionApp1.Function1.Run(HttpRequest req, ILogger log) in C:\Users\skeet\Test\Projects\Issues\google-cloud-dotnet\Issue3101\Issue3101\FunctionApp1\Function1.cs:line 26

This is very odd indeed:

  • There are no different Grpc versions involved. We're compiling against Grpc.Auth 1.21.0 which was compiled against Grpc.Core 1.21.0, and that's the version present in both places.
  • Looking at the IL for the method declaration and the call to it, from the DLLs in the bin directory, they look fine to me:
// Declaration in Grpc.Core, in ChannelCredentials:
.method public hidebysig static class Grpc.Core.ChannelCredentials 
        Create(class Grpc.Core.ChannelCredentials channelCredentials,
               class [Grpc.Core.Api]Grpc.Core.CallCredentials callCredentials) cil managed


// Call in Grpc.Auth, in GoogleGrpcCredentials.ToChannelCredentials:
call       class [Grpc.Core]Grpc.Core.ChannelCredentials [Grpc.Core]Grpc.Core.ChannelCredentials::Create(class [Grpc.Core]Grpc.Core.ChannelCredentials,
                                                                                                                     class [Grpc.Core.Api]Grpc.Core.CallCredentials)

Note that the ToCallCredentials method works. CallCredentials is in Grpc.Core.Api; ChannelCredentials is in Grpc.Core.

I'm going to keep investigating this a bit to see if I can reproduce this without even a Grpc.Auth reference...

@jskeet

This comment has been minimized.

Copy link

commented Jun 7, 2019

Okay, even simpler repro - and an explanation.

First, the project file:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <AzureFunctionsVersion>v2</AzureFunctionsVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Grpc.Core" Version="1.21.0" />
    <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
  </ItemGroup>
  <ItemGroup>
    <None Update="host.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
    <None Update="local.settings.json">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <CopyToPublishDirectory>Never</CopyToPublishDirectory>
    </None>
  </ItemGroup>
</Project>

Next, source code:

using Grpc.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;

namespace FunctionApp1
{
    public static class Function1
    {
        [FunctionName("Function1")]
        public static IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req)
        {
            CreateChannelCredentials(null);
            return new OkObjectResult("Passed");
        }

        private static ChannelCredentials CreateChannelCredentials(CallCredentials callCredentials) =>
            ChannelCredentials.Create(new SslCredentials(), callCredentials);
    }
}

This fails with the same MissingMethodException when you try to execute CreateChannelCredentials, when the JIT compiler works on that method - because it can't find the method.

The problem is that it's not loading Grpc.Core 1.21.0 at all - it's using 1.18.0 which is in C:\Users\skeet\AppData\Local\AzureFunctionsTools\Releases\2.22.0\cli\

If you update the cli directory to contain the relevant files from Grpc.Core and Grpc.Core.Api, and update func.deps.json accordingly, it all works - including Google.Cloud.Firestore.

So basically, this is a problem for any function needing to use a version of Grpc.Core later than the one embedded in the CLI application.

@SamuelDebruyn SamuelDebruyn changed the title Type forwarding seems to be ignored Incorrect version of Grpc is being used (package version ignored) Jun 7, 2019

@AartBluestoke

This comment has been minimized.

Copy link

commented Jul 1, 2019

Behind the scenes the following sequence occurs.
. You compile and include the azure functions sdk, vs finds that it you load 1.21 all the binary dependencies can be satisfied, so complies without error.
. The project starts to run
. Azure functions host starts, and loads 1.18
. Azure functions loads your functions
.net runtime notes that the library is present in the app domain and so can't load another copy.
. At runtime you try to use a 1.21 feature and exception as noted.

This is because the functions runtime in azure loads that specific version of the library , but the announced dependencies are often vague. If you use features from a library newer that what is present things fail in the way described.
The same goes of you assume bugs are fixed.
This happens for other libraries as well, e.g. newtonsoft.json #4049 (comment)
For the json dependency they fixed this by causing the functions sdk to depend on the exact version loaded by the runtime, but this block including in your prophecy any library that includes any other version. Probably a more helpful failure mechanism imo).

@fabiocav fabiocav added this to the Triaged milestone Jul 15, 2019

@fabiocav fabiocav self-assigned this Jul 15, 2019

@fabiocav

This comment has been minimized.

Copy link
Member

commented Jul 15, 2019

Will investigate this as the version packaged with the CLI shouldn't cause this issue, so looks like there's a bug here. Do you happen to know it this is happening outside of the context of the CLI?

@jskeet

This comment has been minimized.

Copy link

commented Jul 15, 2019

@fabiocav: I don't know, I'm afraid. I assume that VS just launches the CLI? How can we test this outside the context of the CLI?

@fabiocav

This comment has been minimized.

Copy link
Member

commented Jul 15, 2019

This was just in case you had tested this on Azure or in one of the container options. Wasn't really expecting that to be the case, but thought I'd ask.

I'll assign this to the next sprint and take a closer look to see what is causing the issue.

@fabiocav fabiocav modified the milestones: Triaged, Functions Sprint 55 Jul 15, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.