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

Shutdowner: Support calling from fx.Invoke #1

Closed
wants to merge 3 commits into from

Commits on Apr 29, 2023

  1. App.Run: Respect Shutdowner ExitCode (uber-go#1075)

    We added support for changing the exit code for a Shutdowner with the
    ExitCode option in uber-go#989, but this was somehow not respected by App.Run.
    
    This changes App.Run to use the same underlying machinery (`Wait()`) to
    decide on the exit code to use.
    
    To test this, we add a new internal/e2e submodule that will hold full,
    end-to-end integration tests.
    These can be full Fx applications that we run tests against.
    This is a submodule so that it can have dependencies that are not
    desirable as direct dependencies of Fx,
    and it's inside the internal/ directory so that it can consume
    Fx-internal packages (like testutil).
    
    The included regression test verifies the behavior described in uber-go#1074.
    An Fx program using App.Run, and shut down with Shutdowner.Shutdown
    and an explicit exit code, does not exit with the requested exit code.
    Failure before the fix:
    
    ```
    % go test
    --- FAIL: TestShutdownExitCode (0.01s)
        writer.go:40: [Fx] PROVIDE  fx.Lifecycle <= go.uber.org/fx.New.func1()
        writer.go:40: [Fx] PROVIDE  fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
        writer.go:40: [Fx] PROVIDE  fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
        writer.go:40: [Fx] INVOKE           go.uber.org/fx/internal/e2e/shutdowner_run_exitcode.main.func1()
        writer.go:40: [Fx] RUNNING
        writer.go:40: [Fx] TERMINATED
        main_test.go:46:
                    Error Trace:    [..]/fx/internal/e2e/shutdowner_run_exitcode/main_test.go:46
                    Error:          An error is expected but got nil.
                    Test:           TestShutdownExitCode
    FAIL
    exit status 1
    FAIL    go.uber.org/fx/internal/e2e/shutdowner_run_exitcode     0.016s
    ```
    
    Resolves uber-go#1074
    
    ---
    
    There's a follow up to this: #1.
    It depends on the e2e test machinery, so I'll make a PR out of it once
    this is merged.
    abhinav authored Apr 29, 2023
    Configuration menu
    Copy the full SHA
    8f5597f View commit details
    Browse the repository at this point in the history
  2. test: Shutdown from fx.Invoke with App.Start

    Adds a test for calling Shutdown from an fx.Invoke.
    This is partially tested in uber-go#1075 already,
    but as reported in uber-go#1074, it doesn't work if Start is used.
    This adds a test for that case as well.
    abhinav committed Apr 29, 2023
    Configuration menu
    Copy the full SHA
    d54a944 View commit details
    Browse the repository at this point in the history
  3. Support Shutdown from fx.Invoke after App.Start

    App.Start nils out the "last" signal recorded by signalReceivers,
    which it otherwise broadcasts to waiters if it was already received.
    This is unnecessary especially because there's a discrepancy in behavior
    of using App.Start vs App.Run when shutting down from fx.Invoke.
    
    Given a program that calls Shutdown from fx.Invoke,
    when we do:
    
        app := fx.New(...)
    
    The shutdowner has already sent the signal, and signalReceivers has
    already recorded it.
    At that point, whether we call App.Start or App.Run changes behavior:
    
    - If we call App.Run, that calls App.Done (or App.Wait after uber-go#1075),
      which gives it back a channel that already has the signal filled in.
      It then calls App.Start, waits on the channel--which returns
      immediately--and then calls App.Stop.
    - If we call App.Start and App.Wait, on the other hand,
      Start will clear the signal recorded in signalReceivers,
      and then App.Wait will build a channel that will block indefinitely
      because Shutdowner.Shutdown will not be called again.
    
    So even though App.Run() and App.Start()+App.Wait() are meant to be
    equivalent, this causes a serious discrepancy in behavior.
    It makes sense to resolve this by supporting Shutdown from Invoke.
    
    Refs uber-go#1074
    abhinav committed Apr 29, 2023
    Configuration menu
    Copy the full SHA
    fd94f3f View commit details
    Browse the repository at this point in the history