github.com/dolthub/go-mysql-server@v0.18.0/sql/lock_subsystem_test.go (about) 1 // Copyright 2020-2021 Dolthub, 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 // 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 sql 16 17 import ( 18 "math/rand" 19 "sync" 20 "testing" 21 "time" 22 23 "github.com/stretchr/testify/assert" 24 ) 25 26 const testLockName = "my_lock" 27 28 type lockDiffs struct { 29 missing []string 30 extra []string 31 } 32 33 func getLockDiffs(ctx *Context, testLockNames ...string) *lockDiffs { 34 var missing []string 35 var extra []string 36 37 locks := make(map[string]bool) 38 for _, name := range testLockNames { 39 locks[name] = true 40 } 41 42 userLocks := make(map[string]bool) 43 _ = ctx.IterLocks(func(name string) error { 44 userLocks[name] = true 45 46 if !locks[name] { 47 extra = append(extra, name) 48 } 49 return nil 50 }) 51 52 for name := range locks { 53 if !userLocks[name] { 54 missing = append(missing, name) 55 } 56 } 57 58 if len(missing) == 0 && len(extra) == 0 { 59 return nil 60 } 61 62 return &lockDiffs{missing: missing, extra: extra} 63 } 64 65 func TestLock(t *testing.T) { 66 ls := NewLockSubsystem() 67 user1Ctx := NewEmptyContext() 68 user2Ctx := NewEmptyContext() 69 70 err := ls.Lock(user1Ctx, testLockName, 0) 71 assert.NoError(t, err) 72 assert.Nil(t, getLockDiffs(user1Ctx, testLockName)) 73 74 err = ls.Lock(user1Ctx, testLockName, 0) 75 assert.NoError(t, err) 76 assert.Nil(t, getLockDiffs(user1Ctx, testLockName)) 77 78 err = ls.Lock(user2Ctx, testLockName, 0) 79 assert.Error(t, err) 80 assert.Nil(t, getLockDiffs(user2Ctx)) 81 82 err = ls.Unlock(user1Ctx, testLockName) 83 assert.NoError(t, err) 84 assert.Nil(t, getLockDiffs(user1Ctx, testLockName)) 85 86 err = ls.Lock(user2Ctx, testLockName, 0) 87 assert.Error(t, err) 88 assert.Nil(t, getLockDiffs(user2Ctx)) 89 90 err = ls.Unlock(user1Ctx, testLockName) 91 assert.NoError(t, err) 92 assert.Nil(t, getLockDiffs(user1Ctx)) 93 94 err = ls.Lock(user1Ctx, testLockName, 0) 95 assert.NoError(t, err) 96 assert.Nil(t, getLockDiffs(user1Ctx, testLockName)) 97 98 err = ls.Lock(user2Ctx, testLockName, 0) 99 assert.Error(t, err) 100 assert.Nil(t, getLockDiffs(user2Ctx)) 101 } 102 103 func TestRace(t *testing.T) { 104 const numGoRoutines = 8 105 106 ls := NewLockSubsystem() 107 wg := &sync.WaitGroup{} 108 for i := 0; i < numGoRoutines; i++ { 109 wg.Add(1) 110 go func(ctx *Context) { 111 defer wg.Done() 112 113 err := ls.Lock(ctx, testLockName, -1) 114 assert.NoError(t, err) 115 assert.Nil(t, getLockDiffs(ctx, testLockName)) 116 117 defer func() { 118 err := ls.Unlock(ctx, testLockName) 119 assert.NoError(t, err) 120 assert.Nil(t, getLockDiffs(ctx)) 121 }() 122 123 err = ls.Lock(ctx, testLockName, -1) 124 assert.NoError(t, err) 125 assert.Nil(t, getLockDiffs(ctx, testLockName)) 126 127 defer func() { 128 err := ls.Unlock(ctx, testLockName) 129 assert.NoError(t, err) 130 assert.Nil(t, getLockDiffs(ctx, testLockName)) 131 }() 132 133 time.Sleep(time.Duration(rand.Int63n(int64(time.Millisecond)))) 134 }(NewEmptyContext()) 135 } 136 137 wg.Wait() 138 } 139 140 func TestTimeout(t *testing.T) { 141 ls := NewLockSubsystem() 142 user1 := NewEmptyContext() 143 user2 := NewEmptyContext() 144 145 err := ls.Lock(user1, testLockName, 0) 146 assert.NoError(t, err) 147 assert.Nil(t, getLockDiffs(user1, testLockName)) 148 149 err = ls.Lock(user2, testLockName, time.Millisecond) 150 assert.True(t, ErrLockTimeout.Is(err)) 151 assert.Nil(t, getLockDiffs(user2)) 152 } 153 154 func TestErrLockNotOwned(t *testing.T) { 155 user1 := NewEmptyContext() 156 user2 := NewEmptyContext() 157 ls := NewLockSubsystem() 158 159 err := ls.Lock(user1, testLockName, 0) 160 assert.NoError(t, err) 161 assert.Nil(t, getLockDiffs(user1, testLockName)) 162 assert.Nil(t, getLockDiffs(user2)) 163 164 err = ls.Unlock(user2, testLockName) 165 assert.True(t, ErrLockNotOwned.Is(err)) 166 assert.Nil(t, getLockDiffs(user1, testLockName)) 167 assert.Nil(t, getLockDiffs(user2)) 168 } 169 170 func TestGetLockState(t *testing.T) { 171 user1 := NewEmptyContext() 172 ls := NewLockSubsystem() 173 174 state, owner := ls.GetLockState(testLockName) 175 assert.Equal(t, LockDoesNotExist, state) 176 assert.Equal(t, uint32(0), owner) 177 178 err := ls.Lock(user1, testLockName, 0) 179 assert.NoError(t, err) 180 state, owner = ls.GetLockState(testLockName) 181 assert.Equal(t, LockInUse, state) 182 assert.Equal(t, user1.Session.ID(), owner) 183 184 err = ls.Unlock(user1, testLockName) 185 assert.NoError(t, err) 186 state, owner = ls.GetLockState(testLockName) 187 assert.Equal(t, LockFree, state) 188 assert.Equal(t, uint32(0), owner) 189 }