#development #golang #mysql #pattern

In a lot of instances, you need to have the ability to wait for a MySQL database to startup (e.g. when using in a Docker container). This is something which can easily be done with a simple Go program:

 1package main
 2
 3import (
 4    "database/sql"
 5    "fmt"
 6    "os"
 7    "time"
 8
 9    "github.com/go-sql-driver/mysql"
10)
11
12type discardLogger struct {
13}
14
15func (d discardLogger) Print(args ...interface{}) {
16}
17
18func main() {
19
20    mysql.SetLogger(discardLogger{})
21
22    host := getenvWithDefault("HOST", "127.0.0.1")
23    port := getenvWithDefault("PORT", "3306")
24    user := getenvWithDefault("USER", "root")
25    passwd := getenvWithDefault("PASSWD", "")
26    dbName := getenvWithDefault("DB_NAME", "")
27
28    connectionString := user + ":" + passwd + "@tcp(" + host + ":" + port + ")/" + dbName
29
30    fmt.Println("Waiting for:", user+"@tcp("+host+":"+port+")/"+dbName)
31
32    for {
33
34        fmt.Print(".")
35
36        db, err := sql.Open("mysql", connectionString)
37        if err != nil {
38            fmt.Println(err.Error())
39            return
40        }
41
42        err = db.Ping()
43        if err == nil {
44            fmt.Println()
45            break
46        }
47
48        time.Sleep(1 * time.Second)
49        continue
50
51    }
52
53}
54
55func getenvWithDefault(name string, defaultVal string) string {
56    if val := os.Getenv(name); val != "" {
57        return val
58    }
59    return defaultVal
60}

What we first do is to register a custom logger for the mysql package so that it doesn't output anything. Then we read the environment variables which contain the connection details. Then, we just ping the database every second until it succeeds. If it does, the program exits.

You can run it like this:

1$ HOST=127.0.0.1 DB_NAME=my_database USER=root go run wait_for_db.go
2Waiting for: root@tcp(127.0.0.1:3308)/my_database
3...

It can be easily modified to do the same for any other supported database engine.