Голанг не может оправиться от паники mgo.DialWithInfo - PullRequest
0 голосов
/ 10 сентября 2018

Я попытался выполнить модульное тестирование соединения с MongoDB, используя функцию mgo.DialWithInfo (в случае сбоя). 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 голосов
/ 10 сентября 2018

Почему mgo.DialWithInfo возвращает не ошибку, а панику?

Я не знаю. Это может быть случай, что:

  • вы делаете что-то неподдерживаемое, что вызывает панику в библиотечном вызове
  • автор библиотеки решил использовать панику для сообщения о неисправимых ошибках
  • в библиотеке есть внутренняя ошибка, которая вызывает панику во время выполнения при обнаружении неисправимой ситуации (например, разыменование нулевого указателя)

или их комбинация.

Ваш код не выглядит явно испорченным, хотя я не являюсь пользователем библиотеки mgo, и это не значит, что нет проблем с обменом данными с внешними ресурсами, которые невозможно диагностировать, посмотрев на код. Опубликуйте содержание сообщения о панике, которое может помочь нам точно определить, в чем проблема.

Почему не работает восстановление?

Это не ясно из вашего вопроса, но из вашего юнит-теста и этого комментария , похоже, восстановление работает , но не так, как вы предполагали, потому что оно не возвращает ошибка со значением паники.

Вызов recover() в отложенном операторе восстановит управление программой паники, вернет значение, данное при инициировании паники, и возобновит нормальный поток выполнения. Это все, что recover сделает. Это ничего не изменит с ошибками, если вы явно не напишите код для этого. (И, для полноты, без вызова recover где-нибудь в отложенной функции в вашем стеке вызовов, паника в конечном итоге вспыхнет и станет фатальной для вашего приложения.)

В этом контексте работает паника: приложение не завершится смертельно, если паника возникает во время выполнения функции Connect. Однако вы испытываете панику, не предпринимая никаких действий и не сообщая вызывающей стороне о том, что во время метода Connect произошла ошибка.

Чтобы сообщить об этой ошибке, вы должны восстановить панику и выдать ошибку, которая может быть возвращена функцией Connect. Вы не можете вернуть значение в отложенной функции напрямую, но вы можете использовать именованное возвращаемое значение для этого. Пример кода, пропускающий несущественные части тела функции:

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
}
...