mirror of https://github.com/tikv/client-go.git
164 lines
4.7 KiB
Go
164 lines
4.7 KiB
Go
// Copyright 2021 TiKV Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// NOTE: The code in this file is based on code from the
|
|
// TiDB project, licensed under the Apache License v 2.0
|
|
//
|
|
// https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/latch/latch_test.go
|
|
//
|
|
|
|
// Copyright 2018 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package latch
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/tikv/client-go/v2/oracle"
|
|
"go.uber.org/atomic"
|
|
)
|
|
|
|
var tso atomic.Uint64
|
|
|
|
func getTso() uint64 {
|
|
return tso.Inc()
|
|
}
|
|
|
|
func newLock(latches *Latches, keys [][]byte) (startTs uint64, lock *Lock) {
|
|
startTs = getTso()
|
|
lock = latches.genLock(startTs, keys)
|
|
return
|
|
}
|
|
|
|
func TestWakeUp(t *testing.T) {
|
|
latches := NewLatches(256)
|
|
|
|
keysA := [][]byte{[]byte("a"), []byte("b"), []byte("c")}
|
|
_, lockA := newLock(latches, keysA)
|
|
|
|
keysB := [][]byte{[]byte("d"), []byte("e"), []byte("a"), []byte("c")}
|
|
startTSB, lockB := newLock(latches, keysB)
|
|
|
|
// A acquire lock success.
|
|
result := latches.acquire(lockA)
|
|
assert.Equal(t, acquireSuccess, result)
|
|
|
|
// B acquire lock failed.
|
|
result = latches.acquire(lockB)
|
|
assert.Equal(t, acquireLocked, result)
|
|
|
|
// A release lock, and get wakeup list.
|
|
commitTSA := getTso()
|
|
wakeupList := make([]*Lock, 0)
|
|
lockA.SetCommitTS(commitTSA)
|
|
wakeupList = latches.release(lockA, wakeupList)
|
|
assert.Equal(t, startTSB, wakeupList[0].startTS)
|
|
|
|
// B acquire failed since startTSB has stale for some keys.
|
|
result = latches.acquire(lockB)
|
|
assert.Equal(t, acquireStale, result)
|
|
|
|
// B release lock since it received a stale.
|
|
wakeupList = latches.release(lockB, wakeupList)
|
|
assert.Len(t, wakeupList, 0)
|
|
|
|
// B restart:get a new startTS.
|
|
startTSB = getTso()
|
|
lockB = latches.genLock(startTSB, keysB)
|
|
result = latches.acquire(lockB)
|
|
assert.Equal(t, acquireSuccess, result)
|
|
}
|
|
|
|
func TestFirstAcquireFailedWithStale(t *testing.T) {
|
|
latches := NewLatches(256)
|
|
|
|
keys := [][]byte{[]byte("a"), []byte("b"), []byte("c")}
|
|
_, lockA := newLock(latches, keys)
|
|
startTSB, lockB := newLock(latches, keys)
|
|
|
|
// acquire lockA success
|
|
result := latches.acquire(lockA)
|
|
assert.Equal(t, acquireSuccess, result)
|
|
|
|
// release lockA
|
|
commitTSA := getTso()
|
|
wakeupList := make([]*Lock, 0)
|
|
lockA.SetCommitTS(commitTSA)
|
|
latches.release(lockA, wakeupList)
|
|
assert.Greater(t, commitTSA, startTSB)
|
|
|
|
// acquire lockB first time, should be failed with stale since commitTSA > startTSB
|
|
result = latches.acquire(lockB)
|
|
assert.Equal(t, acquireStale, result)
|
|
latches.release(lockB, wakeupList)
|
|
}
|
|
|
|
func TestRecycle(t *testing.T) {
|
|
latches := NewLatches(8)
|
|
now := time.Now()
|
|
startTS := oracle.GoTimeToTS(now)
|
|
lock := latches.genLock(startTS, [][]byte{
|
|
[]byte("a"), []byte("b"),
|
|
})
|
|
lock1 := latches.genLock(startTS, [][]byte{
|
|
[]byte("b"), []byte("c"),
|
|
})
|
|
assert.Equal(t, acquireSuccess, latches.acquire(lock))
|
|
assert.Equal(t, acquireLocked, latches.acquire(lock1))
|
|
lock.SetCommitTS(startTS + 1)
|
|
var wakeupList []*Lock
|
|
latches.release(lock, wakeupList)
|
|
// Release lock will grant latch to lock1 automatically,
|
|
// so release lock1 is called here.
|
|
latches.release(lock1, wakeupList)
|
|
|
|
lock2 := latches.genLock(startTS+3, [][]byte{
|
|
[]byte("b"), []byte("c"),
|
|
})
|
|
assert.Equal(t, acquireSuccess, latches.acquire(lock2))
|
|
wakeupList = wakeupList[:0]
|
|
latches.release(lock2, wakeupList)
|
|
|
|
allEmpty := true
|
|
for i := 0; i < len(latches.slots); i++ {
|
|
latch := &latches.slots[i]
|
|
if latch.queue != nil {
|
|
allEmpty = false
|
|
}
|
|
}
|
|
assert.False(t, allEmpty)
|
|
|
|
currentTS := oracle.GoTimeToTS(now.Add(expireDuration)) + 3
|
|
latches.recycle(currentTS)
|
|
|
|
for i := 0; i < len(latches.slots); i++ {
|
|
latch := &latches.slots[i]
|
|
assert.Nil(t, latch.queue)
|
|
}
|
|
}
|