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>
Этот коммит содержится в:
@@ -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:
|
||||
|
||||
Ссылка в новой задаче
Block a user