-
Notifications
You must be signed in to change notification settings - Fork 222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
region cache: batch find regions by key ranges from cache #1410
Conversation
internal/locate/sorted_btree.go
Outdated
f := func(item *btreeItem) bool { | ||
region := item.cachedRegion | ||
if len(startKey) > 0 && bytes.Compare(region.StartKey(), startKey) <= 0 { | ||
if !region.checkRegionCacheTTL(time.Now().Unix()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move time.Now().Unix()
out of the check loop?
internal/locate/region_cache.go
Outdated
var regions []*Region | ||
c.mu.RLock() | ||
defer c.mu.RUnlock() | ||
regions = c.mu.sorted.AscendGreaterOrEqual(startKey, endKey, limit) | ||
regions = c.mu.sorted.DescendLessOrEqual(startKey, endKey) | ||
slices.Reverse(regions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just use ascend scan instead of using descend scan and revert it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just use ascend scan maybe lost the first region. For example, search range [b,d) from region [a, c) [c, e), use ascend scan can only get region [c, e).
internal/locate/sorted_btree.go
Outdated
regions = append(regions, region) | ||
return false | ||
} | ||
if !region.checkRegionCacheTTL(time.Now().Unix()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is also a now~
internal/locate/sorted_btree.go
Outdated
} | ||
|
||
// DescendLess returns all items that are less than or equal to the key. | ||
func (s *SortedRegions) DescendLessOrEqual(startKey, endKey []byte) (regions []*Region) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another question, why or-equal for this location func? IMO, endKey
should be excluded bound.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTree doesn't provide the function DescendLess...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little concened here that this function holds a global lock, if it takes too long time, maybe this sql will affect the other sqls, especially the oltp queries.
internal/locate/region_cache.go
Outdated
} | ||
hit := false | ||
for _, r := range batchRegionInCache { | ||
if !r.Contains(keyRange.StartKey) { // uncached hole, load the rest regions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this code. If [keyRange.StartKey, keyRange.Endkey)
contains multiple regions, in my understanding only the first region can contain keyRange.StartKey
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After processing a region, I set keyRange.StartKey = r.endkey(). So the next region also can contain the keyRange.StartKey.
internal/locate/sorted_btree.go
Outdated
} | ||
|
||
// DescendLess returns all items that are less than or equal to the key. | ||
func (s *SortedRegions) DescendLessOrEqual(startKey, endKey []byte) (regions []*Region) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little concened here that this function holds a global lock, if it takes too long time, maybe this sql will affect the other sqls, especially the oltp queries.
add benchmark Signed-off-by: wshwsh12 <[email protected]>
Signed-off-by: wshwsh12 <[email protected]>
Signed-off-by: wshwsh12 <[email protected]>
Signed-off-by: wshwsh12 <[email protected]>
internal/locate/region_cache.go
Outdated
} | ||
keyRange.StartKey = r.EndKey() | ||
// Batch load rest regions from Cache. | ||
batchSize := 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you can use defaultRegionsPerBatch
as the batchSize?
internal/locate/region_cache.go
Outdated
keyRange.StartKey = r.EndKey() | ||
} | ||
if len(batchRegionInCache) < batchSize { // region cache miss, load the rest regions | ||
break outer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks like just use break
here is ok
internal/locate/region_cache.go
Outdated
@@ -2078,7 +2105,8 @@ func (c *RegionCache) loadRegionByID(bo *retry.Backoffer, regionID uint64) (*Reg | |||
} | |||
} | |||
|
|||
// TODO(youjiali1995): for optimizing BatchLoadRegionsWithKeyRange, not used now. | |||
// For optimizing BatchLocateKeyRanges, scanRegionsFromCache scans at most `limit` regions from cache. | |||
// The first region's startKey is `startKey`, all regions returned are continuous and have no hole. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe you need to emphasize that it is the caller's responsibility to make sure that startKey
is a node in the B-tree, otherwise, the startKey
will not be included in the return regions?
Signed-off-by: wshwsh12 <[email protected]>
internal/locate/sorted_btree.go
Outdated
if !region.checkRegionCacheTTL(now) { | ||
return false | ||
} | ||
if len(lastStartKey) > 0 && !region.Contains(lastStartKey) { // uncached hole |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when will len(lastStartKey) == 0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some test cases have the key, for -inf to +inf.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The case len(lastStartKey) == 0
should never happen in current usage(start key is assigned here, and it's never empty if the code runs here).
When len(lastStartKey) == 0
and the first region is not start from -inf, we should stop scan regions, see the following test.
func (s *testRegionRequestToSingleStoreSuite) TestRegionCacheStartNonEmpty() {
_ = s.cache.refreshRegionIndex(s.bo)
r, _ := s.cache.scanRegionsFromCache(s.bo, []byte{}, nil, 10)
s.Equal(len(r), 1)
region, _ := s.cache.LocateRegionByID(s.bo, s.region)
v2 := region.Region.confVer + 1
r2 := metapb.Region{Id: region.Region.id, RegionEpoch: &metapb.RegionEpoch{Version: region.Region.ver, ConfVer: v2}, StartKey: []byte{1}}
st := newUninitializedStore(s.store)
s.cache.mu.Lock()
s.cache.mu.sorted.Clear()
s.cache.mu.Unlock()
// region cache after clear: []
s.cache.insertRegionToCache(&Region{meta: &r2, store: unsafe.Pointer(st), ttl: nextTTLWithoutJitter(time.Now().Unix())}, true, true)
// region cache after insert: [[1, +inf)]
r, _ = s.cache.scanRegionsFromCache(s.bo, []byte{}, nil, 10)
s.Equal(len(r), 0) // fail in current implementation
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I add the test TestRegionCacheStartNonEmpty
and remove the len(lastStartKey) == 0
check. Now the code stop scan regions when len(lastStartKey) == 0
and the first region is not start from -inf. PTAL again.
Signed-off-by: wshwsh12 <[email protected]>
lgtm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rest LGTM
internal/locate/region_cache_test.go
Outdated
// region cache after insert: [[1, +inf)] | ||
|
||
r, _ = s.cache.scanRegionsFromCache(s.bo, []byte{}, nil, 10) | ||
s.Equal(len(r), 0) // fail in current implementation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove the comment // fail ...
@@ -2259,13 +2260,35 @@ func (s *testRegionRequestToSingleStoreSuite) TestRefreshCache() { | |||
s.cache.insertRegionToCache(&Region{meta: &r2, store: unsafe.Pointer(st), ttl: nextTTLWithoutJitter(time.Now().Unix())}, true, true) | |||
|
|||
r, _ = s.cache.scanRegionsFromCache(s.bo, []byte{}, nil, 10) | |||
s.Equal(len(r), 2) | |||
s.Equal(len(r), 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not related to this PR I think.
Since region cache doesn't remove the first intersected region(it scan intersected region by AscendGreaterOrEqual
), the outdated region (-inf, inf) is still alive and the new inserted valid region [{1}, inf) is ignored because the first seen region (-inf, inf) contains all the required ranges... We finally got outdated region though we know some valid regions.
But this won't affect finding region by single key(it use descend scan).
I don't know if removing all intersected regions is a good idea, what about adding some comments explain it in this test?
Signed-off-by: wshwsh12 <[email protected]>
Currently, client-go only supports batch reading of region information from pd, but not from region cache.
If there is a large table with 200k regions, even if all regions are in memory, it still takes 0.8-0.9s to read all regions.
This PR optimizes the querying times to the B-tree, the time consumption reduces by about 40%.