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 }