mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-09-20 13:30:46 +02:00
Concurrent bucket deletion across multiple filer replicas races on the per-bucket DROP TABLE. The first replica drops the table; the rest hit an undefined-table error (postgres 42P01, mysql 1051) which propagates out of DeleteFolderChildren and panics the filer. On restart the same pending DROP re-runs and the filer crash-loops. Make the drop idempotent. Same defect class in all SQL backends, so fix postgres, postgres2, mysql, mysql2, and sqlite together.
106 lines
3.2 KiB
Go
106 lines
3.2 KiB
Go
package mysql2
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
_ "github.com/go-sql-driver/mysql"
|
|
"github.com/seaweedfs/seaweedfs/weed/filer"
|
|
"github.com/seaweedfs/seaweedfs/weed/filer/abstract_sql"
|
|
"github.com/seaweedfs/seaweedfs/weed/filer/mysql"
|
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
|
)
|
|
|
|
const (
|
|
CONNECTION_URL_PATTERN = "%s:%s@tcp(%s:%d)/%s?collation=utf8mb4_bin"
|
|
)
|
|
|
|
var _ filer.BucketAware = (*MysqlStore2)(nil)
|
|
|
|
func init() {
|
|
filer.Stores = append(filer.Stores, &MysqlStore2{})
|
|
}
|
|
|
|
type MysqlStore2 struct {
|
|
abstract_sql.AbstractSqlStore
|
|
}
|
|
|
|
func (store *MysqlStore2) GetName() string {
|
|
return "mysql2"
|
|
}
|
|
|
|
func (store *MysqlStore2) Initialize(configuration util.Configuration, prefix string) (err error) {
|
|
// Absent key keeps a pooled default; an explicit 0 disables the idle pool.
|
|
configuration.SetDefault(prefix+"connection_max_idle", 2)
|
|
// Default on so minimal configs avoid the duplicate-key roundtrip the
|
|
// inode-index KvPut would otherwise emit on every write.
|
|
configuration.SetDefault(prefix+"enableUpsert", true)
|
|
return store.initialize(
|
|
configuration.GetString(prefix+"createTable"),
|
|
configuration.GetString(prefix+"upsertQuery"),
|
|
configuration.GetBool(prefix+"enableUpsert"),
|
|
configuration.GetString(prefix+"username"),
|
|
configuration.GetString(prefix+"password"),
|
|
configuration.GetString(prefix+"hostname"),
|
|
configuration.GetInt(prefix+"port"),
|
|
configuration.GetString(prefix+"database"),
|
|
configuration.GetInt(prefix+"connection_max_idle"),
|
|
configuration.GetInt(prefix+"connection_max_open"),
|
|
configuration.GetInt(prefix+"connection_max_lifetime_seconds"),
|
|
configuration.GetBool(prefix+"interpolateParams"),
|
|
)
|
|
}
|
|
|
|
func (store *MysqlStore2) initialize(createTable, upsertQuery string, enableUpsert bool, user, password, hostname string, port int, database string, maxIdle, maxOpen,
|
|
maxLifetimeSeconds int, interpolateParams bool) (err error) {
|
|
|
|
store.SupportBucketTable = true
|
|
if !enableUpsert {
|
|
upsertQuery = ""
|
|
} else if upsertQuery == "" {
|
|
upsertQuery = mysql.DefaultUpsertQuery
|
|
}
|
|
gen := &mysql.SqlGenMysql{
|
|
CreateTableSqlTemplate: createTable,
|
|
DropTableSqlTemplate: "DROP TABLE IF EXISTS `%s`",
|
|
UpsertQueryTemplate: upsertQuery,
|
|
}
|
|
store.SqlGenerator = gen
|
|
|
|
sqlUrl := fmt.Sprintf(CONNECTION_URL_PATTERN, user, password, hostname, port, database)
|
|
adaptedSqlUrl := fmt.Sprintf(CONNECTION_URL_PATTERN, user, "<ADAPTED>", hostname, port, database)
|
|
if interpolateParams {
|
|
sqlUrl += "&interpolateParams=true"
|
|
adaptedSqlUrl += "&interpolateParams=true"
|
|
}
|
|
|
|
var dbErr error
|
|
store.DB, dbErr = sql.Open("mysql", sqlUrl)
|
|
if dbErr != nil {
|
|
if store.DB != nil {
|
|
store.DB.Close()
|
|
}
|
|
store.DB = nil
|
|
return fmt.Errorf("can not connect to %s error:%w", adaptedSqlUrl, dbErr)
|
|
}
|
|
|
|
store.DB.SetMaxIdleConns(maxIdle)
|
|
store.DB.SetMaxOpenConns(maxOpen)
|
|
store.DB.SetConnMaxLifetime(time.Duration(maxLifetimeSeconds) * time.Second)
|
|
|
|
if err = store.DB.Ping(); err != nil {
|
|
return fmt.Errorf("connect to %s error:%v", adaptedSqlUrl, err)
|
|
}
|
|
|
|
if err = store.CreateTable(context.Background(), abstract_sql.DEFAULT_TABLE); err != nil && !strings.Contains(err.Error(), "table already exist") {
|
|
return fmt.Errorf("init table %s: %v", abstract_sql.DEFAULT_TABLE, err)
|
|
}
|
|
|
|
mysql.ConfigureListOrdering(store.DB, gen)
|
|
|
|
return nil
|
|
}
|