uber-go/fx

Add support for hooking after provide stage / Modification of Options on boot

Opened this issue · 3 comments

With this one could add more fx.Options before running the invokes. As example I wrote a small wrapper to inject all configs as struct into the graph. With this Feature one could hook into fx and check if a module is enabled by using these structs and if it is enabled provide more Invokes/Provide options.

This could be similar to the CompilerPasses from Symfony

Repeating part of uber-go/dig#263 (comment) here for when we come back to this feature.


Unfortunately, we do not yet have plans to support a feature like this in Fx.
Late provides make the Fx dependency graph more dynamic.
That is, dependency graph, which was previously static and fully known when fx.New was called,
is now dynamic and arbitrary user-provided code must run before the rest of the graph is visible.

We've wrestled with this idea before, discussing it even during the initial release of Fx.
(In our discussions, we referred to this feature as "dynamic graphs".)

After having a few years of experience running Fx in production,
we're of the opinion that a dynamic graph greatly increases the risk of inscrutable application startup errors.
Today, two of the most common start-up issues we see in our support channels are:

  • dependency not specified
  • attempt to make an RPC request in a constructor (as opposed to a lifecycle hook) caused a time out

These issues will be greatly amplified if we add support for arbitrary function execution before the graph is even fully visible.

We're open to reconsidering this in the future, but it's not the highest priority feature on our list right now. We have work in flight to significantly alter a number of Fx's internals, so we'd like to focus on that before we consider another complex feature.

However, when we do discuss this feature, we'd probably want the design to account for the following:

We'll want to fully understand how lifecycle hooks will interact with the feature. Currently at startup, Fx effectively has these two phases: invoke everything, run all startup hooks. With Late Provides, the invoke everything stage will now do a lot more. We have no means of guarding against RPC calls being made there so we'd want to make a good choice of how those should interact with lifecycle hooks. Should lifecycle hooks be implemented in terms of Late Provides?

Further, we'd want a deliberate focus on error messaging for such an API so that it is obvious to users at startup why their application failed.

@abhinav Thanks for the response. Perfectly valid. fx beauty is definitely in its simplicity.

I do think that this could be supported in a much more limited fashion though and solve my usecase issue.

First pass wouldn't allow for injecting the fx.Lifecycle and would only allow for a constructor. Basically, a way to populate groups dynamically, which is something that is already supported. RPC's in a constructor are a no-no as it is.