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 }