#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:

package main

import (
    "database/sql"
    "fmt"
    "os"
    "time"

    "github.com/go-sql-driver/mysql"
)

type discardLogger struct {
}

func (d discardLogger) Print(args ...interface{}) {
}

func main() {

    mysql.SetLogger(discardLogger{})

    host := getenvWithDefault("HOST", "127.0.0.1")
    port := getenvWithDefault("PORT", "3306")
    user := getenvWithDefault("USER", "root")
    passwd := getenvWithDefault("PASSWD", "")
    dbName := getenvWithDefault("DB_NAME", "")

    connectionString := user + ":" + passwd + "@tcp(" + host + ":" + port + ")/" + dbName

    fmt.Println("Waiting for:", user+"@tcp("+host+":"+port+")/"+dbName)

    for {

        fmt.Print(".")

        db, err := sql.Open("mysql", connectionString)
        if err != nil {
            fmt.Println(err.Error())
            return
        }

        err = db.Ping()
        if err == nil {
            fmt.Println()
            break
        }

        time.Sleep(1 * time.Second)
        continue

    }

}

func getenvWithDefault(name string, defaultVal string) string {
    if val := os.Getenv(name); val != "" {
        return val
    }
    return defaultVal
}

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:

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

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