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

Azure Function v2 Autocomplete Not Working #34

Closed
dacris opened this issue Oct 11, 2019 · 15 comments
Closed

Azure Function v2 Autocomplete Not Working #34

dacris opened this issue Oct 11, 2019 · 15 comments
Assignees

Comments

@dacris
Copy link

dacris commented Oct 11, 2019

I have configured my host.json as follows:

{
   "version": "2.0",
   "extensions": {
     "serviceBus": {
       "messageHandlerOptions": {
         "autoComplete": false
       }
     }
   }
}

However, when I run my service bus triggered Azure function, the host log says the following:
[2019-10-11 4:48:27 PM] ServiceBusOptions
[2019-10-11 4:48:27 PM] {
[2019-10-11 4:48:27 PM] "PrefetchCount": 0,
[2019-10-11 4:48:27 PM] "MessageHandlerOptions": {
[2019-10-11 4:48:27 PM] "AutoComplete": true,
[2019-10-11 4:48:27 PM] "MaxAutoRenewDuration": "00:05:00",
[2019-10-11 4:48:27 PM] "MaxConcurrentCalls": 16
[2019-10-11 4:48:27 PM] },
[2019-10-11 4:48:27 PM] "SessionHandlerOptions": {
[2019-10-11 4:48:27 PM] "AutoComplete": true,
[2019-10-11 4:48:27 PM] "MaxAutoRenewDuration": "00:05:00",
[2019-10-11 4:48:27 PM] "MaxConcurrentSessions": 2000,
[2019-10-11 4:48:27 PM] "MessageWaitTimeout": "00:01:00"
[2019-10-11 4:48:27 PM] }
[2019-10-11 4:48:27 PM] }

When I do messageReceiver.DeadLetterAsync in my Azure function, there is an error message afterward saying the lock is invalid (it is trying to complete the message again).

From the log, also these are the versions I am using:
Azure Functions Core Tools (2.7.1704 Commit hash: fbab3b9c1de5ab95e3b4b6a471ead62c4f37e89c)
Function Runtime Version: 2.0.12742.0
[2019-10-11 4:48:16 PM] Building host: startup suppressed:False, configuration suppressed: False
[2019-10-11 4:48:16 PM] Loading startup extension 'Startup'
[2019-10-11 4:48:16 PM] Loaded extension 'Startup' (1.0.0.0)
[2019-10-11 4:48:16 PM] Loading startup extension 'ServiceBus'
[2019-10-11 4:48:16 PM] Loaded extension 'ServiceBus' (3.1.1.0)

I have a Startup.cs also, if it makes a difference.

@dacris
Copy link
Author

dacris commented Oct 11, 2019

This is the message I'm seeing after the function completes (note this is happening after the function has finished executing):
[2019-10-11 6:24:09 PM] Executed 'CreateOrderRetry' (Succeeded, Id=31c77e5a-3364-4a4e-9c14-30d1980f8f67)
[2019-10-11 6:24:09 PM] Message processing error (Action=Complete, ClientId=MessageReceiver2conveycreateorderretry, EntityPath=conveycreateorderretry, Endpoint=mzhernovkovbus.servicebus.windows.net)
[2019-10-11 6:24:09 PM] Microsoft.Azure.ServiceBus: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue, or was received by a different receiver instance.

@dacris
Copy link
Author

dacris commented Oct 11, 2019

Update - I have created a brand new project with a very simple function app and the issue does not occur there. It seems to be something related to my specific project setup.

@dacris
Copy link
Author

dacris commented Oct 11, 2019

Update 2 - I have added a very basic Startup.cs to my very simple project and it fails again.

Here is Startup.cs:

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.ServiceBus;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Indigo.Convey.Integration.Startup))]

namespace Indigo.Convey.Integration
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            var config = new ConfigurationBuilder()
               .AddEnvironmentVariables()
               .Build();

            ConfigureServices(builder.Services, config).BuildServiceProvider(true);
        }

        private IServiceCollection ConfigureServices(IServiceCollection services, IConfigurationRoot root)
        {
            services
                .AddSingleton<IConfiguration>(root);

            return services;
        }
    }
}

I have also added this Nuget package: Microsoft.Azure.Functions.Extensions, v1.0.0

Commenting out Startup.cs makes the problem go away, so it seems to be related to Startup.cs.

@dacris
Copy link
Author

dacris commented Oct 18, 2019

Here is the Azure function I have created for testing:

    public static class Function1
    {
        [FunctionName("Function1")]
        public static void Run([ServiceBusTrigger("conveycreateorderretry", Connection = "ServiceBusConnString")]Message msg, ILogger log, MessageReceiver messageReceiver)
        {
            log.LogInformation("Received message, sending to deadletter...");
            messageReceiver.DeadLetterAsync(msg.SystemProperties.LockToken);
        }
    }

@alrod alrod self-assigned this Oct 18, 2019
@alrod alrod transferred this issue from Azure/Azure-Functions Oct 18, 2019
@ghost ghost added the Needs: triage 🔍 label Oct 18, 2019
@alrod alrod added this to the Functions Sprint 61 milestone Oct 18, 2019
@alrod
Copy link
Member

alrod commented Oct 18, 2019

Function runtime already provided IConfiguration:
https://docs.microsoft.com/bs-latn-ba/azure/azure-functions/functions-dotnet-dependency-injection#function-app-provided-services

After you added new IConfiguration DI starts using last added.

        private IServiceCollection ConfigureServices(IServiceCollection services, IConfigurationRoot root)
        {
            var existingConfigs = services.Where(svc => svc.ServiceType.Name == "IConfiguration").ToList();
            services.AddSingleton<IConfiguration>(root);

            return services;
        }

@alrod alrod closed this as completed Oct 18, 2019
@dacris
Copy link
Author

dacris commented Oct 18, 2019

Thank you for showing me what I was doing wrong. So it turns out this line is not needed:
services.AddSingleton<IConfiguration>(root);

@NoPanicBanick
Copy link

NoPanicBanick commented Mar 9, 2020

public override void Configure(IFunctionsHostBuilder builder)
        {
            var tempServices = builder.Services.BuildServiceProvider();
            var azureFuncConfig = tempServices.GetService<IConfiguration>();
            var configBuilder = new ConfigurationBuilder()
                .AddConfiguration(azureFuncConfig)
                .SetBasePath(Environment.CurrentDirectory)
                .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables();

            var configReader = configBuilder.Build();
            var config = configBuilder.AddAzureKeyVault(new AzureKeyVaultConfigurationOptions()
            {
                Client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(new AzureServiceTokenProvider().KeyVaultTokenCallback)),
                Vault = configReader["keyvaulturl"],
                Manager = new DefaultKeyVaultSecretManager()),
            }).Build();

            builder.Services.AddSingleton<IConfiguration>(config);
        }

If you want to keep your custom configuration, you can pull the IConfiguration from the DI and add it to your custom configuration. Then re-register your new configuration using DI. The autocomplete will be respected and you can keep your custom providers such as key vault.

***Edit: Keeping the above for history and context. For better solutions see the comments later in the thread:
#34 (comment)
#34 (comment)

@A30006565
Copy link

Thanks @Bubba95x! Your suggestion worked like a charm. Now we have both host.json loading fixed and custom configs with keyVault working.

@hybridtechie
Copy link

For anyone checking on adding custom config

You can override the

   public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
        {
        builder.ConfigurationBuilder.AddAzureAppConfiguration( 
        ...
        )
        }

Refer https://docs.microsoft.com/bs-latn-ba/azure/azure-functions/functions-dotnet-dependency-injection#customizing-configuration-sources

@NoPanicBanick
Copy link

NoPanicBanick commented Aug 27, 2021

Yes! @hybridtechie is absolutely correct. We ran across this information when speaking to someone at Azure Support. This is all detailed in the link they provided, but the general rule of thumb is use ConfigureAppConfiguration for app configuration and Configure for registering services. Our start up files these days look something like this:

public static class Startup
{
	public static void Configure(IFunctionsHostBuilder builder)
	{
		var config = builder.GetContext().Configuration; // Can use this to pull the config for services if need be		
		
                // Register your services here
		builder.Services.AddSingleton<IMyServiceProvider, MyServiceProvider>();
		builder.Services.AddSingleton<IYetAnotherService, AnotherService>();
		// Build your provider
		builder.Services.BuildServiceProvider();
	}

	public static IConfigurationRoot ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
	{
		builder.ConfigurationBuilder
			.SetBasePath(Environment.CurrentDirectory)
			.AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
			.AddEnvironmentVariables();
		
		// Build config to hit Key Vault
		var configReader = builder.ConfigurationBuilder.Build();
		var kvUrl = configReader["keyvaulturl"];
		var config = builder.ConfigurationBuilder.AddAzureKeyVault(new AzureKeyVaultConfigurationOptions()
		{
			Client = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(new AzureServiceTokenProvider().KeyVaultTokenCallback)),
			Vault = kvUrl
		}
		).Build();		
		return config;
	}
}

@meyunus
Copy link

meyunus commented Apr 28, 2022

@NoPanicBanick how do you add host.json configuration in this way? Is it also to be added under ConfigureAppConfiguration?

@unruledboy
Copy link

@hybridtechie where does that AddAzureAppConfiguration come from? I've added Microsoft.Extensions.Configuration 3.1.17 but I could not find it.

@unruledboy
Copy link

@NoPanicBanick same question as @meyunus

@NoPanicBanick
Copy link

Checkout this link. That extension method is for registering an Azure Configuration Service in your application. Not to be confused with the App Settings located on your Azure Function's configuration. App Settings should be automatically registered by the Azure Function SDK.

Similarly, the host.json registration should be handled by the Azure Function SDK. You don't have to manually register it. If you want to verify this; build the config in the startup, set a break point, and check the providers in the built config. You should see a provider that contains your host.json configuration.
@unruledboy

@wilsonchenbhs
Copy link

wilsonchenbhs commented Jul 18, 2022

@NoPanicBanick I am using v3 and I finally ended up using a mixed version of your above solution, but via override of ConfigureAppConfiguration. Something like this:

public class Startup: FunctionStartup
{
   public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
   {
      // obtain host.json configs
      var config = builder.ConfigurationBuilder.Build();

      builder.ConfigurationBuilder
        .AddConfiguration(config) // add configs in the host.json
        .AddJson("full-path-to-your-appsettings.json")
        .AddkeyVault(......)  // you can add configs from Azure key Vault
        .AddEnvironmentVariables();
   }
}

And there is NO need to do sth like this builder.Services.AddSingleton<IConfiguration>(config);.

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

No branches or pull requests

8 participants