From 61c25b49c7c4c21abe667a8f7b06099df2c252dc Mon Sep 17 00:00:00 2001 From: iSuperCoder Date: Thu, 11 May 2023 18:54:15 +0800 Subject: [PATCH 1/3] perf: optimize the performance of XA transactions There is no need to query the database version every time an xa transaction is executed. Just query it once in getOpenConnectorProxy of seataDriver. --- pkg/datasource/sql/connector.go | 55 +------------------------- pkg/datasource/sql/db.go | 11 ++++++ pkg/datasource/sql/driver.go | 44 +++++++++++++++++++-- pkg/rm/tcc/fence/fence_driver_tx.go | 17 ++++++++ pkg/rm/tcc/fence/fennce_driver_test.go | 17 ++++++++ 5 files changed, 87 insertions(+), 57 deletions(-) diff --git a/pkg/datasource/sql/connector.go b/pkg/datasource/sql/connector.go index 189203615..98d6b78e7 100644 --- a/pkg/datasource/sql/connector.go +++ b/pkg/datasource/sql/connector.go @@ -20,14 +20,11 @@ package sql import ( "context" "database/sql/driver" - "errors" - "github.com/go-sql-driver/mysql" - "io" - "reflect" "sync" + "github.com/go-sql-driver/mysql" + "github.com/seata/seata-go/pkg/datasource/sql/types" - "github.com/seata/seata-go/pkg/util/log" ) type seataATConnector struct { @@ -117,16 +114,6 @@ func (c *seataConnector) Connect(ctx context.Context) (driver.Conn, error) { if err != nil { return nil, err } - - // get the version of mysql for xa. - if c.transType == types.XAMode { - version, err := c.dbVersion(ctx, conn) - if err != nil { - return nil, err - } - c.res.SetDbVersion(version) - } - return &Conn{ targetConn: conn, res: c.res, @@ -137,44 +124,6 @@ func (c *seataConnector) Connect(ctx context.Context) (driver.Conn, error) { }, nil } -func (c *seataConnector) dbVersion(ctx context.Context, conn driver.Conn) (string, error) { - queryConn, isQueryContext := conn.(driver.QueryerContext) - if !isQueryContext { - return "", errors.New("get db version error for unexpected driver conn") - } - - res, err := queryConn.QueryContext(ctx, "SELECT VERSION()", nil) - if err != nil { - log.Errorf("seata connector get the xa mysql version err:%v", err) - return "", err - } - - dest := make([]driver.Value, 1) - var version string - - if err = res.Next(dest); err != nil { - if err == io.EOF { - return version, nil - } - return "", err - } - if len(dest) != 1 { - return "", errors.New("get the mysql version is not column 1") - } - - switch reflect.TypeOf(dest[0]).Kind() { - case reflect.Slice, reflect.Array: - val := reflect.ValueOf(dest[0]).Bytes() - version = string(val) - case reflect.String: - version = reflect.ValueOf(dest[0]).String() - default: - return "", errors.New("get the mysql version is not a string") - } - - return version, nil -} - // Driver returns the underlying Driver of the Connector, // mainly to maintain compatibility with the Driver method // on sql.DB. diff --git a/pkg/datasource/sql/db.go b/pkg/datasource/sql/db.go index 6cfaaa771..b3a731564 100644 --- a/pkg/datasource/sql/db.go +++ b/pkg/datasource/sql/db.go @@ -29,6 +29,7 @@ import ( "github.com/seata/seata-go/pkg/datasource/sql/undo" "github.com/seata/seata-go/pkg/datasource/sql/util" "github.com/seata/seata-go/pkg/protocol/branch" + "github.com/seata/seata-go/pkg/util/log" ) type dbOption func(db *DBResource) @@ -123,6 +124,16 @@ func (db *DBResource) GetResourceGroupId() string { } func (db *DBResource) init() { + ctx := context.Background() + conn, err := db.connector.Connect(ctx) + if err != nil { + log.Errorf("connect: %w", err) + } + version, err := selectDBVersion(ctx, conn) + if err != nil { + log.Errorf("select db version: %w", err) + } + db.SetDbVersion(version) db.checkDbVersion() } diff --git a/pkg/datasource/sql/driver.go b/pkg/datasource/sql/driver.go index 6c1e3baa7..1a919abe2 100644 --- a/pkg/datasource/sql/driver.go +++ b/pkg/datasource/sql/driver.go @@ -23,6 +23,8 @@ import ( "database/sql/driver" "errors" "fmt" + "io" + "reflect" "strings" "github.com/go-sql-driver/mysql" @@ -146,20 +148,16 @@ func (d *seataDriver) getOpenConnectorProxy(connector driver.Connector, dbType t withDBName(cfg.DBName), withConnector(connector), } - res, err := newResource(options...) if err != nil { log.Errorf("create new resource: %w", err) return nil, err } - datasource.RegisterTableCache(types.DBTypeMySQL, mysql2.NewTableMetaInstance(db)) - if err = datasource.GetDataSourceManager(d.branchType).RegisterResource(res); err != nil { log.Errorf("regisiter resource: %w", err) return nil, err } - return &seataConnector{ res: res, target: connector, @@ -192,3 +190,41 @@ func parseResourceID(dsn string) string { } return strings.ReplaceAll(res, ",", "|") } + +func selectDBVersion(ctx context.Context, conn driver.Conn) (string, error) { + queryConn, isQueryContext := conn.(driver.QueryerContext) + if !isQueryContext { + return "", errors.New("get db version error for unexpected driver conn") + } + + res, err := queryConn.QueryContext(ctx, "SELECT VERSION()", nil) + if err != nil { + log.Errorf("seata connector get the xa mysql version err:%v", err) + return "", err + } + + dest := make([]driver.Value, 1) + var version string + + if err = res.Next(dest); err != nil { + if err == io.EOF { + return version, nil + } + return "", err + } + if len(dest) != 1 { + return "", errors.New("get the mysql version is not column 1") + } + + switch reflect.TypeOf(dest[0]).Kind() { + case reflect.Slice, reflect.Array: + val := reflect.ValueOf(dest[0]).Bytes() + version = string(val) + case reflect.String: + version = reflect.ValueOf(dest[0]).String() + default: + return "", errors.New("get the mysql version is not a string") + } + + return version, nil +} diff --git a/pkg/rm/tcc/fence/fence_driver_tx.go b/pkg/rm/tcc/fence/fence_driver_tx.go index 3cbe6c6ff..f828c6ac4 100644 --- a/pkg/rm/tcc/fence/fence_driver_tx.go +++ b/pkg/rm/tcc/fence/fence_driver_tx.go @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package fence import ( diff --git a/pkg/rm/tcc/fence/fennce_driver_test.go b/pkg/rm/tcc/fence/fennce_driver_test.go index 36d2d1197..cfaddd2a1 100644 --- a/pkg/rm/tcc/fence/fennce_driver_test.go +++ b/pkg/rm/tcc/fence/fennce_driver_test.go @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package fence import ( From 5251cc31ffe87422b7d8ee8f9df700afaf46d635 Mon Sep 17 00:00:00 2001 From: iSuperCoder Date: Sat, 13 May 2023 16:00:21 +0800 Subject: [PATCH 2/3] refactor: optimize error log --- pkg/datasource/sql/driver.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/datasource/sql/driver.go b/pkg/datasource/sql/driver.go index 1a919abe2..b0c07a100 100644 --- a/pkg/datasource/sql/driver.go +++ b/pkg/datasource/sql/driver.go @@ -199,7 +199,7 @@ func selectDBVersion(ctx context.Context, conn driver.Conn) (string, error) { res, err := queryConn.QueryContext(ctx, "SELECT VERSION()", nil) if err != nil { - log.Errorf("seata connector get the xa mysql version err:%v", err) + log.Errorf("get db version error:%v", err) return "", err } @@ -213,7 +213,7 @@ func selectDBVersion(ctx context.Context, conn driver.Conn) (string, error) { return "", err } if len(dest) != 1 { - return "", errors.New("get the mysql version is not column 1") + return "", errors.New("get db version is not column 1") } switch reflect.TypeOf(dest[0]).Kind() { @@ -223,7 +223,7 @@ func selectDBVersion(ctx context.Context, conn driver.Conn) (string, error) { case reflect.String: version = reflect.ValueOf(dest[0]).String() default: - return "", errors.New("get the mysql version is not a string") + return "", errors.New("get db version is not a string") } return version, nil From 37e97d819405b4af0cd819d2735ba7d1720085a6 Mon Sep 17 00:00:00 2001 From: iSuperCoder Date: Sun, 14 May 2023 10:28:46 +0800 Subject: [PATCH 3/3] refactor: optimize database version query during initialization of DBResource Compatible with two types of driver.Conn that implement either the Queryer or QueryerContext interface. --- pkg/datasource/sql/driver.go | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/pkg/datasource/sql/driver.go b/pkg/datasource/sql/driver.go index b0c07a100..a96a0d84a 100644 --- a/pkg/datasource/sql/driver.go +++ b/pkg/datasource/sql/driver.go @@ -28,10 +28,10 @@ import ( "strings" "github.com/go-sql-driver/mysql" - "github.com/seata/seata-go/pkg/datasource/sql/datasource" mysql2 "github.com/seata/seata-go/pkg/datasource/sql/datasource/mysql" "github.com/seata/seata-go/pkg/datasource/sql/types" + "github.com/seata/seata-go/pkg/datasource/sql/util" "github.com/seata/seata-go/pkg/protocol/branch" "github.com/seata/seata-go/pkg/util/log" ) @@ -192,21 +192,33 @@ func parseResourceID(dsn string) string { } func selectDBVersion(ctx context.Context, conn driver.Conn) (string, error) { - queryConn, isQueryContext := conn.(driver.QueryerContext) - if !isQueryContext { - return "", errors.New("get db version error for unexpected driver conn") - } + var rowsi driver.Rows + var err error - res, err := queryConn.QueryContext(ctx, "SELECT VERSION()", nil) - if err != nil { - log.Errorf("get db version error:%v", err) - return "", err + queryerCtx, ok := conn.(driver.QueryerContext) + var queryer driver.Queryer + if !ok { + queryer, ok = conn.(driver.Queryer) + } + if ok { + rowsi, err = util.CtxDriverQuery(ctx, queryerCtx, queryer, "SELECT VERSION()", nil) + defer func() { + if rowsi != nil { + rowsi.Close() + } + }() + if err != nil { + log.Errorf("ctx driver query: %+v", err) + return "", err + } + } else { + log.Errorf("target conn should been driver.QueryerContext or driver.Queryer") + return "", fmt.Errorf("invalid conn") } dest := make([]driver.Value, 1) var version string - - if err = res.Next(dest); err != nil { + if err = rowsi.Next(dest); err != nil { if err == io.EOF { return version, nil }