mirror of
https://github.com/talent-plan/tinykv.git
synced 2024-12-28 05:40:10 +08:00
135 lines
3.7 KiB
Go
135 lines
3.7 KiB
Go
package runner
|
|
|
|
import (
|
|
"encoding/hex"
|
|
|
|
"github.com/Connor1996/badger"
|
|
"github.com/pingcap-incubator/tinykv/kv/config"
|
|
"github.com/pingcap-incubator/tinykv/kv/raftstore/message"
|
|
"github.com/pingcap-incubator/tinykv/kv/raftstore/util"
|
|
"github.com/pingcap-incubator/tinykv/kv/util/codec"
|
|
"github.com/pingcap-incubator/tinykv/kv/util/engine_util"
|
|
"github.com/pingcap-incubator/tinykv/kv/util/worker"
|
|
"github.com/pingcap-incubator/tinykv/log"
|
|
"github.com/pingcap-incubator/tinykv/proto/pkg/metapb"
|
|
)
|
|
|
|
type SplitCheckTask struct {
|
|
Region *metapb.Region
|
|
}
|
|
|
|
type splitCheckHandler struct {
|
|
engine *badger.DB
|
|
router message.RaftRouter
|
|
checker *sizeSplitChecker
|
|
}
|
|
|
|
func NewSplitCheckHandler(engine *badger.DB, router message.RaftRouter, conf *config.Config) *splitCheckHandler {
|
|
runner := &splitCheckHandler{
|
|
engine: engine,
|
|
router: router,
|
|
checker: newSizeSplitChecker(conf.RegionMaxSize, conf.RegionSplitSize),
|
|
}
|
|
return runner
|
|
}
|
|
|
|
/// run checks a region with split checkers to produce split keys and generates split admin command.
|
|
func (r *splitCheckHandler) Handle(t worker.Task) {
|
|
spCheckTask, ok := t.(*SplitCheckTask)
|
|
if !ok {
|
|
log.Errorf("unsupported worker.Task: %+v", t)
|
|
return
|
|
}
|
|
region := spCheckTask.Region
|
|
regionId := region.Id
|
|
log.Debugf("executing split check worker.Task: [regionId: %d, startKey: %s, endKey: %s]", regionId,
|
|
hex.EncodeToString(region.StartKey), hex.EncodeToString(region.EndKey))
|
|
key := r.splitCheck(regionId, region.StartKey, region.EndKey)
|
|
if key != nil {
|
|
_, userKey, err := codec.DecodeBytes(key)
|
|
if err == nil {
|
|
// It's not a raw key.
|
|
// To make sure the keys of same user key locate in one Region, decode and then encode to truncate the timestamp
|
|
key = codec.EncodeBytes(userKey)
|
|
}
|
|
msg := message.Msg{
|
|
Type: message.MsgTypeSplitRegion,
|
|
RegionID: regionId,
|
|
Data: &message.MsgSplitRegion{
|
|
RegionEpoch: region.GetRegionEpoch(),
|
|
SplitKey: key,
|
|
},
|
|
}
|
|
err = r.router.Send(regionId, msg)
|
|
if err != nil {
|
|
log.Warnf("failed to send check result: [regionId: %d, err: %v]", regionId, err)
|
|
}
|
|
} else {
|
|
log.Debugf("no need to send, split key not found: [regionId: %v]", regionId)
|
|
}
|
|
}
|
|
|
|
/// SplitCheck gets the split keys by scanning the range.
|
|
func (r *splitCheckHandler) splitCheck(regionID uint64, startKey, endKey []byte) []byte {
|
|
txn := r.engine.NewTransaction(false)
|
|
defer txn.Discard()
|
|
|
|
r.checker.reset()
|
|
it := engine_util.NewCFIterator(engine_util.CfDefault, txn)
|
|
defer it.Close()
|
|
for it.Seek(startKey); it.Valid(); it.Next() {
|
|
item := it.Item()
|
|
key := item.Key()
|
|
if engine_util.ExceedEndKey(key, endKey) {
|
|
// update region size
|
|
r.router.Send(regionID, message.Msg{
|
|
Type: message.MsgTypeRegionApproximateSize,
|
|
Data: r.checker.currentSize,
|
|
})
|
|
break
|
|
}
|
|
if r.checker.onKv(key, item) {
|
|
break
|
|
}
|
|
}
|
|
return r.checker.getSplitKey()
|
|
}
|
|
|
|
type sizeSplitChecker struct {
|
|
maxSize uint64
|
|
splitSize uint64
|
|
|
|
currentSize uint64
|
|
splitKey []byte
|
|
}
|
|
|
|
func newSizeSplitChecker(maxSize, splitSize uint64) *sizeSplitChecker {
|
|
return &sizeSplitChecker{
|
|
maxSize: maxSize,
|
|
splitSize: splitSize,
|
|
}
|
|
}
|
|
|
|
func (checker *sizeSplitChecker) reset() {
|
|
checker.currentSize = 0
|
|
checker.splitKey = nil
|
|
}
|
|
|
|
func (checker *sizeSplitChecker) onKv(key []byte, item engine_util.DBItem) bool {
|
|
valueSize := uint64(item.ValueSize())
|
|
size := uint64(len(key)) + valueSize
|
|
checker.currentSize += size
|
|
if checker.currentSize > checker.splitSize && checker.splitKey == nil {
|
|
checker.splitKey = util.SafeCopy(key)
|
|
}
|
|
return checker.currentSize > checker.maxSize
|
|
}
|
|
|
|
func (checker *sizeSplitChecker) getSplitKey() []byte {
|
|
// Make sure not to split when less than maxSize for last part
|
|
if checker.currentSize < checker.maxSize {
|
|
checker.splitKey = nil
|
|
}
|
|
return checker.splitKey
|
|
}
|