github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/stochastik/txn.go (about) 1 // Copyright 2020 WHTCORPS INC, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package stochastik 15 16 import ( 17 "bytes" 18 "context" 19 "fmt" 20 "runtime/trace" 21 "strings" 22 "sync/atomic" 23 24 "github.com/opentracing/opentracing-go" 25 "github.com/whtcorpsinc/BerolinaSQL/terror" 26 "github.com/whtcorpsinc/errors" 27 "github.com/whtcorpsinc/failpoint" 28 "github.com/whtcorpsinc/fidelpb/go-binlog" 29 "github.com/whtcorpsinc/milevadb/blockcodec" 30 "github.com/whtcorpsinc/milevadb/causetstore/einsteindb/oracle" 31 "github.com/whtcorpsinc/milevadb/config" 32 "github.com/whtcorpsinc/milevadb/ekv" 33 "github.com/whtcorpsinc/milevadb/soliton/logutil" 34 "github.com/whtcorpsinc/milevadb/stochastikctx" 35 "github.com/whtcorpsinc/milevadb/stochastikctx/binloginfo" 36 "go.uber.org/zap" 37 ) 38 39 // TxnState wraps ekv.Transaction to provide a new ekv.Transaction. 40 // 1. It holds all memex related modification in the buffer before flush to the txn, 41 // so if execute memex meets error, the txn won't be made dirty. 42 // 2. It's a lazy transaction, that means it's a txnFuture before StartTS() is really need. 43 type TxnState struct { 44 // States of a TxnState should be one of the followings: 45 // Invalid: ekv.Transaction == nil && txnFuture == nil 46 // Pending: ekv.Transaction == nil && txnFuture != nil 47 // Valid: ekv.Transaction != nil && txnFuture == nil 48 ekv.Transaction 49 txnFuture *txnFuture 50 51 initCnt int 52 stagingHandle ekv.StagingHandle 53 mutations map[int64]*binlog.TableMutation 54 } 55 56 func (st *TxnState) init() { 57 st.mutations = make(map[int64]*binlog.TableMutation) 58 } 59 60 func (st *TxnState) initStmtBuf() { 61 if st.Transaction == nil { 62 return 63 } 64 buf := st.Transaction.GetMemBuffer() 65 st.initCnt = buf.Len() 66 st.stagingHandle = buf.Staging() 67 } 68 69 // countHint is estimated count of mutations. 70 func (st *TxnState) countHint() int { 71 if st.stagingHandle == ekv.InvalidStagingHandle { 72 return 0 73 } 74 return st.Transaction.GetMemBuffer().Len() - st.initCnt 75 } 76 77 func (st *TxnState) flushStmtBuf() { 78 if st.stagingHandle == ekv.InvalidStagingHandle { 79 return 80 } 81 buf := st.Transaction.GetMemBuffer() 82 buf.Release(st.stagingHandle) 83 st.initCnt = buf.Len() 84 } 85 86 func (st *TxnState) cleanupStmtBuf() { 87 if st.stagingHandle == ekv.InvalidStagingHandle { 88 return 89 } 90 buf := st.Transaction.GetMemBuffer() 91 buf.Cleanup(st.stagingHandle) 92 st.initCnt = buf.Len() 93 } 94 95 // Size implements the MemBuffer interface. 96 func (st *TxnState) Size() int { 97 if st.Transaction == nil { 98 return 0 99 } 100 return st.Transaction.Size() 101 } 102 103 // Valid implements the ekv.Transaction interface. 104 func (st *TxnState) Valid() bool { 105 return st.Transaction != nil && st.Transaction.Valid() 106 } 107 108 func (st *TxnState) pending() bool { 109 return st.Transaction == nil && st.txnFuture != nil 110 } 111 112 func (st *TxnState) validOrPending() bool { 113 return st.txnFuture != nil || st.Valid() 114 } 115 116 func (st *TxnState) String() string { 117 if st.Transaction != nil { 118 return st.Transaction.String() 119 } 120 if st.txnFuture != nil { 121 return "txnFuture" 122 } 123 return "invalid transaction" 124 } 125 126 // GoString implements the "%#v" format for fmt.Printf. 127 func (st *TxnState) GoString() string { 128 var s strings.Builder 129 s.WriteString("Txn{") 130 if st.pending() { 131 s.WriteString("state=pending") 132 } else if st.Valid() { 133 s.WriteString("state=valid") 134 fmt.Fprintf(&s, ", txnStartTS=%d", st.Transaction.StartTS()) 135 if len(st.mutations) > 0 { 136 fmt.Fprintf(&s, ", len(mutations)=%d, %#v", len(st.mutations), st.mutations) 137 } 138 } else { 139 s.WriteString("state=invalid") 140 } 141 142 s.WriteString("}") 143 return s.String() 144 } 145 146 func (st *TxnState) changeInvalidToValid(txn ekv.Transaction) { 147 st.Transaction = txn 148 st.initStmtBuf() 149 st.txnFuture = nil 150 } 151 152 func (st *TxnState) changeInvalidToPending(future *txnFuture) { 153 st.Transaction = nil 154 st.txnFuture = future 155 } 156 157 func (st *TxnState) changePendingToValid(ctx context.Context) error { 158 if st.txnFuture == nil { 159 return errors.New("transaction future is not set") 160 } 161 162 future := st.txnFuture 163 st.txnFuture = nil 164 165 defer trace.StartRegion(ctx, "WaitTsoFuture").End() 166 txn, err := future.wait() 167 if err != nil { 168 st.Transaction = nil 169 return err 170 } 171 st.Transaction = txn 172 st.initStmtBuf() 173 return nil 174 } 175 176 func (st *TxnState) changeToInvalid() { 177 if st.stagingHandle != ekv.InvalidStagingHandle { 178 st.Transaction.GetMemBuffer().Cleanup(st.stagingHandle) 179 } 180 st.stagingHandle = ekv.InvalidStagingHandle 181 st.Transaction = nil 182 st.txnFuture = nil 183 } 184 185 var hasMockAutoIncIDRetry = int64(0) 186 187 func enableMockAutoIncIDRetry() { 188 atomic.StoreInt64(&hasMockAutoIncIDRetry, 1) 189 } 190 191 func mockAutoIncIDRetry() bool { 192 return atomic.LoadInt64(&hasMockAutoIncIDRetry) == 1 193 } 194 195 var mockAutoRandIDRetryCount = int64(0) 196 197 func needMockAutoRandIDRetry() bool { 198 return atomic.LoadInt64(&mockAutoRandIDRetryCount) > 0 199 } 200 201 func decreaseMockAutoRandIDRetryCount() { 202 atomic.AddInt64(&mockAutoRandIDRetryCount, -1) 203 } 204 205 // ResetMockAutoRandIDRetryCount set the number of occurrences of 206 // `ekv.ErrTxnRetryable` when calling TxnState.Commit(). 207 func ResetMockAutoRandIDRetryCount(failTimes int64) { 208 atomic.StoreInt64(&mockAutoRandIDRetryCount, failTimes) 209 } 210 211 // Commit overrides the Transaction interface. 212 func (st *TxnState) Commit(ctx context.Context) error { 213 defer st.reset() 214 if len(st.mutations) != 0 || st.countHint() != 0 { 215 logutil.BgLogger().Error("the code should never run here", 216 zap.String("TxnState", st.GoString()), 217 zap.Int("staging handler", int(st.stagingHandle)), 218 zap.Stack("something must be wrong")) 219 return errors.Trace(ekv.ErrInvalidTxn) 220 } 221 222 // mockCommitError8942 is used for PR #8942. 223 failpoint.Inject("mockCommitError8942", func(val failpoint.Value) { 224 if val.(bool) { 225 failpoint.Return(ekv.ErrTxnRetryable) 226 } 227 }) 228 229 // mockCommitRetryForAutoIncID is used to mock an commit retry for adjustAutoIncrementCauset. 230 failpoint.Inject("mockCommitRetryForAutoIncID", func(val failpoint.Value) { 231 if val.(bool) && !mockAutoIncIDRetry() { 232 enableMockAutoIncIDRetry() 233 failpoint.Return(ekv.ErrTxnRetryable) 234 } 235 }) 236 237 failpoint.Inject("mockCommitRetryForAutoRandID", func(val failpoint.Value) { 238 if val.(bool) && needMockAutoRandIDRetry() { 239 decreaseMockAutoRandIDRetryCount() 240 failpoint.Return(ekv.ErrTxnRetryable) 241 } 242 }) 243 244 return st.Transaction.Commit(ctx) 245 } 246 247 // Rollback overrides the Transaction interface. 248 func (st *TxnState) Rollback() error { 249 defer st.reset() 250 return st.Transaction.Rollback() 251 } 252 253 func (st *TxnState) reset() { 254 st.cleanup() 255 st.changeToInvalid() 256 } 257 258 func (st *TxnState) cleanup() { 259 st.cleanupStmtBuf() 260 st.initStmtBuf() 261 for key := range st.mutations { 262 delete(st.mutations, key) 263 } 264 } 265 266 // KeysNeedToLock returns the keys need to be locked. 267 func (st *TxnState) KeysNeedToLock() ([]ekv.Key, error) { 268 if st.stagingHandle == ekv.InvalidStagingHandle { 269 return nil, nil 270 } 271 keys := make([]ekv.Key, 0, st.countHint()) 272 buf := st.Transaction.GetMemBuffer() 273 buf.InspectStage(st.stagingHandle, func(k ekv.Key, flags ekv.KeyFlags, v []byte) { 274 if !keyNeedToLock(k, v, flags) { 275 return 276 } 277 keys = append(keys, k) 278 }) 279 return keys, nil 280 } 281 282 func keyNeedToLock(k, v []byte, flags ekv.KeyFlags) bool { 283 isTableKey := bytes.HasPrefix(k, blockcodec.TablePrefix()) 284 if !isTableKey { 285 // spacetime key always need to dagger. 286 return true 287 } 288 if flags.HasPresumeKeyNotExists() { 289 return true 290 } 291 isDelete := len(v) == 0 292 if isDelete { 293 // only need to delete event key. 294 return k[10] == 'r' 295 } 296 if blockcodec.IsUntouchedIndexKValue(k, v) { 297 return false 298 } 299 isNonUniqueIndex := blockcodec.IsIndexKey(k) && len(v) == 1 300 // Put event key and unique index need to dagger. 301 return !isNonUniqueIndex 302 } 303 304 func getBinlogMutation(ctx stochastikctx.Context, blockID int64) *binlog.TableMutation { 305 bin := binloginfo.GetPrewriteValue(ctx, true) 306 for i := range bin.Mutations { 307 if bin.Mutations[i].TableId == blockID { 308 return &bin.Mutations[i] 309 } 310 } 311 idx := len(bin.Mutations) 312 bin.Mutations = append(bin.Mutations, binlog.TableMutation{TableId: blockID}) 313 return &bin.Mutations[idx] 314 } 315 316 func mergeToMutation(m1, m2 *binlog.TableMutation) { 317 m1.InsertedRows = append(m1.InsertedRows, m2.InsertedRows...) 318 m1.UFIDelatedRows = append(m1.UFIDelatedRows, m2.UFIDelatedRows...) 319 m1.DeletedIds = append(m1.DeletedIds, m2.DeletedIds...) 320 m1.DeletedPks = append(m1.DeletedPks, m2.DeletedPks...) 321 m1.DeletedRows = append(m1.DeletedRows, m2.DeletedRows...) 322 m1.Sequence = append(m1.Sequence, m2.Sequence...) 323 } 324 325 type txnFailFuture struct{} 326 327 func (txnFailFuture) Wait() (uint64, error) { 328 return 0, errors.New("mock get timestamp fail") 329 } 330 331 // txnFuture is a promise, which promises to return a txn in future. 332 type txnFuture struct { 333 future oracle.Future 334 causetstore ekv.CausetStorage 335 } 336 337 func (tf *txnFuture) wait() (ekv.Transaction, error) { 338 startTS, err := tf.future.Wait() 339 if err == nil { 340 return tf.causetstore.BeginWithStartTS(startTS) 341 } else if config.GetGlobalConfig().CausetStore == "entangledstore" { 342 return nil, err 343 } 344 345 logutil.BgLogger().Warn("wait tso failed", zap.Error(err)) 346 // It would retry get timestamp. 347 return tf.causetstore.Begin() 348 } 349 350 func (s *stochastik) getTxnFuture(ctx context.Context) *txnFuture { 351 if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil { 352 span1 := span.Tracer().StartSpan("stochastik.getTxnFuture", opentracing.ChildOf(span.Context())) 353 defer span1.Finish() 354 ctx = opentracing.ContextWithSpan(ctx, span1) 355 } 356 357 oracleStore := s.causetstore.GetOracle() 358 var tsFuture oracle.Future 359 if s.stochastikVars.LowResolutionTSO { 360 tsFuture = oracleStore.GetLowResolutionTimestampAsync(ctx) 361 } else { 362 tsFuture = oracleStore.GetTimestampAsync(ctx) 363 } 364 ret := &txnFuture{future: tsFuture, causetstore: s.causetstore} 365 failpoint.InjectContext(ctx, "mockGetTSFail", func() { 366 ret.future = txnFailFuture{} 367 }) 368 return ret 369 } 370 371 // HasDirtyContent checks whether there's dirty uFIDelate on the given causet. 372 // Put this function here is to avoid cycle import. 373 func (s *stochastik) HasDirtyContent(tid int64) bool { 374 if s.txn.Transaction == nil { 375 return false 376 } 377 seekKey := blockcodec.EncodeTablePrefix(tid) 378 it, err := s.txn.GetMemBuffer().Iter(seekKey, nil) 379 terror.Log(err) 380 return it.Valid() && bytes.HasPrefix(it.Key(), seekKey) 381 } 382 383 // StmtCommit implements the stochastikctx.Context interface. 384 func (s *stochastik) StmtCommit() { 385 defer func() { 386 s.txn.cleanup() 387 }() 388 389 st := &s.txn 390 st.flushStmtBuf() 391 392 // Need to flush binlog. 393 for blockID, delta := range st.mutations { 394 mutation := getBinlogMutation(s, blockID) 395 mergeToMutation(mutation, delta) 396 } 397 } 398 399 // StmtRollback implements the stochastikctx.Context interface. 400 func (s *stochastik) StmtRollback() { 401 s.txn.cleanup() 402 } 403 404 // StmtGetMutation implements the stochastikctx.Context interface. 405 func (s *stochastik) StmtGetMutation(blockID int64) *binlog.TableMutation { 406 st := &s.txn 407 if _, ok := st.mutations[blockID]; !ok { 408 st.mutations[blockID] = &binlog.TableMutation{TableId: blockID} 409 } 410 return st.mutations[blockID] 411 }