-
Notifications
You must be signed in to change notification settings - Fork 728
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
server, core: implement the query region gRPC server #8979
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,7 @@ | |
const ( | ||
randomRegionMaxRetry = 10 | ||
scanRegionLimit = 1000 | ||
batchSearchSize = 16 | ||
// CollectFactor is the factor to collect the count of region. | ||
CollectFactor = 0.9 | ||
) | ||
|
@@ -1464,6 +1465,122 @@ | |
return regions | ||
} | ||
|
||
// TODO: benchmark the performance of `QueryRegions`. | ||
// QueryRegions searches RegionInfo from regionTree by keys and IDs in batch. | ||
func (r *RegionsInfo) QueryRegions( | ||
keys, prevKeys [][]byte, ids []uint64, needBuckets bool, | ||
) ([]uint64, []uint64, map[uint64]*pdpb.RegionResponse) { | ||
// Iterate the region keys to find the regions. | ||
regions := r.getRegionsByKeys(keys) | ||
// Assert the returned regions count matches the input keys. | ||
if len(regions) != len(keys) { | ||
panic("returned regions count mismatch with the input keys") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we return an error instead of panic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea is to set such panics at the initial stage of implementing this feature to help with testing. Once the feature needs to be released, I will change them to regular errors. |
||
} | ||
// Iterate the prevKeys to find the regions. | ||
prevRegions := r.getRegionsByPrevKeys(prevKeys) | ||
// Assert the returned regions count matches the input keys. | ||
if len(prevRegions) != len(prevKeys) { | ||
panic("returned prev regions count mismatch with the input keys") | ||
} | ||
// Build the key -> ID map for the final results. | ||
regionsByID := make(map[uint64]*pdpb.RegionResponse, len(regions)) | ||
keyIDMap := sortOutKeyIDMap(regionsByID, regions, needBuckets) | ||
prevKeyIDMap := sortOutKeyIDMap(regionsByID, prevRegions, needBuckets) | ||
// Iterate the region IDs to find the regions. | ||
for _, id := range ids { | ||
// Check if the region has been found. | ||
if regionFound, ok := regionsByID[id]; (ok && regionFound != nil) || id == 0 { | ||
continue | ||
} | ||
// If the given region ID is not found in the region tree, set the region to nil. | ||
if region := r.GetRegion(id); region == nil { | ||
regionsByID[id] = nil | ||
} else { | ||
regionResp := &pdpb.RegionResponse{ | ||
Region: region.GetMeta(), | ||
Leader: region.GetLeader(), | ||
DownPeers: region.GetDownPeers(), | ||
PendingPeers: region.GetPendingPeers(), | ||
} | ||
if needBuckets { | ||
regionResp.Buckets = region.GetBuckets() | ||
} | ||
regionsByID[id] = regionResp | ||
} | ||
} | ||
return keyIDMap, prevKeyIDMap, regionsByID | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What are those maps used for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It helps the client map the keys passed in with their corresponding region IDs. |
||
} | ||
|
||
// getRegionsByKeys searches RegionInfo from regionTree by keys. | ||
func (r *RegionsInfo) getRegionsByKeys(keys [][]byte) []*RegionInfo { | ||
regions := make([]*RegionInfo, 0, len(keys)) | ||
// Split the keys into multiple batches, and search each batch separately. | ||
// This is to avoid the lock contention on the `regionTree`. | ||
for _, batch := range splitKeysIntoBatches(keys) { | ||
r.t.RLock() | ||
results := r.tree.searchByKeys(batch) | ||
r.t.RUnlock() | ||
regions = append(regions, results...) | ||
} | ||
return regions | ||
} | ||
|
||
func splitKeysIntoBatches(keys [][]byte) [][][]byte { | ||
keysLen := len(keys) | ||
batches := make([][][]byte, 0, (keysLen+batchSearchSize-1)/batchSearchSize) | ||
for i := 0; i < keysLen; i += batchSearchSize { | ||
end := i + batchSearchSize | ||
if end > keysLen { | ||
end = keysLen | ||
} | ||
batches = append(batches, keys[i:end]) | ||
} | ||
return batches | ||
} | ||
|
||
func (r *RegionsInfo) getRegionsByPrevKeys(prevKeys [][]byte) []*RegionInfo { | ||
regions := make([]*RegionInfo, 0, len(prevKeys)) | ||
for _, batch := range splitKeysIntoBatches(prevKeys) { | ||
r.t.RLock() | ||
results := r.tree.searchByPrevKeys(batch) | ||
r.t.RUnlock() | ||
regions = append(regions, results...) | ||
} | ||
return regions | ||
} | ||
|
||
// sortOutKeyIDMap will iterate the regions, convert it to a slice of regionID that corresponds to the input regions. | ||
// It will also update `regionsByID` with the regionID and regionResponse. | ||
func sortOutKeyIDMap( | ||
regionsByID map[uint64]*pdpb.RegionResponse, regions []*RegionInfo, needBuckets bool, | ||
) []uint64 { | ||
keyIDMap := make([]uint64, len(regions)) | ||
for idx, region := range regions { | ||
regionID := region.GetMeta().GetId() | ||
keyIDMap[idx] = regionID | ||
// Check if the region has been found. | ||
if regionFound, ok := regionsByID[regionID]; (ok && regionFound != nil) || regionID == 0 { | ||
continue | ||
} | ||
// If the given key is not found in the region tree, set the region to nil. | ||
if region == nil { | ||
regionsByID[regionID] = nil | ||
} else { | ||
regionResp := &pdpb.RegionResponse{ | ||
Region: region.GetMeta(), | ||
Leader: region.GetLeader(), | ||
DownPeers: region.GetDownPeers(), | ||
PendingPeers: region.GetPendingPeers(), | ||
} | ||
if needBuckets { | ||
regionResp.Buckets = region.GetBuckets() | ||
} | ||
regionsByID[regionID] = regionResp | ||
} | ||
} | ||
return keyIDMap | ||
} | ||
|
||
// SubTreeRegionType is the type of sub tree region. | ||
type SubTreeRegionType string | ||
|
||
|
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.
Will we pass both keys and prevKeys at the same time?
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.
Yes, if the
GetRegion
andGetPrevRegion
are batched in the same request.