gobeego

How do I wait for the termination of my goroutines in beego


I have n number of goroutines waiting on a task channel. These goroutines are responsible for performing these tasks. Currently, I'm using beego as my web golang framework. When do I signal termination to my goroutines in a beego application? How do I deduce when a service termination request is received?


Solution

  • As a first step, let's create a channel and bind it to signal which is in your sphere of interest. Then you need to create context and trigger cancelation function when you will receive this signal.

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    ctx, cancel := context.WithCancel(context.Background()) // pass your ctx in all goroutines as first argument, 
    go func() {
        signal := <-c
        logger.Info("signal was received", zap.Stringer("signal", signal)
        cancel()
    }()
    

    Then, you can create WaitGroup and pass your context as first argument in every goroutine

    wg := &sync.WaitGroup{}
    hooks.RunStartHooks(ctx, wg)
    

    Inside your worker listen to context cancelation working appropriately with wg as specified in docs

    for {
    
        select {
    
        case <-ctx.Done():
            wg.Done()
            return
        }
    
        // other cases
    }
    

    And finally,

        timeout := cfg.Server.HooksCloseTimeout // this is from your config 
        if waitTimeout(wg, timeout) {
            logger.Info("timed out waiting for wait group")
        } else {
            logger.Info("server exited properly")
        }
    

    where waitTimeout is

    // waitTimeout waits for the waitgroup for the specified max timeout.
    // Returns true if waiting timed out.
    func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool {
        c := make(chan struct{})
        go func() {
            defer close(c)
            wg.Wait()
        }()
        select {
        case <-c:
            return false // completed normally
        case <-time.After(timeout):
            return true // timed out
        }
    }