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 }