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

[ISSUE] Handling SIGINT and SIGTERM for Graceful Shutdown in Go Websocket App #893

Closed
1 task done
matterai opened this issue Feb 5, 2024 · 2 comments
Closed
1 task done
Labels

Comments

@matterai
Copy link

matterai commented Feb 5, 2024

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

Hey!

I've got a main.go file where I'm playing around with this code snippet:

ctx, cancelCtx := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer cancelCtx()

service := NewService(ctx)
service.Connect()

Inside the Run() method, I'm setting up a connection with DialContext(s.context, url, nil) and diving into an endless loop that listens for signals. My goal here is to catch SIGINT and SIGTERM so I can neatly wrap up the connection and close the app on a good note. Here's what the service code looks like:

func NewService(context context.Context) *GatewayService {
	return &GatewayService{
		context: context,
	}
}

func (s *Service) Connect() error {
	url := "wss://dial.ws"
	conn, _, err := websocket.DefaultDialer.DialContext(s.context, url, nil)
	if err != nil {
		log.Printf("Error dialing: %v", err)
		return err
	}

	for {
		select {
		case <-s.context.Done():
			log.Println("Context done")
			if err := s.conn.Close(); err != nil {
				log.Printf("Error closing connection: %v", err)
				return err
			}

			return s.context.Err()

		default:
			_, message, err := conn.ReadMessage()
			if err != nil {
				log.Printf("Error reading message: %v", err)
				return err
			}

			log.Println(string(message))
		}
	}
}

When I give it a go and trigger a SIGTERM, the app just keeps on running. It looks like conn.ReadMessage() is holding up the main go-routine while it waits for new bytes from the websocket channel. I might be missing something here, but this seems a bit off to me. Is this how it's supposed to work? What's the best way to deal with termination signals with this library?

Thanks a bunch for your insights!

Expected Behavior

No response

Steps To Reproduce

No response

Anything else?

No response

@matterai matterai added the bug label Feb 5, 2024
@ghost
Copy link

ghost commented Feb 6, 2024

Use two goroutines, one to read the connection and the other to handle signal. Here's an example.

@matterai
Copy link
Author

matterai commented Feb 6, 2024

Yep, I fixed it yesterday already. I made a goroutine for reading messages. The main goroutine is awaiting signals in for-cycle in the end of my Connect func:

	done := make(chan struct{})
	go func() {
		defer close(done)
		for {
			_, message, err := conn.ReadMessage()
			if err != nil {
				s.logger.Printf("Error reading message: %v", err)
				return
			}

			_, err = s.handler.HandleMessage(message)
			if err != nil {
				s.logger.Printf("Error handling message: %v", err)
				return
			}
		}
	}()

	for {
		select {
		case <-s.context.Done():
			s.logger.Println("Conext done, closing connection")
			return s.context.Err()
		case <-done:
			s.logger.Println("Message handler done, closing connection")
			return s.context.Err()
		}
	}

Thank you. Sorry for bothering.

@matterai matterai closed this as completed Feb 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant