Skip to content

Commit ed15ad5

Browse files
authored
executor: avoid concurrently update for SimpleLRUCache in ApplyCache (#50356) (#50388)
close #50347
1 parent 102ab31 commit ed15ad5

File tree

2 files changed

+66
-1
lines changed

2 files changed

+66
-1
lines changed

executor/apply_cache.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ func (c *applyCache) put(key applyCacheKey, val kvcache.Value) {
6767
c.cache.Put(key, val)
6868
}
6969

70+
func (c *applyCache) removeOldest() (kvcache.Key, kvcache.Value, bool) {
71+
c.lock.Lock()
72+
defer c.lock.Unlock()
73+
return c.cache.RemoveOldest()
74+
}
75+
7076
// Get gets a cache item according to cache key. It's thread-safe.
7177
func (c *applyCache) Get(key applyCacheKey) (*chunk.List, error) {
7278
value, hit := c.get(key)
@@ -84,7 +90,7 @@ func (c *applyCache) Set(key applyCacheKey, value *chunk.List) (bool, error) {
8490
return false, nil
8591
}
8692
for mem+c.memTracker.BytesConsumed() > c.memCapacity {
87-
evictedKey, evictedValue, evicted := c.cache.RemoveOldest()
93+
evictedKey, evictedValue, evicted := c.removeOldest()
8894
if !evicted {
8995
return false, nil
9096
}

executor/apply_cache_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package executor
1717
import (
1818
"strconv"
1919
"strings"
20+
"sync"
2021
"testing"
2122

2223
"github.com/pingcap/tidb/parser/mysql"
@@ -77,3 +78,61 @@ func TestApplyCache(t *testing.T) {
7778
require.NoError(t, err)
7879
require.Nil(t, result)
7980
}
81+
82+
func TestApplyCacheConcurrent(t *testing.T) {
83+
ctx := mock.NewContext()
84+
ctx.GetSessionVars().MemQuotaApplyCache = 100
85+
applyCache, err := newApplyCache(ctx)
86+
require.NoError(t, err)
87+
88+
fields := []*types.FieldType{types.NewFieldType(mysql.TypeLonglong)}
89+
value := make([]*chunk.List, 2)
90+
key := make([][]byte, 2)
91+
for i := 0; i < 2; i++ {
92+
value[i] = chunk.NewList(fields, 1, 1)
93+
srcChunk := chunk.NewChunkWithCapacity(fields, 1)
94+
srcChunk.AppendInt64(0, int64(i))
95+
srcRow := srcChunk.GetRow(0)
96+
value[i].AppendRow(srcRow)
97+
key[i] = []byte(strings.Repeat(strconv.Itoa(i), 100))
98+
99+
// TODO: *chunk.List.GetMemTracker().BytesConsumed() is not accurate, fix it later.
100+
require.Equal(t, int64(100), applyCacheKVMem(key[i], value[i]))
101+
}
102+
103+
applyCache.Set(key[0], value[0])
104+
var wg sync.WaitGroup
105+
wg.Add(2)
106+
var func1 = func() {
107+
for i := 0; i < 100; i++ {
108+
for {
109+
result, err := applyCache.Get(key[0])
110+
require.NoError(t, err)
111+
if result != nil {
112+
applyCache.Set(key[1], value[1])
113+
break
114+
}
115+
}
116+
}
117+
wg.Done()
118+
}
119+
var func2 = func() {
120+
for i := 0; i < 100; i++ {
121+
for {
122+
result, err := applyCache.Get(key[1])
123+
require.NoError(t, err)
124+
if result != nil {
125+
applyCache.Set(key[0], value[0])
126+
break
127+
}
128+
}
129+
}
130+
wg.Done()
131+
}
132+
go func1()
133+
go func2()
134+
wg.Wait()
135+
result, err := applyCache.Get(key[0])
136+
require.NoError(t, err)
137+
require.NotNil(t, result)
138+
}

0 commit comments

Comments
 (0)