Useful Go Libraries
go-cmd/cmd - Makes it easy to run external commands concurrently and to pipe out to stdout.
go-cmd/cmd pitfalls
setting
cmd.Dir
to a non-existent directory causes an error such asfork/exec /bin/echo: no such file or directory
- The error refers to the working directory though and not the binary being executed.
- It is easy to get confused by this.
go-cmd/cmd race conditions with stopping and logs
see [racecondition/main.go]
Suppose you want to start an asynchronous long running process e.g. runServer()
which uses cmd
to asynchronously run the server. This presents the following challenges
- We want to wait for the server to start before trying to send requests to the server
- The server could fail immediately; e.g. an incorrect command line flag or fail half way through starting the server
- When the client exits we want to terminate the server so that we don’t leave the process running.
- So our code might look like this.
func runServer() *cmd.Cmd{
c := cmd.NewCommandOptions(...)
c.Start()
return c
}
func main() {
cmd := runServer()
defer func() {
if err := cmd.Stop(); err != nil {
log.Error(err, "Error stopping weaviate")
}
}()
sendRequest()
}
The above code has a race condition. runServer
asynchronously starts the process. As a result, sendRequest
might run before cmd.Start
has a chance to actually send the request.
So we need to give the server time to start. There are different ways to do this
- Add a
Sleep
inrunServer
; not great - If the server has a healthCheck we can keep polling until it passes or timeOut