Add logic to skip duplicate table errors when initializing db (#16270)

* Add logic to skip duplicate table errors when initializing db

* Update store/sqlstore/supplier.go

Co-authored-by: Gabe Jackson <gabe@coffeepowered.co>

* Handle mysql and pg table already exist errors

* Apply PR suggestions
* Fix typo on IsDuplicate
* Update mysql error link to point to the official one

Co-authored-by: Gabe Jackson <gabe@coffeepowered.co>
Этот коммит содержится в:
John Tzikas
2020-11-18 00:01:20 +02:00
коммит произвёл GitHub
родитель 887ef0b5e1
Коммит 5797d6ace3
2 изменённых файлов: 54 добавлений и 8 удалений

Просмотреть файл

@@ -7,7 +7,6 @@ import (
"context"
dbsql "database/sql"
"encoding/json"
"errors"
"fmt"
"os"
"strings"
@@ -15,6 +14,8 @@ import (
"sync/atomic"
"time"
"github.com/pkg/errors"
sq "github.com/Masterminds/squirrel"
"github.com/dyatlov/go-opengraph/opengraph"
"github.com/go-sql-driver/mysql"
@@ -28,10 +29,12 @@ import (
)
const (
INDEX_TYPE_FULL_TEXT = "full_text"
INDEX_TYPE_DEFAULT = "default"
DB_PING_ATTEMPTS = 18
DB_PING_TIMEOUT_SECS = 10
INDEX_TYPE_FULL_TEXT = "full_text"
INDEX_TYPE_DEFAULT = "default"
PG_DUP_TABLE_ERROR_CODE = "42P07" // see https://github.com/lib/pq/blob/master/error.go#L268
MYSQL_DUP_TABLE_ERROR_CODE = uint16(1050) // see https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html#error_er_table_exists_error
DB_PING_ATTEMPTS = 18
DB_PING_TIMEOUT_SECS = 10
)
const (
@@ -176,9 +179,12 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
supplier.stores.productNotices = newSqlProductNoticesStore(supplier)
err := supplier.GetMaster().CreateTablesIfNotExists()
if err != nil {
mlog.Critical("Error creating database tables.", mlog.Err(err))
time.Sleep(time.Second)
os.Exit(EXIT_CREATE_TABLE)
if IsDuplicate(err) {
mlog.Warn("Duplicate key error occurred; assuming table already created and proceeding.", mlog.Err(err))
} else {
mlog.Critical("Error creating database tables.", mlog.Err(err))
os.Exit(EXIT_CREATE_TABLE)
}
}
err = upgradeDatabase(supplier, model.CurrentVersion)
@@ -1319,3 +1325,22 @@ func convertMySQLFullTextColumnsToPostgres(columnNames string) string {
return concatenatedColumnNames
}
// IsDuplicate checks whether an error is a duplicate key error, which comes when processes are competing on creating the same
// tables in the database.
func IsDuplicate(err error) bool {
var pqErr *pq.Error
var mysqlErr *mysql.MySQLError
switch {
case errors.As(errors.Cause(err), &pqErr):
if pqErr.Code == PG_DUP_TABLE_ERROR_CODE {
return true
}
case errors.As(errors.Cause(err), &mysqlErr):
if mysqlErr.Number == MYSQL_DUP_TABLE_ERROR_CODE {
return true
}
}
return false
}

Просмотреть файл

@@ -4,12 +4,16 @@
package sqlstore_test
import (
"fmt"
"regexp"
"sync"
"testing"
"github.com/go-sql-driver/mysql"
"github.com/lib/pq"
"github.com/mattermost/gorp"
_ "github.com/mattn/go-sqlite3"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -314,6 +318,23 @@ func TestGetAllConns(t *testing.T) {
}
}
func TestIsDuplicate(t *testing.T) {
testErrors := map[error]bool{
&pq.Error{Code: "42P06"}: false,
&pq.Error{Code: sqlstore.PG_DUP_TABLE_ERROR_CODE}: true,
&mysql.MySQLError{Number: uint16(1000)}: false,
&mysql.MySQLError{Number: sqlstore.MYSQL_DUP_TABLE_ERROR_CODE}: true,
errors.New("Random error"): false,
}
for err, expected := range testErrors {
t.Run(fmt.Sprintf("Should return %t for %s", expected, err.Error()), func(t *testing.T) {
t.Parallel()
assert.Equal(t, expected, sqlstore.IsDuplicate(err))
})
}
}
func makeSqlSettings(driver string) *model.SqlSettings {
switch driver {
case model.DATABASE_DRIVER_POSTGRES: