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  }