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 }