fx
fx copied to clipboard
Startup order isn't preserved when using modules
Describe the bug I've bundled some commonly used dependencies in modules for convenience, for example db initialisation and web server start up. In one of my web services I implemented them as follows (with the intention that the data migration function runs before the web server starts):
fx.New(
...
service.DatastoreModule, // Configures and connects to Google cloud datastore
fx.Invoke(runMigrations), // Runs data migrations
service.ServerModule, // Configures routing and starts web server
).Run()
But on startup the web server always starts first and the web server's OnStart hook is called first:
[Fx] INVOKE ***/service.startServer() from module "server"
...
[Fx] INVOKE main.runMigrations()
...
[Fx] HOOK OnStart ***/service.startServer.func1() executing (caller: ***/service.startServer)
[Fx] HOOK OnStart ***/service.startServer.func1() called by ***/service.startServer ran successfully in 3.48µs
[Fx] HOOK OnStart ***/service.newDatastoreClient.func1() executing (caller: ***/service.newDatastoreClient)
[country] 2024/02/07 02:36:50.404627 Starting HTTP server on 0.0.0.0:8080
[Fx] HOOK OnStart ***/service.newDatastoreClient.func1() called by ***/service.newDatastoreClient ran successfully in 435.478µs
[Fx] HOOK OnStart main.runMigrations.func1() executing (caller: main.runMigrations)
[Fx] HOOK OnStart main.runMigrations.func1() called by main.runMigrations ran successfully in 1.45µs
However if I wrap the migrations in a module as well:
fx.New(
...
service.DatastoreModule,
migrationsModule,
service.ServerModule,
).Run()
Now it works as expected:
[Fx] INVOKE main.runMigrations() from module "migrations"
...
[Fx] INVOKE github.com/boxes-ltd/service-common/service.startServer() from module "server"
...
[Fx] HOOK OnStart ***/service.newDatastoreClient.func1() executing (caller: ***/service.newDatastoreClient)
[Fx] HOOK OnStart ***/service.newDatastoreClient.func1() called by ***/service.newDatastoreClient ran successfully in 406.037µs
[Fx] HOOK OnStart main.runMigrations.func1() executing (caller: main.runMigrations)
[Fx] HOOK OnStart main.runMigrations.func1() called by main.runMigrations ran successfully in 1.42µs
[Fx] HOOK OnStart ***/service.startServer.func1() executing (caller: ***/service.startServer)
[Fx] HOOK OnStart ***/service.startServer.func1() called by ***/service.startServer ran successfully in 780ns
Since documentation states:
Startup hooks, also referred to as OnStart hooks. These run in the order they were appended.
It seems like this is a bug, I couldn't see anything that states that a module takes precedence.
To Reproduce
Add an invoke function before a module that contains an invoke function.
Expected behavior
I would have expected functions to be called in the order they are defined.
Additional context Add any other context about the problem here.
I wonder if https://github.com/uber-go/fx/issues/918 is related?
Hey @Philio, Invoke run order is specified differently than Hook run order. This is a documented behavior. See fx.Invoke documentation.
Invokes registered in Modules are run before the ones registered at the scope of the parent. Invokes within the same Module are run in the order they were provided.
Based on this and the code you're showing, things seem to be running in the expected order.
As a more general comment, if you want to enforce that certain things are run before others without having to worry about the order in which you list them, specify explicit dependencies between them appropriately. I.e., have the migration functionality produce a result that your Invoke
in ServerModule
depends on.
Closing this now. Feel free to re-open if you are still having trouble!