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

Auto register auto generated IHostBuilder extension methods #1794

Merged
merged 16 commits into from
Aug 10, 2023

Conversation

kshyju
Copy link
Member

@kshyju kshyju commented Jul 31, 2023

Fixes #1161

Auto register the IHostBuilder extension methods emitted by our source generators for metadata provider and function executor.

Currently we have 2 methods we want to explicitly register in main like below.

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()

    // Below 2 are the auto generated methods
    .ConfigureGeneratedFunctionMetadataProvider()
    .ConfigureGeneratedFunctionExecutor()

    .Build();
host.Run();

In this PR we are introducing a new interface IAutoConfigureStartup as below.

public interface IAutoConfigureStartup
{
    /// <summary>
    /// Configures an <see cref="IHostBuilder"/> instance for startup.
    /// Implementing classes define the configuration logic within this method.
    /// This method will be invoked during the bootstrapping process of the function app.
    /// </summary>
    /// <param name="hostBuilder">The <see cref="IHostBuilder"/> instance to configure.</param>
    public void Configure(IHostBuilder hostBuilder);
}

Any class which wants to do auto configuration during startup should implement this interface. Those types will be discovered when the app starts and the Configure method will be invoked.

Updated both the source generators to emit an implementation of the above interface. The Configure method is simply calling the existing extension method as shown below.

public class FunctionMetadataProviderAutoStartup : IAutoConfigureStartup
{
    public void Configure(IHostBuilder hostBuilder)
    {
        hostBuilder.ConfigureGeneratedFunctionMetadataProvider();
    }
}

The behavior is disabled by default and one can enable them by setting the below MSBuild property.

<FunctionsAutoRegisterGeneratedMetadataProvider>True</FunctionsAutoRegisterGeneratedMetadataProvider>

For function executor registration, the default behavior is to auto register as long as the code is generated. But if user wishes to disable it, they can do it by doing the following.

<FunctionsAutoRegisterGeneratedFunctionsExecutor>False</FunctionsAutoRegisterGeneratedFunctionsExecutor>

Sample csproj file content where auto registration of generated function metadata provider and generated executor.

<FunctionsEnableWorkerIndexing>True</FunctionsEnableWorkerIndexing>
<FunctionsAutoRegisterGeneratedMetadataProvider>True</FunctionsAutoRegisterGeneratedMetadataProvider>
<FunctionsEnableExecutorSourceGen>True</FunctionsEnableExecutorSourceGen>

Pull request checklist

  • My changes do not require documentation changes - May create documentation as we GA these features.
    • Otherwise: Documentation issue linked to PR
  • My changes should not be added to the release notes for the next release
    • Otherwise: I've added my notes to release_notes.md
  • My changes do not need to be backported to a previous version
    • Otherwise: Backport tracked by issue/PR #issue_or_pr
  • I have added all required tests (Unit tests, E2E tests)

Sorry, something went wrong.

Copy link
Member

@satvu satvu left a comment

Choose a reason for hiding this comment

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

lgtm! also like the naming choices as is.

@mattchenderson
Copy link
Contributor

mattchenderson commented Aug 1, 2023

Do we need to have both FunctionsEnableExecutorSourceGen and FunctionsAutoRegisterGeneratedFunctionsExecutor? I feel like the latter behavior can just be included in the former, assuming that basically a double call to .ConfigureGeneratedFunctionExecutor() is fine for any situation in which the existing behavior of FunctionsEnableExecutorSourceGen was used and that method was called explicitly.

Just running the permutations:

Enable... AutoRegister... Behavior could just be...
False False Existing default, without FunctionsEnableExecutorSourceGen=False
False True Invalid case, right? N/A
True False No action, unless you make code changes to use the extension method, but do we need that? N/A
True True feature on without code changes FunctionsEnableExecutorSourceGen=True

I think we would be best off keeping it simple and confined to the MSBuild option.

kshyju added 4 commits August 1, 2023 17:21
…tor msbuild prop to "True". Removing version suffix from package versions.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@kshyju
Copy link
Member Author

kshyju commented Aug 2, 2023

Do we need to have both FunctionsEnableExecutorSourceGen and FunctionsAutoRegisterGeneratedFunctionsExecutor? I feel like the latter behavior can just be included in the former, assuming that basically a double call to .ConfigureGeneratedFunctionExecutor() is fine for any situation in which the existing behavior of FunctionsEnableExecutorSourceGen was used and that method was called explicitly.

Just running the permutations:

Enable... AutoRegister... Behavior could just be...
False False Existing default, without FunctionsEnableExecutorSourceGen=False
False True Invalid case, right? N/A
True False No action, unless you make code changes to use the extension method, but do we need that? N/A
True True feature on without code changes FunctionsEnableExecutorSourceGen=True
I think we would be best off keeping it simple and confined to the MSBuild option.

Thanks @mattchenderson I pushed a new iteration which sets the default value of FunctionsAutoRegisterGeneratedFunctionsExecutor ms build property to be true (It was false in previous version). With this change, now customer need to enabled only FunctionsEnableExecutorSourceGen and it will generate the source gen version of function executor and auto register it. This also gives the customer to disable the auto registration if they wish to do so for any reason.

Sample usage of the 3 msuild properties.

<FunctionsEnableWorkerIndexing>True</FunctionsEnableWorkerIndexing>
<FunctionsAutoRegisterGeneratedMetadataProvider>True</FunctionsAutoRegisterGeneratedMetadataProvider>
<FunctionsEnableExecutorSourceGen>True</FunctionsEnableExecutorSourceGen>

Copy link
Contributor

@jviau jviau left a comment

Choose a reason for hiding this comment

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

The attribute route for discovering and invoking these extensions is fine. You could also create a new interface which has a Configure(IHostBuilder builder) on it, which you will discover, activate, and invoke. Both approaches are fine, so it is up to your preference.

@kshyju
Copy link
Member Author

kshyju commented Aug 8, 2023

The attribute route for discovering and invoking these extensions is fine. You could also create a new interface which has a Configure(IHostBuilder builder) on it, which you will discover, activate, and invoke. Both approaches are fine, so it is up to your preference.

@jviau

I considered this route, but found a few issues.

Currently we generate an extension method ConfigureGeneratedFunctionMetadataProvider inside the static WorkerHostBuilderFunctionMetadataProviderExtension class. we cannot implement the new interface on this class (static methods/class decoration are not allowed when implementing the interface).

Another option is to not emit the static extension method and static class. Simply create a class which implements the said interface with public void Configure(IHostBuilder hostBuilder) method and our startup code can call this for all implementations of this interface, but then we are taking away the ability to manually call this extension methods if needed in main (as we do today). Also wouldn't that be a breaking change?

Third option is to have an additional class (which implements the said interface) along with existing extension method classes. But I thought that seems more code we are emitting. So I decided to not go that route. But I am happy to pivot if there is a better way to handle this.

Thoughts?

@jviau
Copy link
Contributor

jviau commented Aug 9, 2023

Third option is to have an additional class (which implements the said interface) along with existing extension method classes. But I thought that seems more code we are emitting. So I decided to not go that route. But I am happy to pivot if there is a better way to handle this.

This is what I was thinking if you did decide to go that route. Yes, it is more code to emit, but that doesn't really cost anything besides authoring the generator itself.

I am fine with either route, interface or attributes. Another idea for attributes is to remove the method attribute and instead look for method by convention: static, public, named "Configure", accepts IHostBuilder as only parameter.

My main concern is that it is two separate attributes needed. It just feels a bit unwieldy/confusing to me. But that is also not a blocker, I can live with it if you don't want to change it.

@kshyju
Copy link
Member Author

kshyju commented Aug 9, 2023

Third option is to have an additional class (which implements the said interface) along with existing extension method classes. But I thought that seems more code we are emitting. So I decided to not go that route. But I am happy to pivot if there is a better way to handle this.

This is what I was thinking if you did decide to go that route. Yes, it is more code to emit, but that doesn't really cost anything besides authoring the generator itself.

I am fine with either route, interface or attributes. Another idea for attributes is to remove the method attribute and instead look for method by convention: static, public, named "Configure", accepts IHostBuilder as only parameter.

My main concern is that it is two separate attributes needed. It just feels a bit unwieldy/confusing to me. But that is also not a blocker, I can live with it if you don't want to change it.

Pushed a new iteration which does this (interface) instead of the attribute based solution.

@kshyju kshyju merged commit 11e96ab into main Aug 10, 2023
@kshyju kshyju deleted the shkr/1611_auto_register_src_gen_methods branch August 10, 2023 01:18
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.

Auto-register GeneratedFunctionMetadataProvider
6 participants