Golang无法从惊慌的mgo.DialWithInfo中恢复

时间:2018-09-10 10:40:13

标签: go mgo

我尝试使用mgo.DialWithInfo函数(在失败的情况下)对与MongoDB的连接进行单元测试。 mgo.DialWithInfo不会返回错误,但会出现恐慌。

我试图添加恢复逻辑以从恐慌中恢复但没有成功。

我的问题是:

  1. 为什么mgo.DialWithInfo不会返回error却会慌乱?
  2. 为什么我的恢复无效?

代码:

功能

func Connect(mongoDBDialInfo *mgo.DialInfo) error {
    log.Infof("connect to MongoDB with %v", mongoDBDialInfo)
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()

    mongoSession, err := mgo.DialWithInfo(mongoDBDialInfo)
    if err != nil {
        log.Errorf("error, cannot connect to MongoDB, %v", err)
        msg := fmt.Sprintf("fail, cannot connect to MongoDB, %v", mongoDBDialInfo)
        sf := "main.main"
        errM := err.Error()
        util.LogError(msg, sf, errM)
        return err
    }

    DBSession = mongoSession
    return err
}

单元测试

func TestConnect_Fail(t *testing.T) {
    cf, err := config.GetConfigInstance()
    if err != nil {
        t.Errorf("expected, cannot connect to MongoDB but found, %v", err)
    }
    mongoDBDialInfo := &mgo.DialInfo{
        Addrs:    []string{cf.Config.MongoDB.Host + "ss"},
        Timeout:  time.Duration(cf.Config.MongoDB.Timeout) * time.Second,
        Database: cf.Config.MongoDB.DBName,
        Username: cf.Config.MongoDB.Username,
        Password: cf.Config.MongoDB.Password,
    }
    err = Connect(mongoDBDialInfo)
    if err == nil {
        t.Errorf("expected, cannot connect to MongoDB but can")
    }
}

1 个答案:

答案 0 :(得分:5)

  

为什么mgo.DialWithInfo不会返回错误而是出现恐慌?

我不知道。可能是这样的:

  • 您正在执行不受支持的操作,这会导致库调用出现紧急情况
  • 图书馆作者选择使用恐慌来传达不可恢复的错误情况
  • 该库有一个内部错误,导致运行时在遇到不可恢复的情况时会感到恐慌(例如,取消引用nil指针)

或其组合。

尽管我不是mgo库的用户,但是您的代码看起来没有明显损坏,但这并不意味着您没有与外部资源进行通信的问题,这些问题无法通过查看代码来诊断。发布紧急消息的内容,这可能有助于我们准确确定问题所在。

  

为什么恢复不起作用?

目前尚不清楚您的问题,但从您的单元测试和this comment来看,恢复正在正常运行,但并非按预期运行,因为它不会返回错误恐慌的价值。

deferred语句中的recover()调用将恢复对紧急恐慌例程的控制,返回在发生紧急情况时给出的值,并恢复正常的执行流程。 recover会做的所有事情。除非您明确编写代码,否则它将不会对错误做任何事情。 (而且,出于完整性考虑,如果没有在调用堆栈的延迟函数中的某个位置调用recover,恐慌最终会冒出来,对您的应用程序是致命的。)

在这种情况下,紧急情况有效:如果在执行Connect函数期间发生紧急情况,应用程序不会致命退出。但是,您吃了恐慌却没有采取任何措施,也没有向呼叫者发出在Connect方法期间发生错误的信号。

要报告此错误,您应该恢复恐慌并产生可以从Connect函数返回的错误。您不能直接在deferred函数中返回值,但是可以使用命名返回值来返回。示例代码,跳过了函数主体的不必要部分:

func Connect(mongoDBDialInfo *mgo.DialInfo) (err error) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)

            // Return error from Connect with panic's value
            err = fmt.Errorf("%v", r)
        }
    }()

    // rest of function body
}