fx icon indicating copy to clipboard operation
fx copied to clipboard

Example of using fx for a short-lived CLI process?

Open cweagans opened this issue 2 years ago • 6 comments

All of the information/examples that I can find about how to use Fx in the "correct" way are centered around long-lived processes (HTTP servers and the like). I really like how my application is structured with Fx, but I'm not quite sure how to use it for e.g. a CLI application that should be running for < 1 second.

For instance, is there any way to silence the log output entirely? Is there a way to default to not a long-running process?

cweagans avatar Jul 04 '21 19:07 cweagans

As of now, there isn't really a great way. Fx is designed to be more suitable for long-lived processes because it was mainly designed as a DI framework for microservices at Uber, which are, long-lived processes :).

Enabling fx to be more friendly for short-lived processes has been an ongoing discussion but there is no planned timeline on that happening.

sywhang avatar Jul 04 '21 19:07 sywhang

Fx works quite well with short lived processes thanks to very handy Shutdowner interface. Yes, the boilerplate could be leaner, but the approach definitely woks.

oakad avatar Jul 28 '21 11:07 oakad

@oakad can you give us concrete working example?

rhzs avatar Dec 25 '21 04:12 rhzs

package main

import (
	"fmt"
	"time"
	"context"

	"go.uber.org/fx"
)

type Action struct {
	sh fx.Shutdowner
}

func newAction(
	lc fx.Lifecycle,
	sh fx.Shutdowner,
) *Action {
	act := &Action{
		sh: sh,
	}

	lc.Append(fx.Hook{
		OnStart: act.start,
		OnStop:  act.stop,
	})

	return act
}

func (act *Action) start(ctx context.Context) error {
	go act.run()
	return nil
}

func (act *Action) stop(ctx context.Context) error {
	fmt.Println("Stopped")
	return nil
}

func (act *Action) run() {
	time.Sleep(time.Second)
	fmt.Println("Will stop now")
	act.sh.Shutdown()
}

func main() {
	fx.New(
		fx.Provide(
			newAction,
		),
		fx.Invoke(func(*Action) {}),
	).Run()
}

oakad avatar Dec 25 '21 09:12 oakad

package main

import (
	"fmt"
	"time"
	"context"

	"go.uber.org/fx"
)

type Action struct {
	sh fx.Shutdowner
}

func newAction(
	lc fx.Lifecycle,
	sh fx.Shutdowner,
) *Action {
	act := &Action{
		sh: sh,
	}

	lc.Append(fx.Hook{
		OnStart: act.start,
		OnStop:  act.stop,
	})

	return act
}

func (act *Action) start(ctx context.Context) error {
	go act.run()
	return nil
}

func (act *Action) stop(ctx context.Context) error {
	fmt.Println("Stopped")
	return nil
}

func (act *Action) run() {
	time.Sleep(time.Second)
	fmt.Println("Will stop now")
	act.sh.Shutdown()
}

func main() {
	fx.New(
		fx.Provide(
			newAction,
		),
		fx.Invoke(func(*Action) {}),
	).Run()
}

could this be added to the documentation for short-lived applications

anjolaoluwaakindipe avatar Nov 28 '22 13:11 anjolaoluwaakindipe

Here's another workaround for short-lived processes:

package main

import (
	"context"
	"fmt"

	"go.uber.org/fx"
)

func Run(opts ...fx.Option) {
	app := fx.New(opts...)

	if err := app.Start(context.Background()); err != nil {
		fmt.Println("[Fx] START FAILED\t" + err.Error())
		return
	}
	if err := app.Stop(context.Background()); err != nil {
		fmt.Println("[Fx] STOP FAILED\t" + err.Error())
		return
	}

	fmt.Println("[Fx] DONE")
}

func main() {
	Run(
		fx.Invoke(func() {
			fmt.Println("--> Hello world!")
		}),
	)
}

pespantelis avatar Sep 13 '23 00:09 pespantelis