github.com/wanlay/gorm-dm8@v1.0.5/dmr/zx.go (about)

     1  /*
     2   * Copyright (c) 2000-2018, 达梦数据库有限公司.
     3   * All rights reserved.
     4   */
     5  package dmr
     6  
     7  import (
     8  	"context"
     9  	"database/sql"
    10  	"database/sql/driver"
    11  	"errors"
    12  	"io"
    13  	"regexp"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/wanlay/gorm-dm8/dmr/util"
    18  )
    19  
    20  const (
    21  	SQL_SELECT_STANDBY = "select distinct mailIni.inst_name, mailIni.INST_IP, mailIni.INST_PORT, archIni.arch_status " +
    22  		"from  v$arch_status archIni " +
    23  		"left join (select * from V$DM_MAL_INI) mailIni on archIni.arch_dest = mailIni.inst_name " +
    24  		"left join V$MAL_LINK_STATUS on CTL_LINK_STATUS  = 'CONNECTED' AND DATA_LINK_STATUS = 'CONNECTED' " +
    25  		"where archIni.arch_type in ('TIMELY', 'REALTIME') AND  archIni.arch_status = 'VALID'"
    26  
    27  	SQL_SELECT_STANDBY2 = "select distinct " +
    28  		"mailIni.mal_inst_name, mailIni.mal_INST_HOST, mailIni.mal_INST_PORT, archIni.arch_status " +
    29  		"from v$arch_status archIni " + "left join (select * from V$DM_MAL_INI) mailIni " +
    30  		"on archIni.arch_dest = mailIni.mal_inst_name " + "left join V$MAL_LINK_STATUS " +
    31  		"on CTL_LINK_STATUS  = 'CONNECTED' AND DATA_LINK_STATUS = 'CONNECTED' " +
    32  		"where archIni.arch_type in ('TIMELY', 'REALTIME') AND  archIni.arch_status = 'VALID'"
    33  )
    34  
    35  type rwUtil struct {
    36  }
    37  
    38  var RWUtil = rwUtil{}
    39  
    40  func (RWUtil rwUtil) connect(c *DmConnector, ctx context.Context) (*DmConnection, error) {
    41  	c.loginMode = LOGIN_MODE_PRIMARY_ONLY
    42  	connection, err := c.connect(ctx)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	connection.rwInfo.rwCounter = getRwCounterInstance(connection, connection.StandbyCount)
    48  	err = RWUtil.connectStandby(connection)
    49  
    50  	return connection, err
    51  }
    52  
    53  func (RWUtil rwUtil) reconnect(connection *DmConnection) error {
    54  	if connection.rwInfo == nil {
    55  		return nil
    56  	}
    57  
    58  	RWUtil.removeStandby(connection)
    59  
    60  	err := connection.reconnect()
    61  	if err != nil {
    62  		return err
    63  	}
    64  	connection.rwInfo.cleanup()
    65  	connection.rwInfo.rwCounter = getRwCounterInstance(connection, connection.StandbyCount)
    66  
    67  	err = RWUtil.connectStandby(connection)
    68  
    69  	return err
    70  }
    71  
    72  func (RWUtil rwUtil) recoverStandby(connection *DmConnection) error {
    73  	if connection.closed.IsSet() || RWUtil.isStandbyAlive(connection) {
    74  		return nil
    75  	}
    76  
    77  	ts := time.Now().UnixNano() / 1000000
    78  
    79  	freq := int64(connection.dmConnector.rwStandbyRecoverTime)
    80  	if freq <= 0 || ts-connection.rwInfo.tryRecoverTs < freq {
    81  		return nil
    82  	}
    83  
    84  	err := RWUtil.connectStandby(connection)
    85  	connection.rwInfo.tryRecoverTs = ts
    86  
    87  	return err
    88  }
    89  
    90  func (RWUtil rwUtil) connectStandby(connection *DmConnection) error {
    91  	var err error
    92  	db, err := RWUtil.chooseValidStandby(connection)
    93  	if err != nil {
    94  		return err
    95  	}
    96  	if db == nil {
    97  		return nil
    98  	}
    99  
   100  	standbyConnectorValue := *connection.dmConnector
   101  	standbyConnector := &standbyConnectorValue
   102  	standbyConnector.host = db.host
   103  	standbyConnector.port = db.port
   104  	standbyConnector.rwStandby = true
   105  	standbyConnector.group = nil
   106  	standbyConnector.loginMode = LOGIN_MODE_STANDBY_ONLY
   107  	standbyConnector.switchTimes = 0
   108  	connection.rwInfo.connStandby, err = standbyConnector.connectSingle(context.Background())
   109  	if err != nil {
   110  		return err
   111  	}
   112  
   113  	if connection.rwInfo.connStandby.SvrMode != SERVER_MODE_STANDBY || connection.rwInfo.connStandby.SvrStat != SERVER_STATUS_OPEN {
   114  		RWUtil.removeStandby(connection)
   115  	}
   116  	return nil
   117  }
   118  
   119  func (RWUtil rwUtil) chooseValidStandby(connection *DmConnection) (*ep, error) {
   120  	stmt, rs, err := connection.driverQuery(SQL_SELECT_STANDBY2)
   121  	if err != nil {
   122  		stmt, rs, err = connection.driverQuery(SQL_SELECT_STANDBY)
   123  	}
   124  	defer func() {
   125  		if rs != nil {
   126  			rs.close()
   127  		}
   128  		if stmt != nil {
   129  			stmt.close()
   130  		}
   131  	}()
   132  	if err == nil {
   133  		count := int32(rs.CurrentRows.getRowCount())
   134  		if count > 0 {
   135  			connection.rwInfo.rwCounter = getRwCounterInstance(connection, count)
   136  			i := int32(0)
   137  			rowIndex := connection.rwInfo.rwCounter.random(count)
   138  			dest := make([]driver.Value, 3)
   139  			for err := rs.next(dest); err != io.EOF; err = rs.next(dest) {
   140  				if i == rowIndex {
   141  					ep := newEP(dest[1].(string), dest[2].(int32))
   142  					return ep, nil
   143  				}
   144  				i++
   145  			}
   146  		}
   147  	}
   148  	if err != nil {
   149  		return nil, errors.New("choose valid standby error!" + err.Error())
   150  	}
   151  	return nil, nil
   152  }
   153  
   154  func (RWUtil rwUtil) afterExceptionOnStandby(connection *DmConnection, e error) {
   155  	if e.(*DmError).ErrCode == ECGO_COMMUNITION_ERROR.ErrCode {
   156  		RWUtil.removeStandby(connection)
   157  	}
   158  }
   159  
   160  func (RWUtil rwUtil) removeStandby(connection *DmConnection) {
   161  	if connection.rwInfo.connStandby != nil {
   162  		connection.rwInfo.connStandby.close()
   163  		connection.rwInfo.connStandby = nil
   164  	}
   165  }
   166  
   167  func (RWUtil rwUtil) isCreateStandbyStmt(stmt *DmStatement) bool {
   168  	return stmt != nil && stmt.rwInfo.readOnly && RWUtil.isStandbyAlive(stmt.dmConn)
   169  }
   170  
   171  func (RWUtil rwUtil) executeByConn(conn *DmConnection, query string, execute1 func() (interface{}, error), execute2 func(otherConn *DmConnection) (interface{}, error)) (interface{}, error) {
   172  
   173  	if err := RWUtil.recoverStandby(conn); err != nil {
   174  		return nil, err
   175  	}
   176  	RWUtil.distributeSqlByConn(conn, query)
   177  
   178  	turnToPrimary := false
   179  
   180  	ret, err := execute1()
   181  	if err != nil {
   182  		if conn.rwInfo.connCurrent == conn.rwInfo.connStandby {
   183  
   184  			RWUtil.afterExceptionOnStandby(conn, err)
   185  			turnToPrimary = true
   186  		} else {
   187  
   188  			return nil, err
   189  		}
   190  	}
   191  
   192  	curConn := conn.rwInfo.connCurrent
   193  	var otherConn *DmConnection
   194  	if curConn != conn {
   195  		otherConn = conn
   196  	} else {
   197  		otherConn = conn.rwInfo.connStandby
   198  	}
   199  
   200  	switch curConn.lastExecInfo.retSqlType {
   201  	case Dm_build_691, Dm_build_692, Dm_build_696, Dm_build_703, Dm_build_702, Dm_build_694:
   202  		{
   203  
   204  			if otherConn != nil {
   205  				execute2(otherConn)
   206  			}
   207  		}
   208  	case Dm_build_701:
   209  		{
   210  
   211  			sqlhead := regexp.MustCompile("[ (]").Split(strings.TrimSpace(query), 2)[0]
   212  			if util.StringUtil.EqualsIgnoreCase(sqlhead, "SP_SET_PARA_VALUE") || util.StringUtil.EqualsIgnoreCase(sqlhead, "SP_SET_SESSION_READONLY") {
   213  				if otherConn != nil {
   214  					execute2(otherConn)
   215  				}
   216  			}
   217  		}
   218  	case Dm_build_700:
   219  		{
   220  
   221  			if conn.dmConnector.rwHA && curConn == conn.rwInfo.connStandby &&
   222  				(curConn.lastExecInfo.rsDatas == nil || len(curConn.lastExecInfo.rsDatas) == 0) {
   223  				turnToPrimary = true
   224  			}
   225  		}
   226  	}
   227  
   228  	if turnToPrimary {
   229  		conn.rwInfo.toPrimary()
   230  		conn.rwInfo.connCurrent = conn
   231  
   232  		return execute2(conn)
   233  	}
   234  	return ret, nil
   235  }
   236  
   237  func (RWUtil rwUtil) executeByStmt(stmt *DmStatement, execute1 func() (interface{}, error), execute2 func(otherStmt *DmStatement) (interface{}, error)) (interface{}, error) {
   238  	orgStmt := stmt.rwInfo.stmtCurrent
   239  	query := stmt.nativeSql
   240  
   241  	if err := RWUtil.recoverStandby(stmt.dmConn); err != nil {
   242  		return nil, err
   243  	}
   244  	RWUtil.distributeSqlByStmt(stmt)
   245  	if orgStmt != stmt.rwInfo.stmtCurrent {
   246  		RWUtil.copyStatement(orgStmt, stmt.rwInfo.stmtCurrent)
   247  		stmt.rwInfo.stmtCurrent.nativeSql = orgStmt.nativeSql
   248  	}
   249  
   250  	turnToPrimary := false
   251  
   252  	ret, err := execute1()
   253  	if err != nil {
   254  
   255  		if stmt.rwInfo.stmtCurrent == stmt.rwInfo.stmtStandby {
   256  			RWUtil.afterExceptionOnStandby(stmt.dmConn, err)
   257  			turnToPrimary = true
   258  		} else {
   259  			return nil, err
   260  		}
   261  	}
   262  
   263  	curStmt := stmt.rwInfo.stmtCurrent
   264  	var otherStmt *DmStatement
   265  	if curStmt != stmt {
   266  		otherStmt = stmt
   267  	} else {
   268  		otherStmt = stmt.rwInfo.stmtStandby
   269  	}
   270  
   271  	switch curStmt.execInfo.retSqlType {
   272  	case Dm_build_691, Dm_build_692, Dm_build_696, Dm_build_703, Dm_build_702, Dm_build_694:
   273  		{
   274  
   275  			if otherStmt != nil {
   276  				RWUtil.copyStatement(curStmt, otherStmt)
   277  				execute2(otherStmt)
   278  			}
   279  		}
   280  	case Dm_build_701:
   281  		{
   282  
   283  			var tmpsql string
   284  			if query != "" {
   285  				tmpsql = strings.TrimSpace(query)
   286  			} else if stmt.nativeSql != "" {
   287  				tmpsql = strings.TrimSpace(stmt.nativeSql)
   288  			} else {
   289  				tmpsql = ""
   290  			}
   291  			sqlhead := regexp.MustCompile("[ (]").Split(tmpsql, 2)[0]
   292  			if util.StringUtil.EqualsIgnoreCase(sqlhead, "SP_SET_PARA_VALUE") || util.StringUtil.EqualsIgnoreCase(sqlhead, "SP_SET_SESSION_READONLY") {
   293  				if otherStmt != nil {
   294  					RWUtil.copyStatement(curStmt, otherStmt)
   295  					execute2(otherStmt)
   296  				}
   297  			}
   298  		}
   299  	case Dm_build_700:
   300  		{
   301  
   302  			if stmt.dmConn.dmConnector.rwHA && curStmt == stmt.rwInfo.stmtStandby &&
   303  				(curStmt.execInfo.rsDatas == nil || len(curStmt.execInfo.rsDatas) == 0) {
   304  				turnToPrimary = true
   305  			}
   306  		}
   307  	}
   308  
   309  	if turnToPrimary {
   310  		stmt.dmConn.rwInfo.toPrimary()
   311  		stmt.rwInfo.stmtCurrent = stmt
   312  
   313  		RWUtil.copyStatement(stmt.rwInfo.stmtStandby, stmt)
   314  
   315  		return execute2(stmt)
   316  	}
   317  	return ret, nil
   318  }
   319  
   320  func (RWUtil rwUtil) checkReadonlyByConn(conn *DmConnection, sql string) bool {
   321  	readonly := true
   322  
   323  	if sql != "" && !conn.dmConnector.rwIgnoreSql {
   324  		tmpsql := strings.TrimSpace(sql)
   325  		sqlhead := strings.SplitN(tmpsql, " ", 2)[0]
   326  		if util.StringUtil.EqualsIgnoreCase(sqlhead, "INSERT") ||
   327  			util.StringUtil.EqualsIgnoreCase(sqlhead, "UPDATE") ||
   328  			util.StringUtil.EqualsIgnoreCase(sqlhead, "DELETE") ||
   329  			util.StringUtil.EqualsIgnoreCase(sqlhead, "CREATE") ||
   330  			util.StringUtil.EqualsIgnoreCase(sqlhead, "TRUNCATE") ||
   331  			util.StringUtil.EqualsIgnoreCase(sqlhead, "DROP") ||
   332  			util.StringUtil.EqualsIgnoreCase(sqlhead, "ALTER") {
   333  			readonly = false
   334  		} else {
   335  			readonly = true
   336  		}
   337  	}
   338  	return readonly
   339  }
   340  
   341  func (RWUtil rwUtil) checkReadonlyByStmt(stmt *DmStatement) bool {
   342  	return RWUtil.checkReadonlyByConn(stmt.dmConn, stmt.nativeSql)
   343  }
   344  
   345  func (RWUtil rwUtil) distributeSqlByConn(conn *DmConnection, query string) RWSiteEnum {
   346  	var dest RWSiteEnum
   347  	if !RWUtil.isStandbyAlive(conn) {
   348  
   349  		dest = conn.rwInfo.toPrimary()
   350  	} else if !RWUtil.checkReadonlyByConn(conn, query) {
   351  
   352  		dest = conn.rwInfo.toPrimary()
   353  	} else if (conn.rwInfo.distribute == PRIMARY && !conn.trxFinish) ||
   354  		(conn.rwInfo.distribute == STANDBY && !conn.rwInfo.connStandby.trxFinish) {
   355  
   356  		dest = conn.rwInfo.distribute
   357  	} else if conn.IsoLevel != int32(sql.LevelSerializable) {
   358  
   359  		dest = conn.rwInfo.toAny()
   360  	} else {
   361  		dest = conn.rwInfo.toPrimary()
   362  	}
   363  
   364  	if dest == PRIMARY {
   365  		conn.rwInfo.connCurrent = conn
   366  	} else {
   367  		conn.rwInfo.connCurrent = conn.rwInfo.connStandby
   368  	}
   369  	return dest
   370  }
   371  
   372  func (RWUtil rwUtil) distributeSqlByStmt(stmt *DmStatement) RWSiteEnum {
   373  	var dest RWSiteEnum
   374  	if !RWUtil.isStandbyAlive(stmt.dmConn) {
   375  
   376  		dest = stmt.dmConn.rwInfo.toPrimary()
   377  	} else if !RWUtil.checkReadonlyByStmt(stmt) {
   378  
   379  		dest = stmt.dmConn.rwInfo.toPrimary()
   380  	} else if (stmt.dmConn.rwInfo.distribute == PRIMARY && !stmt.dmConn.trxFinish) ||
   381  		(stmt.dmConn.rwInfo.distribute == STANDBY && !stmt.dmConn.rwInfo.connStandby.trxFinish) {
   382  
   383  		dest = stmt.dmConn.rwInfo.distribute
   384  	} else if stmt.dmConn.IsoLevel != int32(sql.LevelSerializable) {
   385  
   386  		dest = stmt.dmConn.rwInfo.toAny()
   387  	} else {
   388  		dest = stmt.dmConn.rwInfo.toPrimary()
   389  	}
   390  
   391  	if dest == STANDBY && !RWUtil.isStandbyStatementValid(stmt) {
   392  
   393  		var err error
   394  		stmt.rwInfo.stmtStandby, err = stmt.dmConn.rwInfo.connStandby.prepare(stmt.nativeSql)
   395  		if err != nil {
   396  			dest = stmt.dmConn.rwInfo.toPrimary()
   397  		}
   398  	}
   399  
   400  	if dest == PRIMARY {
   401  		stmt.rwInfo.stmtCurrent = stmt
   402  	} else {
   403  		stmt.rwInfo.stmtCurrent = stmt.rwInfo.stmtStandby
   404  	}
   405  	return dest
   406  }
   407  
   408  func (RWUtil rwUtil) isStandbyAlive(connection *DmConnection) bool {
   409  	return connection.rwInfo.connStandby != nil && !connection.rwInfo.connStandby.closed.IsSet()
   410  }
   411  
   412  func (RWUtil rwUtil) isStandbyStatementValid(statement *DmStatement) bool {
   413  	return statement.rwInfo.stmtStandby != nil && !statement.rwInfo.stmtStandby.closed
   414  }
   415  
   416  func (RWUtil rwUtil) copyStatement(srcStmt *DmStatement, destStmt *DmStatement) {
   417  	destStmt.nativeSql = srcStmt.nativeSql
   418  	destStmt.params = srcStmt.params
   419  	destStmt.paramCount = srcStmt.paramCount
   420  	destStmt.curRowBindIndicator = srcStmt.curRowBindIndicator
   421  }