talent-plan-tinykv/kv/raftstore/snap/snap_manager.go
Connor 5e089a2cd1 init course framework
Signed-off-by: Connor <zbk602423539@gmail.com>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: linning <linningde25@gmail.com>
Co-authored-by: YangKeao <keao.yang@yahoo.com>
Co-authored-by: andylokandy <andylokandy@hotmail.com>
Co-authored-by: Iosmanthus Teng <myosmanthustree@gmail.com>
2020-04-30 15:25:07 +08:00

346 lines
8.0 KiB
Go

package snap
import (
"io/ioutil"
"math"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/pingcap-incubator/tinykv/log"
rspb "github.com/pingcap-incubator/tinykv/proto/pkg/raft_serverpb"
"github.com/pingcap/errors"
)
type SnapEntry int
const (
SnapEntryGenerating SnapEntry = 1
SnapEntrySending SnapEntry = 2
SnapEntryReceiving SnapEntry = 3
SnapEntryApplying SnapEntry = 4
)
func (e SnapEntry) String() string {
switch e {
case SnapEntryGenerating:
return "generating"
case SnapEntrySending:
return "sending"
case SnapEntryReceiving:
return "receiving"
case SnapEntryApplying:
return "applying"
}
return "unknown"
}
type SnapStats struct {
ReceivingCount int
SendingCount int
}
type SnapManager struct {
base string
snapSize *int64
registryLock sync.RWMutex
registry map[SnapKey][]SnapEntry
MaxTotalSize uint64
}
func NewSnapManager(path string) *SnapManager {
return new(SnapManagerBuilder).Build(path)
}
func (sm *SnapManager) Init() error {
fi, err := os.Stat(sm.base)
if os.IsNotExist(err) {
err = os.MkdirAll(sm.base, 0600)
if err != nil {
return errors.WithStack(err)
}
return nil
} else if err != nil {
return errors.WithStack(err)
}
if !fi.IsDir() {
return errors.Errorf("%s should be a directory", sm.base)
}
fis, err := ioutil.ReadDir(sm.base)
if err != nil {
return errors.WithStack(err)
}
for _, fi := range fis {
if !fi.IsDir() {
name := fi.Name()
if strings.HasSuffix(name, tmpFileSuffix) {
err = os.Remove(filepath.Join(sm.base, name))
if err != nil {
return errors.WithStack(err)
}
} else if strings.HasSuffix(name, sstFileSuffix) {
atomic.AddInt64(sm.snapSize, fi.Size())
}
}
}
return nil
}
func (sm *SnapManager) ListIdleSnap() ([]SnapKeyWithSending, error) {
fis, err := ioutil.ReadDir(sm.base)
if err != nil {
return nil, errors.WithStack(err)
}
results := make([]SnapKeyWithSending, 0, len(fis))
for _, fi := range fis {
if fi.IsDir() {
continue
}
name := fi.Name()
if !strings.HasSuffix(name, metaFileSuffix) {
continue
}
name = name[:len(name)-len(metaFileSuffix)]
var key SnapKeyWithSending
if strings.HasPrefix(name, snapGenPrefix) {
key.IsSending = true
}
numberStrs := strings.Split(name, "_")
if len(numberStrs) != 4 {
return nil, errors.Errorf("failed to parse file %s", name)
}
key.SnapKey.RegionID, err = strconv.ParseUint(numberStrs[1], 10, 64)
if err != nil {
return nil, errors.WithStack(err)
}
key.SnapKey.Term, err = strconv.ParseUint(numberStrs[2], 10, 64)
if err != nil {
return nil, errors.WithStack(err)
}
key.SnapKey.Index, err = strconv.ParseUint(numberStrs[3], 10, 64)
if err != nil {
return nil, errors.WithStack(err)
}
sm.registryLock.RLock()
_, ok := sm.registry[key.SnapKey]
sm.registryLock.RUnlock()
if ok {
// Skip those registered snapshot.
continue
}
results = append(results, key)
}
sort.Slice(results, func(i, j int) bool {
keyI := &results[i].SnapKey
keyJ := &results[j].SnapKey
if keyI.RegionID == keyJ.RegionID {
if keyI.Term == keyJ.Term {
if keyI.Index == keyJ.Index {
return !results[i].IsSending
}
return keyI.Index < keyJ.Index
}
return keyI.Term < keyJ.Term
}
return keyI.RegionID < keyJ.RegionID
})
return results, nil
}
func (sm *SnapManager) HasRegistered(key SnapKey) bool {
sm.registryLock.RLock()
_, ok := sm.registry[key]
sm.registryLock.RUnlock()
return ok
}
func (sm *SnapManager) GetTotalSnapSize() uint64 {
return uint64(atomic.LoadInt64(sm.snapSize))
}
func (sm *SnapManager) GetSnapshotForBuilding(key SnapKey) (Snapshot, error) {
if sm.GetTotalSnapSize() > sm.MaxTotalSize {
err := sm.deleteOldIdleSnaps()
if err != nil {
return nil, err
}
}
return NewSnapForBuilding(sm.base, key, sm.snapSize, sm)
}
func (sm *SnapManager) deleteOldIdleSnaps() error {
idleSnaps, err := sm.ListIdleSnap()
if err != nil {
return err
}
type snapWithModTime struct {
key SnapKey
snap Snapshot
modTime time.Time
}
snaps := make([]snapWithModTime, 0, len(idleSnaps))
for _, idleSnap := range idleSnaps {
if !idleSnap.IsSending {
continue
}
snap, err := sm.GetSnapshotForSending(idleSnap.SnapKey)
if err != nil {
continue
}
fi, err := snap.Meta()
if err != nil {
return err
}
snaps = append(snaps, snapWithModTime{key: idleSnap.SnapKey, snap: snap, modTime: fi.ModTime()})
}
sort.Slice(snaps, func(i, j int) bool {
return snaps[i].modTime.Before(snaps[j].modTime)
})
for sm.GetTotalSnapSize() > sm.MaxTotalSize {
if len(snaps) == 0 {
return errors.New("too many snapshots")
}
oldest := snaps[0]
snaps = snaps[1:]
sm.DeleteSnapshot(oldest.key, oldest.snap, false)
}
return nil
}
func (sm *SnapManager) GetSnapshotForSending(snapKey SnapKey) (Snapshot, error) {
return NewSnapForSending(sm.base, snapKey, sm.snapSize, sm)
}
func (sm *SnapManager) GetSnapshotForReceiving(snapKey SnapKey, data []byte) (Snapshot, error) {
snapshotData := new(rspb.RaftSnapshotData)
err := snapshotData.Unmarshal(data)
if err != nil {
return nil, errors.WithStack(err)
}
return NewSnapForReceiving(sm.base, snapKey, snapshotData.Meta, sm.snapSize, sm)
}
func (sm *SnapManager) GetSnapshotForApplying(snapKey SnapKey) (Snapshot, error) {
snap, err := NewSnapForApplying(sm.base, snapKey, sm.snapSize, sm)
if err != nil {
return nil, err
}
if !snap.Exists() {
return nil, errors.Errorf("snapshot of %s not exists", snapKey)
}
return snap, nil
}
func (sm *SnapManager) Register(key SnapKey, entry SnapEntry) {
log.Debugf("register key:%s, entry:%d", key, entry)
sm.registryLock.Lock()
defer sm.registryLock.Unlock()
entries, ok := sm.registry[key]
if ok {
for _, e := range entries {
if e == entry {
log.Warnf("%s is registered more than 1 time", key)
return
}
}
}
entries = append(entries, entry)
sm.registry[key] = entries
}
func (sm *SnapManager) Deregister(key SnapKey, entry SnapEntry) {
log.Debugf("deregister key:%s, entry:%s", key, entry)
sm.registryLock.Lock()
defer sm.registryLock.Unlock()
var handled bool
entries, ok := sm.registry[key]
if ok {
for i, e := range entries {
if e == entry {
entries = append(entries[:i], entries[i+1:]...)
handled = true
break
}
}
if handled {
if len(entries) > 0 {
sm.registry[key] = entries
} else {
delete(sm.registry, key)
}
return
}
}
log.Warnf("stale deregister key:%s, entry:%s", key, entry)
}
func (sm *SnapManager) Stats() SnapStats {
sm.registryLock.RLock()
defer sm.registryLock.RUnlock()
var sendingCount, receivingCount int
for _, entries := range sm.registry {
var isSending, isReceiving bool
for _, entry := range entries {
switch entry {
case SnapEntryGenerating, SnapEntrySending:
isSending = true
case SnapEntryReceiving, SnapEntryApplying:
isReceiving = true
}
}
if isSending {
sendingCount++
}
if isReceiving {
receivingCount++
}
}
return SnapStats{SendingCount: sendingCount, ReceivingCount: receivingCount}
}
func (sm *SnapManager) DeleteSnapshot(key SnapKey, snapshot Snapshot, checkEntry bool) bool {
sm.registryLock.Lock()
defer sm.registryLock.Unlock()
if checkEntry {
if e, ok := sm.registry[key]; ok {
if len(e) > 0 {
log.Infof("skip to delete %s since it's registered more than 1, registered entries %v",
snapshot.Path(), e)
return false
}
}
} else if _, ok := sm.registry[key]; ok {
log.Infof("skip to delete %s since it's registered.", snapshot.Path())
return false
}
snapshot.Delete()
return true
}
type SnapManagerBuilder struct {
maxTotalSize uint64
}
func (smb *SnapManagerBuilder) MaxTotalSize(v uint64) *SnapManagerBuilder {
smb.maxTotalSize = v
return smb
}
func (smb *SnapManagerBuilder) Build(path string) *SnapManager {
var maxTotalSize uint64 = math.MaxUint64
if smb.maxTotalSize > 0 {
maxTotalSize = smb.maxTotalSize
}
return &SnapManager{
base: path,
snapSize: new(int64),
registry: map[SnapKey][]SnapEntry{},
MaxTotalSize: maxTotalSize,
}
}