Golang:即使数据库宕机,第二次仍然可以成功进行Ping。

huangapple go评论83阅读模式
英文:

Golang: Ping succeed the second time even if database is down

问题

我遇到了一个有趣的问题,即使数据库在第一次尝试后被关闭,db.Ping()仍然不会返回错误。

以下是源代码:

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

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

type database struct {
	datasource string
	conn       *sql.DB
}

// Connect creates and initialises a Database struct
func (db *database) Connect(server, user, password, DBPort string) error {
	var err error

	db.datasource = fmt.Sprintf("%s:%s@/&", user, password)
	db.conn, err = sql.Open(server, db.datasource)
	if err != nil {
		log.Fatal(err)
	}

	err = db.conn.Ping()
	if err != nil {
		db.conn.Close()
		return err
	}

	log.Println("Waiting for 15 seconds, kill the DB")
	<-time.After(15 * time.Second)

	err = db.conn.Ping()
	if err != nil {
		db.conn.Close()
		return err
	}

	log.Println("Second ping successful")

	return nil
}

起初,数据库是开启的,所以第一次Ping成功。然而,我在那里加了一个延迟,只是为了测试。在那15秒钟内,我停止了数据库(sudo service mysql stop),然而,db.Ping()仍然成功。

如果我执行任何实际的查询(通过db.Querydb.QueryRowdb.Exec),那么sql包将会因为Broken Pipe而发生panic(这是预期的)。

我做错了什么吗?

另外:go版本为go1.7.1 linux/amd64

提前感谢!

英文:

I have run into an interesting issue, namely, that db.Ping() does not return an error even if the database has been killed since the first attempt.

Source code below:

import (
	&quot;database/sql&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;time&quot;

	_ &quot;github.com/go-sql-driver/mysql&quot;
)

type database struct {
	datasource string
	conn       *sql.DB
}

// Connect creates and initialises a Database struct
func (db *database) Connect(server, user, password, DBPort string) error {
	var err error

	db.datasource = fmt.Sprintf(&quot;%s:%s@/&quot;, user, password)
	db.conn, err = sql.Open(server, db.datasource)
	if err != nil {
		log.Fatal(err)
	}

	err = db.conn.Ping()
	if err != nil {
		db.conn.Close()
		return err
	}

	log.Println(&quot;Waiting for 15 seconds, kill the DB&quot;)
	&lt;-time.After(15 * time.Second)

	err = db.conn.Ping()
	if err != nil {
		db.conn.Close()
		return err
	}

	log.Println(&quot;Second ping successful&quot;)

	return nil
}

At first, the database is up, so the first Ping succeeds. However, I put a delay in there just for the sake of testing. In that 15 seconds I stop the database (sudo service mysql stop), however, db.Ping() still succeeds.

If I were to execute any actual query (via db.Query, db.QueryRow or db.Exec), then the sql package would panic with Broken Pipe (which is expected).

Am I doing something wrong?

also: go version go1.7.1 linux/amd64

Thanks in advance!

答案1

得分: 10

在第一次连接之后,Ping实际上并不会“ping数据库”。这很奇怪,也是错误的,但这就是它的工作方式(直到Go 1.8)。如果连接池中存在一个尚未超时的现有连接,Ping将简单地将其从池中移除并返回给你,而不会实际上去检查数据库是否仍然存在。

Kardianos(撰写了上述链接的文档,以及Govendor)在1.8中修复了这个问题,前提是数据库驱动程序支持它。然而,在那之前,Ping不能可靠地确定数据库是否仍然存在。

英文:

Ping, after the first connection, doesn't actually ping the database. It's odd, and wrong, but that's the way it works (until Go 1.8). If there's an existing connection in the connection pool that hasn't timed out, Ping will simply remove it from the pool and return it to you, without actually bothering to check if the database is still there.

Kardianos (who wrote the above linked document, as well as Govendor) fixed this in 1.8, provided the database driver supports it. Until then, however, Ping isn't reliable for determining if the database is still there.

huangapple
  • 本文由 发表于 2017年1月13日 00:35:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/41618428.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定