失敗内容は掲題の通り。監視もしてないのでなかなか気付かなかったが、数時間はサーバーが死んでいた。
グラフ見たらすごいことになっていたのがわかる。再起動後も順調にあがるのでロールバックしたのだった。
で、原因は単純なことなのだけれど、rollaback処理が漏れていた。
バグを含んだコードはこんな感じの存在確認してからinsertするような処理だった。
簡易化したので、不要な処理に見える、unique制約使えというツッコミをしたくなるな…
func (d *db) CreateUser(id, name string) error {
var err error
tx, err := d.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
tx.Rollback()
}
}()
var existID string
err = tx.QueryRow("select id from user where id = ?", id).Scan(&existID)
if existID != "" {
return xerrors.New("The id is already exist")
}
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
return err
}
if _, err = tx.Exec("insert user (id, name) values(?, ?)", id, name); err != nil {
return err
}
tx.Commit()
return nil
}
deferでerrがnilじゃないときはrollbackするようにしていた。
で、存在する場合にreturnすると、rollbackされないバグだった。
errに代入して、返すようにしてバグは解消。
if existID != "" {
err = xerrors.New("The id is already exist")
return err
}
tx.Rollback()をreturn errの前に書いてたのをdeferに移して起きたバグ。
Goでのerror処理周りになかなか慣れないという言い訳。