github.com/matrixorigin/matrixone@v0.7.0/pkg/lockservice/deadlock_test.go (about)

     1  // Copyright 2022 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 lockservice
    16  
    17  import (
    18  	"context"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  func TestCheckWithDeadlock(t *testing.T) {
    26  	txn1 := []byte("t1")
    27  	txn2 := []byte("t2")
    28  	txn3 := []byte("t3")
    29  	txn4 := []byte("t4")
    30  
    31  	m := map[string][][]byte{
    32  		string(txn1): {txn2},
    33  		string(txn2): {txn3},
    34  		string(txn3): {txn1},
    35  	}
    36  	abortC := make(chan []byte, 1)
    37  	defer close(abortC)
    38  
    39  	d := newDeadlockDetector(func(txn []byte, w *waiters) bool {
    40  		for _, id := range m[string(txn)] {
    41  			if !w.add(id) {
    42  				return false
    43  			}
    44  		}
    45  		return true
    46  	}, func(txn []byte) {
    47  		abortC <- txn
    48  	})
    49  	defer d.close()
    50  
    51  	assert.NoError(t, d.check(txn1))
    52  	assert.Equal(t, txn1, <-abortC)
    53  	d.txnClosed(txn1)
    54  
    55  	assert.NoError(t, d.check(txn2))
    56  	assert.Equal(t, txn2, <-abortC)
    57  	d.txnClosed(txn2)
    58  
    59  	assert.NoError(t, d.check(txn3))
    60  	assert.Equal(t, txn3, <-abortC)
    61  	d.txnClosed(txn3)
    62  
    63  	assert.NoError(t, d.check(txn4))
    64  	select {
    65  	case <-abortC:
    66  		assert.Fail(t, "can not found dead lock")
    67  	case <-time.After(time.Millisecond * 100):
    68  	}
    69  }
    70  
    71  type rowToLock struct {
    72  	tableID uint64
    73  	row     []byte
    74  }
    75  
    76  func TestTwoTxsDeadlock(t *testing.T) {
    77  	cases := []struct {
    78  		row1, row2 rowToLock
    79  	}{
    80  		{
    81  			rowToLock{0, []byte{1}},
    82  			rowToLock{0, []byte{2}},
    83  		},
    84  		{
    85  			rowToLock{0, []byte{1}},
    86  			rowToLock{1, []byte{1}},
    87  		},
    88  	}
    89  
    90  	for _, c := range cases {
    91  		runDeadlock(t, c.row1, c.row2)
    92  	}
    93  }
    94  
    95  //			txnA			txnB
    96  //	   locks row1		locks row2
    97  //					 	locks row1 		(txnB waits for txnA)
    98  //	   locks row2						(deadlock happens)
    99  func runDeadlock(t *testing.T, row1, row2 rowToLock) {
   100  	txnA := []byte("txnA")
   101  	txnB := []byte("txnB")
   102  	l := NewLockService()
   103  	ctx := context.Background()
   104  	option := LockOptions{
   105  		granularity: Row,
   106  		mode:        Exclusive,
   107  		policy:      Wait,
   108  	}
   109  
   110  	err := l.Lock(context.Background(), row1.tableID, [][]byte{row1.row}, txnA, option)
   111  	assert.NoError(t, err)
   112  	go func() {
   113  		err := l.Lock(ctx, row2.tableID, [][]byte{row2.row}, txnB, option)
   114  		assert.NoError(t, err)
   115  		err = l.Lock(ctx, row1.tableID, [][]byte{row1.row}, txnB, option)
   116  		assert.NoError(t, err)
   117  	}()
   118  	time.Sleep(time.Second / 2)
   119  	err = l.Lock(context.Background(), row2.tableID, [][]byte{row2.row}, txnA, option)
   120  	assert.Error(t, err)
   121  }