github.com/xiyichan/dm8@v0.0.0-20211213021639-be727be3e136/zw.go (about)

     1  /*
     2   * Copyright (c) 2000-2018, 达梦数据库有限公司.
     3   * All rights reserved.
     4   */
     5  package dm
     6  
     7  import (
     8  	"context"
     9  	"database/sql"
    10  	"database/sql/driver"
    11  	"github.com/xiyichan/dm8/util"
    12  	"io"
    13  	"strings"
    14  	"time"
    15  )
    16  
    17  const (
    18  	SQL_SELECT_STANDBY = "select distinct mailIni.inst_name, mailIni.INST_IP, mailIni.INST_PORT, archIni.arch_status " +
    19  		"from  v$arch_status archIni " +
    20  		"left join (select * from V$DM_MAL_INI) mailIni on archIni.arch_dest = mailIni.inst_name " +
    21  		"left join V$MAL_LINK_STATUS on CTL_LINK_STATUS  = 'CONNECTED' AND DATA_LINK_STATUS = 'CONNECTED' " +
    22  		"where archIni.arch_type in ('TIMELY', 'REALTIME')"
    23  
    24  	SQL_SELECT_STANDBY2 = "select distinct " +
    25  		"mailIni.mal_inst_name, mailIni.mal_INST_HOST, mailIni.mal_INST_PORT, archIni.arch_status " +
    26  		"from v$arch_status archIni " + "left join (select * from V$DM_MAL_INI) mailIni " +
    27  		"on archIni.arch_dest = mailIni.mal_inst_name " + "left join V$MAL_LINK_STATUS " +
    28  		"on CTL_LINK_STATUS  = 'CONNECTED' AND DATA_LINK_STATUS = 'CONNECTED' " +
    29  		"where archIni.arch_type in ('TIMELY', 'REALTIME')"
    30  )
    31  
    32  type rwUtil struct {
    33  }
    34  
    35  var RWUtil = rwUtil{}
    36  
    37  func (RWUtil rwUtil) reconnect(connection *DmConnection) error {
    38  	if connection.rwInfo == nil {
    39  		return nil
    40  	}
    41  
    42  	RWUtil.removeStandby(connection)
    43  
    44  	err := connection.reconnect()
    45  	if err != nil {
    46  		return err
    47  	}
    48  	connection.rwInfo.cleanup()
    49  	connection.rwInfo.rwCounter = getRwCounterInstance(connection)
    50  
    51  	RWUtil.connectStandby(connection)
    52  
    53  	return nil
    54  }
    55  
    56  func (RWUtil rwUtil) recoverStandby(connection *DmConnection) {
    57  	if connection.closed.IsSet() || RWUtil.isStandbyAlive(connection) {
    58  		return
    59  	}
    60  
    61  	ts := time.Now().Nanosecond()
    62  
    63  	freq := connection.dmConnector.rwStandbyRecoverTime * int(time.Second)
    64  	if freq <= 0 || ts-connection.rwInfo.tryRecoverTs < freq {
    65  		return
    66  	}
    67  
    68  	RWUtil.connectStandby(connection)
    69  	connection.rwInfo.tryRecoverTs = ts
    70  
    71  }
    72  
    73  func (RWUtil rwUtil) connectStandby(connection *DmConnection) {
    74  	db := RWUtil.chooseValidStandby(connection)
    75  	if db == nil {
    76  		return
    77  	}
    78  
    79  	standbyConnector := *connection.dmConnector
    80  	standbyConnector.host = db.host
    81  	standbyConnector.port = db.port
    82  	standbyConnector.rwStandby = true
    83  	standbyConnectorPointer := &standbyConnector
    84  	var err error
    85  	connection.rwInfo.connStandby, err = standbyConnectorPointer.connectSingle(context.Background())
    86  	if err != nil {
    87  		return
    88  	}
    89  
    90  	if int(connection.rwInfo.connStandby.SvrMode) != SERVER_MODE_STANDBY || int(connection.rwInfo.connStandby.SvrStat) != SERVER_STATUS_OPEN {
    91  		RWUtil.removeStandby(connection)
    92  	}
    93  }
    94  
    95  func (RWUtil rwUtil) chooseValidStandby(connection *DmConnection) *DB {
    96  	rs, err := connection.query(SQL_SELECT_STANDBY2, nil)
    97  	if err != nil {
    98  		rs, err = connection.query(SQL_SELECT_STANDBY, nil)
    99  		if err != nil {
   100  			return nil
   101  		}
   102  	}
   103  	rowsCount := int(rs.CurrentRows.getRowCount())
   104  	if rowsCount > 0 {
   105  		i := 0
   106  		rowIndex := connection.rwInfo.rwCounter.random(rowsCount)
   107  		dest := make([]driver.Value, 3)
   108  		for err := rs.next(dest); err != io.EOF; i++ {
   109  			if i == rowIndex {
   110  				db := newDB(dest[1].(string), int(dest[2].(int32)))
   111  				rs.close()
   112  				return db
   113  			}
   114  			rs.next(dest)
   115  		}
   116  	}
   117  
   118  	rs.close()
   119  	return nil
   120  }
   121  
   122  func (RWUtil rwUtil) afterExceptionOnStandby(connection *DmConnection, e error) {
   123  	if e.(*DmError).ErrCode == ECGO_COMMUNITION_ERROR.ErrCode {
   124  		RWUtil.removeStandby(connection)
   125  	}
   126  }
   127  
   128  func (RWUtil rwUtil) removeStandby(connection *DmConnection) {
   129  	if connection.rwInfo.connStandby != nil {
   130  		connection.rwInfo.connStandby.close()
   131  		connection.rwInfo.connStandby = nil
   132  	}
   133  }
   134  
   135  func (RWUtil rwUtil) executeByConn(conn *DmConnection, execute1 func() (interface{}, error), execute2 func(otherConn *DmConnection) (interface{}, error)) (interface{}, error) {
   136  	turnToPrimary := false
   137  	var standbyException error
   138  
   139  	RWUtil.recoverStandby(conn)
   140  
   141  	ret, err := execute1()
   142  	if err != nil {
   143  		standbyException = err
   144  
   145  		if conn.rwInfo.connCurrent == conn.rwInfo.connStandby {
   146  			RWUtil.afterExceptionOnStandby(conn, err)
   147  			turnToPrimary = true
   148  		} else {
   149  			return nil, err
   150  		}
   151  	}
   152  
   153  	curConn := conn.rwInfo.connCurrent
   154  	var otherConn *DmConnection
   155  	if curConn != conn {
   156  		otherConn = conn
   157  	} else {
   158  		otherConn = conn.rwInfo.connStandby
   159  	}
   160  
   161  	switch conn.lastExecInfo.retSqlType {
   162  	case Dm_build_91, Dm_build_92, Dm_build_96, Dm_build_103, Dm_build_102, Dm_build_94:
   163  		{
   164  
   165  			if otherConn != nil {
   166  				execute2(otherConn)
   167  			}
   168  		}
   169  	case Dm_build_100:
   170  		{
   171  
   172  			if conn.dmConnector.rwHA && conn == conn.rwInfo.connStandby &&
   173  				(conn.lastExecInfo.rsDatas == nil || len(conn.lastExecInfo.rsDatas) == 0) {
   174  				turnToPrimary = true
   175  			}
   176  		}
   177  	}
   178  
   179  	if turnToPrimary {
   180  		conn.rwInfo.distribute = conn.rwInfo.rwCounter.countPrimary()
   181  
   182  		t, err := execute2(conn)
   183  		if err != nil {
   184  			if standbyException != nil {
   185  				conn.rwInfo.stmtCurrent = conn.rwInfo.stmtStandby
   186  			}
   187  			return t, err
   188  		}
   189  		return t, nil
   190  
   191  	}
   192  	return ret, nil
   193  }
   194  
   195  func (RWUtil rwUtil) executeByStmt(stmt *DmStatement, execute1 func() (interface{}, error), execute2 func(otherStmt *DmStatement) (interface{}, error)) (interface{}, error) {
   196  	turnToPrimary := false
   197  	var standbyException error
   198  
   199  	RWUtil.recoverStandby(stmt.dmConn)
   200  
   201  	ret, err := execute1()
   202  	if err != nil {
   203  		standbyException = err
   204  
   205  		if stmt.rwInfo.stmtCurrent == stmt.rwInfo.stmtStandby {
   206  			RWUtil.afterExceptionOnStandby(stmt.dmConn, err)
   207  			turnToPrimary = true
   208  		} else {
   209  			return nil, err
   210  		}
   211  	}
   212  
   213  	curStmt := stmt.rwInfo.stmtCurrent
   214  	var otherStmt *DmStatement
   215  	if curStmt != stmt {
   216  		otherStmt = stmt
   217  	} else {
   218  		otherStmt = stmt.rwInfo.stmtStandby
   219  	}
   220  
   221  	switch stmt.execInfo.retSqlType {
   222  	case Dm_build_91, Dm_build_92, Dm_build_96, Dm_build_103, Dm_build_102, Dm_build_94:
   223  		{
   224  
   225  			RWUtil.copyStatement(curStmt, otherStmt)
   226  			execute2(otherStmt)
   227  		}
   228  	case Dm_build_100:
   229  		{
   230  
   231  			if stmt.dmConn.dmConnector.rwHA && curStmt == stmt.rwInfo.stmtStandby &&
   232  				(curStmt.execInfo.rsDatas == nil || len(curStmt.execInfo.rsDatas) == 0) {
   233  				turnToPrimary = true
   234  			}
   235  		}
   236  	}
   237  
   238  	if turnToPrimary {
   239  		stmt.dmConn.rwInfo.distribute = stmt.dmConn.rwInfo.rwCounter.countPrimary()
   240  		stmt.rwInfo.stmtCurrent = stmt
   241  
   242  		RWUtil.copyStatement(stmt.rwInfo.stmtStandby, stmt)
   243  
   244  		t, err := execute2(stmt)
   245  		if err != nil {
   246  			if standbyException != nil {
   247  				stmt.rwInfo.stmtCurrent = stmt.rwInfo.stmtStandby
   248  			}
   249  			return t, err
   250  		}
   251  		return t, nil
   252  
   253  	}
   254  	return ret, nil
   255  }
   256  
   257  func (RWUtil rwUtil) distributeSqlByConn(connection *DmConnection, query string) RWSiteEnum {
   258  	if !RWUtil.isStandbyAlive(connection) {
   259  		connection.rwInfo.connCurrent = connection
   260  		return connection.rwInfo.rwCounter.countPrimary()
   261  	}
   262  
   263  	if (connection.rwInfo.distribute == PRIMARY && !connection.trxFinish) ||
   264  		(connection.rwInfo.distribute == STANDBY && !connection.rwInfo.connStandby.trxFinish) {
   265  		if connection.rwInfo.distribute == PRIMARY {
   266  			connection.rwInfo.connCurrent = connection
   267  		} else {
   268  			connection.rwInfo.connCurrent = connection.rwInfo.connStandby
   269  		}
   270  
   271  		return connection.rwInfo.distribute
   272  	}
   273  
   274  	readonly := true
   275  
   276  	if query != "" {
   277  		tmpsql := strings.TrimSpace(query)
   278  		sqlhead := strings.SplitN(tmpsql, " ", 2)[0]
   279  		if util.StringUtil.EqualsIgnoreCase(sqlhead, "INSERT") ||
   280  			util.StringUtil.EqualsIgnoreCase(sqlhead, "UPDATE") ||
   281  			util.StringUtil.EqualsIgnoreCase(sqlhead, "DELETE") ||
   282  			util.StringUtil.EqualsIgnoreCase(sqlhead, "CREATE") ||
   283  			util.StringUtil.EqualsIgnoreCase(sqlhead, "TRUNCATE") ||
   284  			util.StringUtil.EqualsIgnoreCase(sqlhead, "DROP") ||
   285  			util.StringUtil.EqualsIgnoreCase(sqlhead, "ALTER") ||
   286  			util.StringUtil.EqualsIgnoreCase(sqlhead, "SP_SET_SESSION_READONLY") {
   287  			readonly = false
   288  		} else {
   289  			readonly = true
   290  		}
   291  	}
   292  
   293  	if readonly && connection.IsoLevel != int32(sql.LevelSerializable) {
   294  		rWSiteEnum := connection.rwInfo.rwCounter.count(ANYOF, connection.rwInfo.connStandby)
   295  		if rWSiteEnum == PRIMARY {
   296  			connection.rwInfo.connCurrent = connection
   297  		} else if rWSiteEnum == STANDBY {
   298  			connection.rwInfo.connCurrent = connection.rwInfo.connStandby
   299  		}
   300  		return rWSiteEnum
   301  	}
   302  
   303  	connection.rwInfo.connCurrent = connection
   304  	return connection.rwInfo.rwCounter.countPrimary()
   305  }
   306  
   307  func (RWUtil rwUtil) distributeSqlByStmt(statement *DmStatement, sql string) {
   308  	if !RWUtil.isStandbyAlive(statement.dmConn) || !RWUtil.isStandbyStatementValid(statement) {
   309  		statement.dmConn.rwInfo.rwCounter.countPrimary()
   310  		return
   311  	}
   312  
   313  	statement.dmConn.rwInfo.distribute = RWUtil.distributeSqlByConn(statement.dmConn, sql)
   314  
   315  	if statement.dmConn.rwInfo.distribute == PRIMARY {
   316  		statement.rwInfo.stmtCurrent = statement
   317  	} else {
   318  		statement.rwInfo.stmtCurrent = statement.rwInfo.stmtStandby
   319  	}
   320  }
   321  
   322  func (RWUtil rwUtil) copyStatement(srcStmt *DmStatement, destStmt *DmStatement) {
   323  	destStmt.nativeSql = srcStmt.nativeSql
   324  	destStmt.params = srcStmt.params
   325  	destStmt.paramCount = srcStmt.paramCount
   326  	destStmt.curRowBindIndicator = srcStmt.curRowBindIndicator
   327  }
   328  
   329  func (RWUtil rwUtil) isStandbyAlive(connection *DmConnection) bool {
   330  	return connection.rwInfo.connStandby != nil && !connection.rwInfo.connStandby.closed.IsSet()
   331  }
   332  
   333  func (RWUtil rwUtil) isStandbyStatementValid(statement *DmStatement) bool {
   334  	return statement.rwInfo.stmtStandby != nil && !statement.rwInfo.stmtStandby.closed
   335  }