github.com/matrixorigin/matrixone@v1.2.0/pkg/frontend/routine.go (about) 1 // Copyright 2021 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package frontend 16 17 import ( 18 "context" 19 "fmt" 20 "sync" 21 "sync/atomic" 22 "time" 23 24 "github.com/fagongzi/goetty/v2" 25 "go.uber.org/zap" 26 27 "github.com/matrixorigin/matrixone/pkg/common/moerr" 28 "github.com/matrixorigin/matrixone/pkg/common/runtime" 29 "github.com/matrixorigin/matrixone/pkg/config" 30 "github.com/matrixorigin/matrixone/pkg/defines" 31 "github.com/matrixorigin/matrixone/pkg/pb/query" 32 "github.com/matrixorigin/matrixone/pkg/util/metric" 33 v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2" 34 "github.com/matrixorigin/matrixone/pkg/util/status" 35 "github.com/matrixorigin/matrixone/pkg/util/trace" 36 ) 37 38 // Routine handles requests. 39 // Read requests from the IOSession layer, 40 // use the executor to handle requests, and response them. 41 type Routine struct { 42 //protocol layer 43 protocol MysqlProtocol 44 45 cancelRoutineCtx context.Context 46 cancelRoutineFunc context.CancelFunc 47 cancelRequestFunc context.CancelFunc 48 49 parameters *config.FrontendParameters 50 51 ses *Session 52 53 closeOnce sync.Once 54 55 inProcessRequest bool 56 57 cancelled atomic.Bool 58 59 connectionBeCounted atomic.Bool 60 61 mu sync.Mutex 62 63 // the id of goroutine that executes the request 64 goroutineID uint64 65 66 restricted atomic.Bool 67 68 printInfoOnce bool 69 70 mc *migrateController 71 } 72 73 func (rt *Routine) needPrintSessionInfo() bool { 74 if rt.printInfoOnce { 75 rt.printInfoOnce = false 76 return true 77 } 78 return false 79 } 80 81 func (rt *Routine) setResricted(val bool) { 82 rt.restricted.Store(val) 83 } 84 85 func (rt *Routine) isRestricted() bool { 86 return rt.restricted.Load() 87 } 88 89 func (rt *Routine) increaseCount(counter func()) { 90 if rt.connectionBeCounted.CompareAndSwap(false, true) { 91 if counter != nil { 92 counter() 93 } 94 } 95 } 96 97 func (rt *Routine) decreaseCount(counter func()) { 98 if rt.connectionBeCounted.CompareAndSwap(true, false) { 99 if counter != nil { 100 counter() 101 } 102 } 103 } 104 105 func (rt *Routine) setCancelled(b bool) bool { 106 return rt.cancelled.Swap(b) 107 } 108 109 func (rt *Routine) isCancelled() bool { 110 return rt.cancelled.Load() 111 } 112 113 func (rt *Routine) setInProcessRequest(b bool) { 114 rt.mu.Lock() 115 defer rt.mu.Unlock() 116 rt.inProcessRequest = b 117 } 118 119 // execCallbackInProcessRequestOnly denotes if inProcessRequest is true, 120 // then the callback will be called. 121 // It has used the mutex. 122 func (rt *Routine) execCallbackBasedOnRequest(want bool, callback func()) { 123 rt.mu.Lock() 124 defer rt.mu.Unlock() 125 if rt.inProcessRequest == want { 126 if callback != nil { 127 callback() 128 } 129 } 130 } 131 132 func (rt *Routine) releaseRoutineCtx() { 133 rt.mu.Lock() 134 defer rt.mu.Unlock() 135 if rt.cancelRoutineFunc != nil { 136 rt.cancelRoutineFunc() 137 } 138 } 139 140 func (rt *Routine) getCancelRoutineCtx() context.Context { 141 rt.mu.Lock() 142 defer rt.mu.Unlock() 143 return rt.cancelRoutineCtx 144 } 145 146 func (rt *Routine) getProtocol() MysqlProtocol { 147 rt.mu.Lock() 148 defer rt.mu.Unlock() 149 return rt.protocol 150 } 151 152 func (rt *Routine) getConnectionID() uint32 { 153 return rt.getProtocol().ConnectionID() 154 } 155 156 func (rt *Routine) updateGoroutineId() { 157 if rt.goroutineID == 0 { 158 rt.goroutineID = GetRoutineId() 159 } 160 } 161 162 func (rt *Routine) getGoroutineId() uint64 { 163 return rt.goroutineID 164 } 165 166 func (rt *Routine) getParameters() *config.FrontendParameters { 167 rt.mu.Lock() 168 defer rt.mu.Unlock() 169 return rt.parameters 170 } 171 172 func (rt *Routine) setSession(ses *Session) { 173 rt.mu.Lock() 174 defer rt.mu.Unlock() 175 rt.ses = ses 176 } 177 178 func (rt *Routine) getSession() *Session { 179 rt.mu.Lock() 180 defer rt.mu.Unlock() 181 return rt.ses 182 } 183 184 func (rt *Routine) setCancelRequestFunc(cf context.CancelFunc) { 185 rt.mu.Lock() 186 defer rt.mu.Unlock() 187 rt.cancelRequestFunc = cf 188 } 189 190 func (rt *Routine) cancelRequestCtx() { 191 rt.mu.Lock() 192 defer rt.mu.Unlock() 193 if rt.cancelRequestFunc != nil { 194 rt.cancelRequestFunc() 195 } 196 } 197 198 func (rt *Routine) reportSystemStatus() (r bool) { 199 ss := rt.ses 200 if ss == nil { 201 return 202 } 203 rm := ss.getRoutineManager() 204 if rm == nil { 205 return 206 } 207 208 now := time.Now() 209 defer func() { 210 if r { 211 rm.reportSystemStatusTime.Store(&now) 212 } 213 }() 214 last := rm.reportSystemStatusTime.Load() 215 if last == nil { 216 r = true 217 return 218 } 219 if now.Sub(*last) > time.Minute { 220 r = true 221 return 222 } 223 return 224 } 225 226 func (rt *Routine) handleRequest(req *Request) error { 227 var routineCtx context.Context 228 var err error 229 var resp *Response 230 var quit bool 231 232 ses := rt.getSession() 233 234 execCtx := ExecCtx{ 235 ses: ses, 236 } 237 238 v2.StartHandleRequestCounter.Inc() 239 defer func() { 240 v2.EndHandleRequestCounter.Inc() 241 }() 242 243 reqBegin := time.Now() 244 var span trace.Span 245 routineCtx, span = trace.Start(rt.getCancelRoutineCtx(), "Routine.handleRequest", 246 trace.WithHungThreshold(30*time.Minute), 247 trace.WithProfileGoroutine(), 248 trace.WithProfileSystemStatus(func() ([]byte, error) { 249 ss, ok := runtime.ProcessLevelRuntime().GetGlobalVariables(runtime.StatusServer) 250 if !ok { 251 return nil, nil 252 } 253 if !rt.reportSystemStatus() { 254 return nil, nil 255 } 256 data, err := ss.(*status.Server).Dump() 257 return data, err 258 }), 259 ) 260 defer span.End() 261 262 parameters := rt.getParameters() 263 //all offspring related to the request inherit the txnCtx 264 cancelRequestCtx, cancelRequestFunc := context.WithTimeout(ses.GetTxnHandler().GetTxnCtx(), parameters.SessionTimeout.Duration) 265 rt.setCancelRequestFunc(cancelRequestFunc) 266 ses.UpdateDebugString() 267 268 if rt.needPrintSessionInfo() { 269 logInfof(ses.GetDebugString(), "mo received first request") 270 } 271 272 tenant := ses.GetTenantInfo() 273 nodeCtx := cancelRequestCtx 274 if ses.getRoutineManager().baseService != nil { 275 nodeCtx = context.WithValue(cancelRequestCtx, defines.NodeIDKey{}, ses.getRoutineManager().baseService.ID()) 276 } 277 tenantCtx := defines.AttachAccount(nodeCtx, tenant.GetTenantID(), tenant.GetUserID(), tenant.GetDefaultRoleID()) 278 279 rt.increaseCount(func() { 280 metric.ConnectionCounter(ses.GetTenantInfo().GetTenant()).Inc() 281 }) 282 283 execCtx.reqCtx = tenantCtx 284 if resp, err = ExecRequest(ses, &execCtx, req); err != nil { 285 if !skipClientQuit(err.Error()) { 286 logError(ses, ses.GetDebugString(), 287 "Failed to execute request", 288 zap.Error(err)) 289 } 290 } 291 292 if resp != nil { 293 if err = rt.getProtocol().SendResponse(tenantCtx, resp); err != nil { 294 logError(ses, ses.GetDebugString(), 295 "Failed to send response", 296 zap.String("response", fmt.Sprintf("%v", resp)), 297 zap.Error(err)) 298 } 299 } 300 301 logDebugf(ses.GetDebugString(), "the time of handling the request %s", time.Since(reqBegin).String()) 302 303 cancelRequestFunc() 304 305 //check the connection has been already canceled or not. 306 select { 307 case <-routineCtx.Done(): 308 quit = true 309 default: 310 } 311 312 quit = quit || rt.isCancelled() 313 314 if quit { 315 rt.decreaseCount(func() { 316 metric.ConnectionCounter(ses.GetTenantInfo().GetTenant()).Dec() 317 }) 318 319 //ensure cleaning the transaction 320 logError(ses, ses.GetDebugString(), "rollback the txn.") 321 tempExecCtx := ExecCtx{ 322 ses: ses, 323 txnOpt: FeTxnOption{byRollback: true}, 324 } 325 err = ses.GetTxnHandler().Rollback(&tempExecCtx) 326 if err != nil { 327 logError(ses, ses.GetDebugString(), 328 "Failed to rollback txn", 329 zap.Error(err)) 330 } 331 332 //close the network connection 333 proto := rt.getProtocol() 334 if proto != nil { 335 proto.Quit() 336 } 337 } 338 339 return err 340 } 341 342 // killQuery if there is a running query, just cancel it. 343 func (rt *Routine) killQuery(killMyself bool, statementId string) { 344 if !killMyself { 345 //1,cancel request ctx 346 rt.cancelRequestCtx() 347 //2.update execute state 348 ses := rt.getSession() 349 if ses != nil { 350 ses.SetQueryInExecute(false) 351 } 352 } 353 } 354 355 // killConnection close the network connection 356 // myself: true -- the client kill itself. 357 // myself: false -- the client kill another connection. 358 func (rt *Routine) killConnection(killMyself bool) { 359 //Case 1: kill the connection itself. Do not close the network connection here. 360 //label the connection with the cancelled tag 361 //if it was cancelled, do nothing 362 if rt.setCancelled(true) { 363 return 364 } 365 366 //Case 2: kill another connection. Close the network here. 367 // if the connection is processing the request, the response may be dropped. 368 // if the connection is not processing the request, it has no effect. 369 if !killMyself { 370 //If it is in processing the request, cancel the root context of the connection. 371 //At the same time, it cancels all the contexts 372 //(includes the request context) derived from the root context. 373 //After the context is cancelled. In handleRequest, the network 374 //will be closed finally. 375 rt.releaseRoutineCtx() 376 377 //If it is in processing the request, it responds to the client normally 378 //before closing the network to avoid the mysql client to be hung. 379 closeConn := func() { 380 //If it is not in processing the request, just close the network 381 proto := rt.protocol 382 if proto != nil { 383 proto.Quit() 384 } 385 } 386 387 rt.execCallbackBasedOnRequest(false, closeConn) 388 } 389 } 390 391 // cleanup When the io is closed, the cleanup will be called in callback Closed(). 392 // cleanup releases the resources only once. 393 // both the client and the server can close the connection. 394 func (rt *Routine) cleanup() { 395 //step 1: cancel the query if there is a running query. 396 //step 2: close the connection. 397 rt.closeOnce.Do(func() { 398 // we should wait for the migration and close the migration controller. 399 rt.mc.waitAndClose() 400 401 ses := rt.getSession() 402 //step A: rollback the txn 403 if ses != nil { 404 tempExecCtx := ExecCtx{ 405 ses: ses, 406 txnOpt: FeTxnOption{byRollback: true}, 407 } 408 err := ses.GetTxnHandler().Rollback(&tempExecCtx) 409 if err != nil { 410 logError(ses, ses.GetDebugString(), 411 "Failed to rollback txn", 412 zap.Error(err)) 413 } 414 } 415 416 //step B: cancel the query 417 rt.killQuery(false, "") 418 419 //step C: cancel the root context of the connection. 420 //At the same time, it cancels all the contexts 421 //(includes the request context) derived from the root context. 422 rt.releaseRoutineCtx() 423 424 //step D: clean protocol 425 rt.protocol.Quit() 426 rt.protocol = nil 427 428 //step E: release the resources related to the session 429 if ses != nil { 430 ses.Close() 431 rt.ses = nil 432 } 433 }) 434 } 435 436 func (rt *Routine) migrateConnectionTo(ctx context.Context, req *query.MigrateConnToRequest) error { 437 var err error 438 rt.mc.migrateOnce.Do(func() { 439 if !rt.mc.beginMigrate() { 440 err = moerr.NewInternalErrorNoCtx("cannot start migrate as routine has been closed") 441 return 442 } 443 defer rt.mc.endMigrate() 444 ses := rt.getSession() 445 err = Migrate(ses, req) 446 }) 447 return err 448 } 449 450 func (rt *Routine) migrateConnectionFrom(resp *query.MigrateConnFromResponse) error { 451 ses := rt.getSession() 452 resp.DB = ses.GetDatabaseName() 453 for _, st := range ses.GetPrepareStmts() { 454 resp.PrepareStmts = append(resp.PrepareStmts, &query.PrepareStmt{ 455 Name: st.Name, 456 SQL: st.Sql, 457 ParamTypes: st.ParamTypes, 458 }) 459 } 460 return nil 461 } 462 463 func NewRoutine(ctx context.Context, protocol MysqlProtocol, parameters *config.FrontendParameters, rs goetty.IOSession) *Routine { 464 ctx = trace.Generate(ctx) // fill span{trace_id} in ctx 465 cancelRoutineCtx, cancelRoutineFunc := context.WithCancel(ctx) 466 ri := &Routine{ 467 protocol: protocol, 468 cancelRoutineCtx: cancelRoutineCtx, 469 cancelRoutineFunc: cancelRoutineFunc, 470 parameters: parameters, 471 printInfoOnce: true, 472 mc: newMigrateController(), 473 } 474 protocol.UpdateCtx(cancelRoutineCtx) 475 476 return ri 477 }