github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/client/leak_checker.go (about)

     1  // Copyright 2023 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 client
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"sync"
    21  	"time"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/common/log"
    24  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    25  	"github.com/matrixorigin/matrixone/pkg/common/stopper"
    26  	"github.com/matrixorigin/matrixone/pkg/pb/txn"
    27  )
    28  
    29  // leakChecker is used to detect leak txn which is not committed or aborted.
    30  type leakChecker struct {
    31  	sync.RWMutex
    32  	logger         *log.MOLogger
    33  	actives        []ActiveTxn
    34  	maxActiveAges  time.Duration
    35  	leakHandleFunc func([]ActiveTxn)
    36  	stopper        *stopper.Stopper
    37  }
    38  
    39  func newLeakCheck(
    40  	maxActiveAges time.Duration,
    41  	leakHandleFunc func([]ActiveTxn)) *leakChecker {
    42  	logger := runtime.DefaultRuntime().Logger()
    43  	return &leakChecker{
    44  		logger:         logger,
    45  		maxActiveAges:  maxActiveAges,
    46  		leakHandleFunc: leakHandleFunc,
    47  		stopper: stopper.NewStopper("txn-leak-checker",
    48  			stopper.WithLogger(logger.RawLogger())),
    49  	}
    50  }
    51  
    52  func (lc *leakChecker) start() {
    53  	if err := lc.stopper.RunTask(lc.check); err != nil {
    54  		panic(err)
    55  	}
    56  }
    57  
    58  func (lc *leakChecker) close() {
    59  	lc.stopper.Stop()
    60  }
    61  
    62  func (lc *leakChecker) txnOpened(
    63  	txnOp *txnOperator,
    64  	txnID []byte,
    65  	options txn.TxnOptions) {
    66  	lc.Lock()
    67  	defer lc.Unlock()
    68  	lc.actives = append(lc.actives, ActiveTxn{
    69  		Options:  options,
    70  		ID:       txnID,
    71  		CreateAt: time.Now(),
    72  		txnOp:    txnOp,
    73  	})
    74  }
    75  
    76  func (lc *leakChecker) txnClosed(txnID []byte) {
    77  	lc.Lock()
    78  	defer lc.Unlock()
    79  	values := lc.actives[:0]
    80  	for idx, txn := range lc.actives {
    81  		if bytes.Equal(txn.ID, txnID) {
    82  			values = append(values, lc.actives[idx+1:]...)
    83  			break
    84  		}
    85  		values = append(values, txn)
    86  	}
    87  	lc.actives = values
    88  }
    89  
    90  func (lc *leakChecker) check(ctx context.Context) {
    91  	for {
    92  		select {
    93  		case <-ctx.Done():
    94  			return
    95  		case <-time.After(lc.maxActiveAges):
    96  			if values := lc.doCheck(); len(values) > 0 {
    97  				lc.leakHandleFunc(values)
    98  			}
    99  		}
   100  	}
   101  }
   102  
   103  func (lc *leakChecker) doCheck() []ActiveTxn {
   104  	lc.RLock()
   105  	defer lc.RUnlock()
   106  
   107  	var values []ActiveTxn
   108  	now := time.Now()
   109  	for _, txn := range lc.actives {
   110  		if now.Sub(txn.CreateAt) >= lc.maxActiveAges {
   111  			if txn.txnOp != nil {
   112  				txn.Options.Counter = txn.txnOp.counter()
   113  				txn.Options.InRunSql = txn.txnOp.inRunSql()
   114  				txn.Options.InCommit = txn.txnOp.inCommit()
   115  				txn.Options.InRollback = txn.txnOp.inRollback()
   116  				txn.Options.SessionInfo = txn.txnOp.options.SessionInfo
   117  			}
   118  			values = append(values, txn)
   119  		}
   120  	}
   121  	return values
   122  }
   123  
   124  type ActiveTxn struct {
   125  	Options  txn.TxnOptions
   126  	ID       []byte
   127  	CreateAt time.Time
   128  	txnOp    *txnOperator
   129  }