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