Repository: pingcap/pd
Pull request #720 (2017-09-04 03:29:30)

server: move RegionInfo to core directory.

siddontang: CI failed

Patch:
diff --git a/server/balancer.go b/server/balancer.go
index 79088cffc..d88f39881 100644
--- a/server/balancer.go
+++ b/server/balancer.go
@@ -207,7 +207,7 @@ func (s *balanceRegionScheduler) Schedule(cluster *clusterInfo) Operator {
 	return op
 }
 
-func (s *balanceRegionScheduler) transferPeer(cluster *clusterInfo, region *RegionInfo, oldPeer *metapb.Peer) Operator {
+func (s *balanceRegionScheduler) transferPeer(cluster *clusterInfo, region *core.RegionInfo, oldPeer *metapb.Peer) Operator {
 	// scoreGuard guarantees that the distinct score will not decrease.
 	stores := cluster.getRegionStores(region)
 	source := cluster.getStore(oldPeer.GetStoreId())
@@ -251,7 +251,7 @@ func newReplicaChecker(opt *scheduleOption, cluster *clusterInfo) *replicaChecke
 	}
 }
 
-func (r *replicaChecker) Check(region *RegionInfo) Operator {
+func (r *replicaChecker) Check(region *core.RegionInfo) Operator {
 	if op := r.checkDownPeer(region); op != nil {
 		return op
 	}
@@ -279,7 +279,7 @@ func (r *replicaChecker) Check(region *RegionInfo) Operator {
 }
 
 // SelectBestPeerToAddReplica returns a new peer that to be used to add a replica.
-func (r *replicaChecker) SelectBestPeerToAddReplica(region *RegionInfo, filters ...Filter) *metapb.Peer {
+func (r *replicaChecker) SelectBestPeerToAddReplica(region *core.RegionInfo, filters ...Filter) *metapb.Peer {
 	storeID, _ := r.SelectBestStoreToAddReplica(region, filters...)
 	if storeID == 0 {
 		return nil
@@ -292,7 +292,7 @@ func (r *replicaChecker) SelectBestPeerToAddReplica(region *RegionInfo, filters
 }
 
 // SelectBestStoreToAddReplica returns the store to add a replica.
-func (r *replicaChecker) SelectBestStoreToAddReplica(region *RegionInfo, filters ...Filter) (uint64, float64) {
+func (r *replicaChecker) SelectBestStoreToAddReplica(region *core.RegionInfo, filters ...Filter) (uint64, float64) {
 	// Add some must have filters.
 	newFilters := []Filter{
 		newStateFilter(r.opt),
@@ -311,7 +311,7 @@ func (r *replicaChecker) SelectBestStoreToAddReplica(region *RegionInfo, filters
 }
 
 // selectWorstPeer returns the worst peer in the region.
-func (r *replicaChecker) selectWorstPeer(region *RegionInfo) (*metapb.Peer, float64) {
+func (r *replicaChecker) selectWorstPeer(region *core.RegionInfo) (*metapb.Peer, float64) {
 	regionStores := r.cluster.getRegionStores(region)
 	selector := newReplicaSelector(regionStores, r.rep, r.filters...)
 	worstStore := selector.SelectSource(regionStores)
@@ -322,14 +322,14 @@ func (r *replicaChecker) selectWorstPeer(region *RegionInfo) (*metapb.Peer, floa
 }
 
 // selectBestReplacement returns the best store to replace the region peer.
-func (r *replicaChecker) selectBestReplacement(region *RegionInfo, peer *metapb.Peer) (uint64, float64) {
+func (r *replicaChecker) selectBestReplacement(region *core.RegionInfo, peer *metapb.Peer) (uint64, float64) {
 	// Get a new region without the peer we are going to replace.
-	newRegion := region.clone()
+	newRegion := region.Clone()
 	newRegion.RemoveStorePeer(peer.GetStoreId())
 	return r.SelectBestStoreToAddReplica(newRegion, newExcludedFilter(nil, region.GetStoreIds()))
 }
 
-func (r *replicaChecker) checkDownPeer(region *RegionInfo) Operator {
+func (r *replicaChecker) checkDownPeer(region *core.RegionInfo) Operator {
 	for _, stats := range region.DownPeers {
 		peer := stats.GetPeer()
 		if peer == nil {
@@ -351,7 +351,7 @@ func (r *replicaChecker) checkDownPeer(region *RegionInfo) Operator {
 	return nil
 }
 
-func (r *replicaChecker) checkOfflinePeer(region *RegionInfo) Operator {
+func (r *replicaChecker) checkOfflinePeer(region *core.RegionInfo) Operator {
 	for _, peer := range region.GetPeers() {
 		store := r.cluster.getStore(peer.GetStoreId())
 		if store == nil {
@@ -377,7 +377,7 @@ func (r *replicaChecker) checkOfflinePeer(region *RegionInfo) Operator {
 	return nil
 }
 
-func (r *replicaChecker) checkBestReplacement(region *RegionInfo) Operator {
+func (r *replicaChecker) checkBestReplacement(region *core.RegionInfo) Operator {
 	oldPeer, oldScore := r.selectWorstPeer(region)
 	if oldPeer == nil {
 		return nil
@@ -543,7 +543,7 @@ func (h *balanceHotRegionScheduler) calcScore(cluster *clusterInfo) {
 	}
 }
 
-func (h *balanceHotRegionScheduler) balanceByPeer(cluster *clusterInfo) (*RegionInfo, *metapb.Peer, *metapb.Peer) {
+func (h *balanceHotRegionScheduler) balanceByPeer(cluster *clusterInfo) (*core.RegionInfo, *metapb.Peer, *metapb.Peer) {
 	var (
 		maxWrittenBytes        uint64
 		srcStoreID             uint64
@@ -616,7 +616,7 @@ func (h *balanceHotRegionScheduler) balanceByPeer(cluster *clusterInfo) (*Region
 	return nil, nil, nil
 }
 
-func (h *balanceHotRegionScheduler) selectDestStoreByPeer(candidateStoreIDs []uint64, srcRegion *RegionInfo, srcStoreID uint64) uint64 {
+func (h *balanceHotRegionScheduler) selectDestStoreByPeer(candidateStoreIDs []uint64, srcRegion *core.RegionInfo, srcStoreID uint64) uint64 {
 	sr := h.statisticsAsPeer[srcStoreID]
 	srcWrittenBytes := sr.WrittenBytes
 	srcHotRegionsCount := sr.RegionsStat.Len()
@@ -670,7 +670,7 @@ func (h *balanceHotRegionScheduler) adjustBalanceLimit(storeID uint64, t Balance
 	h.limit = maxUint64(1, limit)
 }
 
-func (h *balanceHotRegionScheduler) balanceByLeader(cluster *clusterInfo) (*RegionInfo, *metapb.Peer) {
+func (h *balanceHotRegionScheduler) balanceByLeader(cluster *clusterInfo) (*core.RegionInfo, *metapb.Peer) {
 	var (
 		maxWrittenBytes        uint64
 		srcStoreID             uint64
@@ -716,7 +716,7 @@ func (h *balanceHotRegionScheduler) balanceByLeader(cluster *clusterInfo) (*Regi
 	return nil, nil
 }
 
-func (h *balanceHotRegionScheduler) selectDestStoreByLeader(srcRegion *RegionInfo) *metapb.Peer {
+func (h *balanceHotRegionScheduler) selectDestStoreByLeader(srcRegion *core.RegionInfo) *metapb.Peer {
 	sr := h.statisticsAsLeader[srcRegion.Leader.GetStoreId()]
 	srcWrittenBytes := sr.WrittenBytes
 	srcHotRegionsCount := sr.RegionsStat.Len()
diff --git a/server/cache.go b/server/cache.go
index 3fd0aa6d5..ae9cc7eda 100644
--- a/server/cache.go
+++ b/server/cache.go
@@ -125,14 +125,14 @@ func (s *storesInfo) totalWrittenBytes() uint64 {
 	return totalWrittenBytes
 }
 
-// regionMap wraps a map[uint64]*RegionInfo and supports randomly pick a region.
+// regionMap wraps a map[uint64]*core.RegionInfo and supports randomly pick a region.
 type regionMap struct {
 	m   map[uint64]*regionEntry
 	ids []uint64
 }
 
 type regionEntry struct {
-	*RegionInfo
+	*core.RegionInfo
 	pos int
 }
 
@@ -149,7 +149,7 @@ func (rm *regionMap) Len() int {
 	return len(rm.m)
 }
 
-func (rm *regionMap) Get(id uint64) *RegionInfo {
+func (rm *regionMap) Get(id uint64) *core.RegionInfo {
 	if rm == nil {
 		return nil
 	}
@@ -159,7 +159,7 @@ func (rm *regionMap) Get(id uint64) *RegionInfo {
 	return nil
 }
 
-func (rm *regionMap) Put(region *RegionInfo) {
+func (rm *regionMap) Put(region *core.RegionInfo) {
 	if old, ok := rm.m[region.GetId()]; ok {
 		old.RegionInfo = region
 		return
@@ -171,7 +171,7 @@ func (rm *regionMap) Put(region *RegionInfo) {
 	rm.ids = append(rm.ids, region.GetId())
 }
 
-func (rm *regionMap) RandomRegion() *RegionInfo {
+func (rm *regionMap) RandomRegion() *core.RegionInfo {
 	if rm.Len() == 0 {
 		return nil
 	}
@@ -208,22 +208,22 @@ func newRegionsInfo() *regionsInfo {
 	}
 }
 
-func (r *regionsInfo) getRegion(regionID uint64) *RegionInfo {
+func (r *regionsInfo) getRegion(regionID uint64) *core.RegionInfo {
 	region := r.regions.Get(regionID)
 	if region == nil {
 		return nil
 	}
-	return region.clone()
+	return region.Clone()
 }
 
-func (r *regionsInfo) setRegion(region *RegionInfo) {
+func (r *regionsInfo) setRegion(region *core.RegionInfo) {
 	if origin := r.regions.Get(region.GetId()); origin != nil {
 		r.removeRegion(origin)
 	}
 	r.addRegion(region)
 }
 
-func (r *regionsInfo) addRegion(region *RegionInfo) {
+func (r *regionsInfo) addRegion(region *core.RegionInfo) {
 	// Add to tree and regions.
 	r.tree.update(region.Region)
 	r.regions.Put(region)
@@ -255,7 +255,7 @@ func (r *regionsInfo) addRegion(region *RegionInfo) {
 	}
 }
 
-func (r *regionsInfo) removeRegion(region *RegionInfo) {
+func (r *regionsInfo) removeRegion(region *core.RegionInfo) {
 	// Remove from tree and regions.
 	r.tree.remove(region.Region)
 	r.regions.Delete(region.GetId())
@@ -268,7 +268,7 @@ func (r *regionsInfo) removeRegion(region *RegionInfo) {
 	}
 }
 
-func (r *regionsInfo) searchRegion(regionKey []byte) *RegionInfo {
+func (r *regionsInfo) searchRegion(regionKey []byte) *core.RegionInfo {
 	region := r.tree.search(regionKey)
 	if region == nil {
 		return nil
@@ -276,10 +276,10 @@ func (r *regionsInfo) searchRegion(regionKey []byte) *RegionInfo {
 	return r.getRegion(region.GetId())
 }
 
-func (r *regionsInfo) getRegions() []*RegionInfo {
-	regions := make([]*RegionInfo, 0, r.regions.Len())
+func (r *regionsInfo) getRegions() []*core.RegionInfo {
+	regions := make([]*core.RegionInfo, 0, r.regions.Len())
 	for _, region := range r.regions.m {
-		regions = append(regions, region.clone())
+		regions = append(regions, region.Clone())
 	}
 	return regions
 }
@@ -308,28 +308,28 @@ func (r *regionsInfo) getStoreFollowerCount(storeID uint64) int {
 	return r.followers[storeID].Len()
 }
 
-func (r *regionsInfo) randRegion() *RegionInfo {
+func (r *regionsInfo) randRegion() *core.RegionInfo {
 	return randRegion(r.regions)
 }
 
-func (r *regionsInfo) randLeaderRegion(storeID uint64) *RegionInfo {
+func (r *regionsInfo) randLeaderRegion(storeID uint64) *core.RegionInfo {
 	return randRegion(r.leaders[storeID])
 }
 
-func (r *regionsInfo) randFollowerRegion(storeID uint64) *RegionInfo {
+func (r *regionsInfo) randFollowerRegion(storeID uint64) *core.RegionInfo {
 	return randRegion(r.followers[storeID])
 }
 
 const randomRegionMaxRetry = 10
 
-func randRegion(regions *regionMap) *RegionInfo {
+func randRegion(regions *regionMap) *core.RegionInfo {
 	for i := 0; i < randomRegionMaxRetry; i++ {
 		region := regions.RandomRegion()
 		if region == nil {
 			return nil
 		}
 		if len(region.DownPeers) == 0 && len(region.PendingPeers) == 0 {
-			return region.clone()
+			return region.Clone()
 		}
 	}
 	return nil
@@ -493,14 +493,14 @@ func (c *clusterInfo) getStoresWriteStat() map[uint64]uint64 {
 	return res
 }
 
-func (c *clusterInfo) getRegion(regionID uint64) *RegionInfo {
+func (c *clusterInfo) getRegion(regionID uint64) *core.RegionInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.regions.getRegion(regionID)
 }
 
 // updateWriteStatCache updates statistic for a region if it's hot, or remove it from statistics if it cools down
-func (c *clusterInfo) updateWriteStatCache(region *RegionInfo, hotRegionThreshold uint64) {
+func (c *clusterInfo) updateWriteStatCache(region *core.RegionInfo, hotRegionThreshold uint64) {
 	var v *RegionStat
 	key := region.GetId()
 	value, isExist := c.writeStatistics.peek(key)
@@ -543,19 +543,19 @@ func (c *clusterInfo) isRegionHot(id uint64) bool {
 	return false
 }
 
-func (c *clusterInfo) searchRegion(regionKey []byte) *RegionInfo {
+func (c *clusterInfo) searchRegion(regionKey []byte) *core.RegionInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.regions.searchRegion(regionKey)
 }
 
-func (c *clusterInfo) putRegion(region *RegionInfo) error {
+func (c *clusterInfo) putRegion(region *core.RegionInfo) error {
 	c.Lock()
 	defer c.Unlock()
-	return c.putRegionLocked(region.clone())
+	return c.putRegionLocked(region.Clone())
 }
 
-func (c *clusterInfo) putRegionLocked(region *RegionInfo) error {
+func (c *clusterInfo) putRegionLocked(region *core.RegionInfo) error {
 	if c.kv != nil {
 		if err := c.kv.saveRegion(region.Region); err != nil {
 			return errors.Trace(err)
@@ -565,13 +565,13 @@ func (c *clusterInfo) putRegionLocked(region *RegionInfo) error {
 	return nil
 }
 
-func (c *clusterInfo) getRegions() []*RegionInfo {
+func (c *clusterInfo) getRegions() []*core.RegionInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.regions.getRegions()
 }
 
-func (c *clusterInfo) randomRegion() *RegionInfo {
+func (c *clusterInfo) randomRegion() *core.RegionInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.regions.randRegion()
@@ -601,19 +601,19 @@ func (c *clusterInfo) getStoreLeaderCount(storeID uint64) int {
 	return c.regions.getStoreLeaderCount(storeID)
 }
 
-func (c *clusterInfo) randLeaderRegion(storeID uint64) *RegionInfo {
+func (c *clusterInfo) randLeaderRegion(storeID uint64) *core.RegionInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.regions.randLeaderRegion(storeID)
 }
 
-func (c *clusterInfo) randFollowerRegion(storeID uint64) *RegionInfo {
+func (c *clusterInfo) randFollowerRegion(storeID uint64) *core.RegionInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.regions.randFollowerRegion(storeID)
 }
 
-func (c *clusterInfo) getRegionStores(region *RegionInfo) []*core.StoreInfo {
+func (c *clusterInfo) getRegionStores(region *core.RegionInfo) []*core.StoreInfo {
 	c.RLock()
 	defer c.RUnlock()
 	var stores []*core.StoreInfo
@@ -625,13 +625,13 @@ func (c *clusterInfo) getRegionStores(region *RegionInfo) []*core.StoreInfo {
 	return stores
 }
 
-func (c *clusterInfo) getLeaderStore(region *RegionInfo) *core.StoreInfo {
+func (c *clusterInfo) getLeaderStore(region *core.RegionInfo) *core.StoreInfo {
 	c.RLock()
 	defer c.RUnlock()
 	return c.stores.getStore(region.Leader.GetStoreId())
 }
 
-func (c *clusterInfo) getFollowerStores(region *RegionInfo) []*core.StoreInfo {
+func (c *clusterInfo) getFollowerStores(region *core.RegionInfo) []*core.StoreInfo {
 	c.RLock()
 	defer c.RUnlock()
 	var stores []*core.StoreInfo
@@ -673,8 +673,8 @@ func (c *clusterInfo) updateStoreStatus(id uint64) {
 }
 
 // handleRegionHeartbeat updates the region information.
-func (c *clusterInfo) handleRegionHeartbeat(region *RegionInfo) error {
-	region = region.clone()
+func (c *clusterInfo) handleRegionHeartbeat(region *core.RegionInfo) error {
+	region = region.Clone()
 	c.RLock()
 	origin := c.regions.getRegion(region.GetId())
 	c.RUnlock()
@@ -748,7 +748,7 @@ func (c *clusterInfo) handleRegionHeartbeat(region *RegionInfo) error {
 	return nil
 }
 
-func (c *clusterInfo) updateWriteStatus(region *RegionInfo) {
+func (c *clusterInfo) updateWriteStatus(region *core.RegionInfo) {
 	var WrittenBytesPerSec uint64
 	v, isExist := c.writeStatistics.peek(region.GetId())
 	if isExist {
diff --git a/server/cluster.go b/server/cluster.go
index ab68c2d8b..1a78e71f2 100644
--- a/server/cluster.go
+++ b/server/cluster.go
@@ -361,7 +361,7 @@ func (c *RaftCluster) GetRegionByKey(regionKey []byte) (*metapb.Region, *metapb.
 }
 
 // GetRegionInfoByKey gets regionInfo by region key from cluster.
-func (c *RaftCluster) GetRegionInfoByKey(regionKey []byte) *RegionInfo {
+func (c *RaftCluster) GetRegionInfoByKey(regionKey []byte) *core.RegionInfo {
 	return c.cachedCluster.searchRegion(regionKey)
 }
 
@@ -375,7 +375,7 @@ func (c *RaftCluster) GetRegionByID(regionID uint64) (*metapb.Region, *metapb.Pe
 }
 
 // GetRegionInfoByID gets regionInfo by regionID from cluster.
-func (c *RaftCluster) GetRegionInfoByID(regionID uint64) *RegionInfo {
+func (c *RaftCluster) GetRegionInfoByID(regionID uint64) *core.RegionInfo {
 	return c.cachedCluster.getRegion(regionID)
 }
 
diff --git a/server/cluster_worker.go b/server/cluster_worker.go
index 60de46ef2..40d2acaf0 100644
--- a/server/cluster_worker.go
+++ b/server/cluster_worker.go
@@ -21,9 +21,10 @@ import (
 	"github.com/juju/errors"
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
+	"github.com/pingcap/pd/server/core"
 )
 
-func (c *RaftCluster) handleRegionHeartbeat(region *RegionInfo) error {
+func (c *RaftCluster) handleRegionHeartbeat(region *core.RegionInfo) error {
 	// If the region peer count is 0, then we should not handle this.
 	if len(region.GetPeers()) == 0 {
 		log.Warnf("invalid region, zero region peer count - %v", region)
diff --git a/server/coordinator.go b/server/coordinator.go
index 35c43f2fb..de5e0d3a3 100644
--- a/server/coordinator.go
+++ b/server/coordinator.go
@@ -91,7 +91,7 @@ func newCoordinator(cluster *clusterInfo, opt *scheduleOption, hbStreams *heartb
 	}
 }
 
-func (c *coordinator) dispatch(region *RegionInfo) {
+func (c *coordinator) dispatch(region *core.RegionInfo) {
 	// Check existed operator.
 	if op := c.getOperator(region.GetId()); op != nil {
 		res, finished := op.Do(region)
diff --git a/server/core/region.go b/server/core/region.go
new file mode 100644
index 000000000..c596e2131
--- /dev/null
+++ b/server/core/region.go
@@ -0,0 +1,139 @@
+// Copyright 2016 PingCAP, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package core
+
+import (
+	"github.com/gogo/protobuf/proto"
+	"github.com/pingcap/kvproto/pkg/metapb"
+	"github.com/pingcap/kvproto/pkg/pdpb"
+)
+
+// RegionInfo records detail region info.
+type RegionInfo struct {
+	*metapb.Region
+	Leader       *metapb.Peer
+	DownPeers    []*pdpb.PeerStats
+	PendingPeers []*metapb.Peer
+	WrittenBytes uint64
+}
+
+// NewRegionInfo creates RegionInfo with region's meta and leader peer.
+func NewRegionInfo(region *metapb.Region, leader *metapb.Peer) *RegionInfo {
+	return &RegionInfo{
+		Region: region,
+		Leader: leader,
+	}
+}
+
+// Clone returns a copy of current regionInfo.
+func (r *RegionInfo) Clone() *RegionInfo {
+	downPeers := make([]*pdpb.PeerStats, 0, len(r.DownPeers))
+	for _, peer := range r.DownPeers {
+		downPeers = append(downPeers, proto.Clone(peer).(*pdpb.PeerStats))
+	}
+	pendingPeers := make([]*metapb.Peer, 0, len(r.PendingPeers))
+	for _, peer := range r.PendingPeers {
+		pendingPeers = append(pendingPeers, proto.Clone(peer).(*metapb.Peer))
+	}
+	return &RegionInfo{
+		Region:       proto.Clone(r.Region).(*metapb.Region),
+		Leader:       proto.Clone(r.Leader).(*metapb.Peer),
+		DownPeers:    downPeers,
+		PendingPeers: pendingPeers,
+		WrittenBytes: r.WrittenBytes,
+	}
+}
+
+// GetPeer returns the peer with specified peer id.
+func (r *RegionInfo) GetPeer(peerID uint64) *metapb.Peer {
+	for _, peer := range r.GetPeers() {
+		if peer.GetId() == peerID {
+			return peer
+		}
+	}
+	return nil
+}
+
+// GetDownPeer returns the down peers with specified peer id.
+func (r *RegionInfo) GetDownPeer(peerID uint64) *metapb.Peer {
+	for _, down := range r.DownPeers {
+		if down.GetPeer().GetId() == peerID {
+			return down.GetPeer()
+		}
+	}
+	return nil
+}
+
+// GetPendingPeer returns the pending peer with specified peer id.
+func (r *RegionInfo) GetPendingPeer(peerID uint64) *metapb.Peer {
+	for _, peer := range r.PendingPeers {
+		if peer.GetId() == peerID {
+			return peer
+		}
+	}
+	return nil
+}
+
+// GetStorePeer returns the peer in specified store.
+func (r *RegionInfo) GetStorePeer(storeID uint64) *metapb.Peer {
+	for _, peer := range r.GetPeers() {
+		if peer.GetStoreId() == storeID {
+			return peer
+		}
+	}
+	return nil
+}
+
+// RemoveStorePeer removes the peer in specified store.
+func (r *RegionInfo) RemoveStorePeer(storeID uint64) {
+	var peers []*metapb.Peer
+	for _, peer := range r.GetPeers() {
+		if peer.GetStoreId() != storeID {
+			peers = append(peers, peer)
+		}
+	}
+	r.Peers = peers
+}
+
+// GetStoreIds returns a map indicate the region distributed.
+func (r *RegionInfo) GetStoreIds() map[uint64]struct{} {
+	peers := r.GetPeers()
+	stores := make(map[uint64]struct{}, len(peers))
+	for _, peer := range peers {
+		stores[peer.GetStoreId()] = struct{}{}
+	}
+	return stores
+}
+
+// GetFollowers returns a map indicate the follow peers distributed.
+func (r *RegionInfo) GetFollowers() map[uint64]*metapb.Peer {
+	peers := r.GetPeers()
+	followers := make(map[uint64]*metapb.Peer, len(peers))
+	for _, peer := range peers {
+		if r.Leader == nil || r.Leader.GetId() != peer.GetId() {
+			followers[peer.GetStoreId()] = peer
+		}
+	}
+	return followers
+}
+
+// GetFollower randomly returns a follow peer.
+func (r *RegionInfo) GetFollower() *metapb.Peer {
+	for _, peer := range r.GetPeers() {
+		if r.Leader == nil || r.Leader.GetId() != peer.GetId() {
+			return peer
+		}
+	}
+	return nil
+}
diff --git a/server/event.go b/server/event.go
index fc7af89ae..dbbbc7443 100644
--- a/server/event.go
+++ b/server/event.go
@@ -172,6 +172,6 @@ func (op *splitOperator) GetName() string {
 }
 
 // Do implements Operator.Do interface.
-func (op *splitOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
+func (op *splitOperator) Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
 	return nil, true
 }
diff --git a/server/grpc_service.go b/server/grpc_service.go
index d56611ef8..d16fc244e 100644
--- a/server/grpc_service.go
+++ b/server/grpc_service.go
@@ -21,6 +21,7 @@ import (
 	"github.com/juju/errors"
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
+	"github.com/pingcap/pd/server/core"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
@@ -266,7 +267,7 @@ func (s *Server) RegionHeartbeat(server pdpb.PD_RegionHeartbeatServer) error {
 			isNew = false
 		}
 
-		region := newRegionInfo(request.GetRegion(), request.GetLeader())
+		region := core.NewRegionInfo(request.GetRegion(), request.GetLeader())
 		region.DownPeers = request.GetDownPeers()
 		region.PendingPeers = request.GetPendingPeers()
 		region.WrittenBytes = request.GetBytesWritten()
diff --git a/server/heartbeat_streams.go b/server/heartbeat_streams.go
index 076c48949..a4ead995b 100644
--- a/server/heartbeat_streams.go
+++ b/server/heartbeat_streams.go
@@ -18,6 +18,7 @@ import (
 
 	log "github.com/Sirupsen/logrus"
 	"github.com/pingcap/kvproto/pkg/pdpb"
+	"github.com/pingcap/pd/server/core"
 	"golang.org/x/net/context"
 )
 
@@ -97,7 +98,7 @@ func (s *heartbeatStreams) bindStream(storeID uint64, stream heartbeatStream) {
 	}
 }
 
-func (s *heartbeatStreams) sendMsg(region *RegionInfo, msg *pdpb.RegionHeartbeatResponse) {
+func (s *heartbeatStreams) sendMsg(region *core.RegionInfo, msg *pdpb.RegionHeartbeatResponse) {
 	if region.Leader == nil {
 		return
 	}
@@ -113,7 +114,7 @@ func (s *heartbeatStreams) sendMsg(region *RegionInfo, msg *pdpb.RegionHeartbeat
 	}
 }
 
-func (s *heartbeatStreams) sendErr(region *RegionInfo, errType pdpb.ErrorType, errMsg string) {
+func (s *heartbeatStreams) sendErr(region *core.RegionInfo, errType pdpb.ErrorType, errMsg string) {
 	regionHeartbeatCounter.WithLabelValues("report", "err").Inc()
 
 	msg := &pdpb.RegionHeartbeatResponse{
diff --git a/server/kv.go b/server/kv.go
index a4833586c..ea9eaac4c 100644
--- a/server/kv.go
+++ b/server/kv.go
@@ -250,7 +250,7 @@ func (kv *kv) loadRegions(regions *regionsInfo, rangeLimit int64) error {
 			}
 
 			nextID = region.GetId() + 1
-			regions.setRegion(newRegionInfo(region, nil))
+			regions.setRegion(core.NewRegionInfo(region, nil))
 		}
 
 		if len(resp.Kvs) < int(rangeLimit) {
diff --git a/server/operator.go b/server/operator.go
index b33ab95c6..b461eea5d 100644
--- a/server/operator.go
+++ b/server/operator.go
@@ -98,19 +98,19 @@ type Operator interface {
 	GetState() OperatorState
 	SetState(OperatorState)
 	GetName() string
-	Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool)
+	Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool)
 }
 
 type adminOperator struct {
 	sync.RWMutex `json:"-"`
-	Name         string        `json:"name"`
-	Start        time.Time     `json:"start"`
-	Region       *RegionInfo   `json:"region"`
-	Ops          []Operator    `json:"ops"`
-	State        OperatorState `json:"state"`
+	Name         string           `json:"name"`
+	Start        time.Time        `json:"start"`
+	Region       *core.RegionInfo `json:"region"`
+	Ops          []Operator       `json:"ops"`
+	State        OperatorState    `json:"state"`
 }
 
-func newAdminOperator(region *RegionInfo, ops ...Operator) *adminOperator {
+func newAdminOperator(region *core.RegionInfo, ops ...Operator) *adminOperator {
 	return &adminOperator{
 		Name:   "admin_operator",
 		Start:  time.Now(),
@@ -155,11 +155,11 @@ func (op *adminOperator) GetName() string {
 	return op.Name
 }
 
-func (op *adminOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
+func (op *adminOperator) Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
 	op.Lock()
 	defer op.Unlock()
 	// Update region.
-	op.Region = region.clone()
+	op.Region = region.Clone()
 
 	// Do all operators in order.
 	for i := 0; i < len(op.Ops); i++ {
@@ -178,7 +178,7 @@ type regionOperator struct {
 	sync.RWMutex `json:"-"`
 	Name         string            `json:"name"`
 	Start        time.Time         `json:"start"`
-	Region       *RegionInfo       `json:"region"`
+	Region       *core.RegionInfo  `json:"region"`
 	End          time.Time         `json:"end"`
 	Index        int               `json:"index"`
 	Ops          []Operator        `json:"ops"`
@@ -186,7 +186,7 @@ type regionOperator struct {
 	State        OperatorState     `json:"state"`
 }
 
-func newRegionOperator(region *RegionInfo, kind core.ResourceKind, ops ...Operator) *regionOperator {
+func newRegionOperator(region *core.RegionInfo, kind core.ResourceKind, ops ...Operator) *regionOperator {
 	// Do some check here, just fatal because it must be bug.
 	if len(ops) == 0 {
 		log.Fatalf("[region %d] new region operator with no ops", region.GetId())
@@ -240,7 +240,7 @@ func (op *regionOperator) GetName() string {
 	return op.Name
 }
 
-func (op *regionOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
+func (op *regionOperator) Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
 	op.Lock()
 	defer op.Unlock()
 	if time.Since(op.Start) > maxOperatorWaitTime {
@@ -250,7 +250,7 @@ func (op *regionOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse,
 	}
 
 	// Update region.
-	op.Region = region.clone()
+	op.Region = region.Clone()
 
 	// If an operator is not finished, do it.
 	for ; op.Index < len(op.Ops); op.Index++ {
@@ -332,7 +332,7 @@ func (op *changePeerOperator) GetName() string {
 	return op.Name
 }
 
-func (op *changePeerOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
+func (op *changePeerOperator) Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
 	op.Lock()
 	defer op.Unlock()
 	// Check if operator is finished.
@@ -417,7 +417,7 @@ func (op *transferLeaderOperator) GetName() string {
 	return op.Name
 }
 
-func (op *transferLeaderOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
+func (op *transferLeaderOperator) Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
 	op.Lock()
 	defer op.Unlock()
 	// Check if operator is finished.
diff --git a/server/region.go b/server/region.go
index 36ad6485d..5fe110df1 100644
--- a/server/region.go
+++ b/server/region.go
@@ -15,129 +15,10 @@ package server
 import (
 	"bytes"
 
-	"github.com/gogo/protobuf/proto"
 	"github.com/google/btree"
 	"github.com/pingcap/kvproto/pkg/metapb"
-	"github.com/pingcap/kvproto/pkg/pdpb"
 )
 
-// RegionInfo record detail region info
-type RegionInfo struct {
-	*metapb.Region
-	Leader       *metapb.Peer
-	DownPeers    []*pdpb.PeerStats
-	PendingPeers []*metapb.Peer
-	WrittenBytes uint64
-}
-
-func newRegionInfo(region *metapb.Region, leader *metapb.Peer) *RegionInfo {
-	return &RegionInfo{
-		Region: region,
-		Leader: leader,
-	}
-}
-
-func (r *RegionInfo) clone() *RegionInfo {
-	downPeers := make([]*pdpb.PeerStats, 0, len(r.DownPeers))
-	for _, peer := range r.DownPeers {
-		downPeers = append(downPeers, proto.Clone(peer).(*pdpb.PeerStats))
-	}
-	pendingPeers := make([]*metapb.Peer, 0, len(r.PendingPeers))
-	for _, peer := range r.PendingPeers {
-		pendingPeers = append(pendingPeers, proto.Clone(peer).(*metapb.Peer))
-	}
-	return &RegionInfo{
-		Region:       proto.Clone(r.Region).(*metapb.Region),
-		Leader:       proto.Clone(r.Leader).(*metapb.Peer),
-		DownPeers:    downPeers,
-		PendingPeers: pendingPeers,
-		WrittenBytes: r.WrittenBytes,
-	}
-}
-
-// GetPeer return the peer with specified peer id
-func (r *RegionInfo) GetPeer(peerID uint64) *metapb.Peer {
-	for _, peer := range r.GetPeers() {
-		if peer.GetId() == peerID {
-			return peer
-		}
-	}
-	return nil
-}
-
-// GetDownPeer return the down peers with specified peer id
-func (r *RegionInfo) GetDownPeer(peerID uint64) *metapb.Peer {
-	for _, down := range r.DownPeers {
-		if down.GetPeer().GetId() == peerID {
-			return down.GetPeer()
-		}
-	}
-	return nil
-}
-
-// GetPendingPeer return the pending peer with specified peer id
-func (r *RegionInfo) GetPendingPeer(peerID uint64) *metapb.Peer {
-	for _, peer := range r.PendingPeers {
-		if peer.GetId() == peerID {
-			return peer
-		}
-	}
-	return nil
-}
-
-// GetStorePeer return the peer in specified store
-func (r *RegionInfo) GetStorePeer(storeID uint64) *metapb.Peer {
-	for _, peer := range r.GetPeers() {
-		if peer.GetStoreId() == storeID {
-			return peer
-		}
-	}
-	return nil
-}
-
-// RemoveStorePeer remove the peer in specified store
-func (r *RegionInfo) RemoveStorePeer(storeID uint64) {
-	var peers []*metapb.Peer
-	for _, peer := range r.GetPeers() {
-		if peer.GetStoreId() != storeID {
-			peers = append(peers, peer)
-		}
-	}
-	r.Peers = peers
-}
-
-// GetStoreIds return a map indicate the region distributed
-func (r *RegionInfo) GetStoreIds() map[uint64]struct{} {
-	peers := r.GetPeers()
-	stores := make(map[uint64]struct{}, len(peers))
-	for _, peer := range peers {
-		stores[peer.GetStoreId()] = struct{}{}
-	}
-	return stores
-}
-
-// GetFollowers return a map indicate the follow peers distributed
-func (r *RegionInfo) GetFollowers() map[uint64]*metapb.Peer {
-	peers := r.GetPeers()
-	followers := make(map[uint64]*metapb.Peer, len(peers))
-	for _, peer := range peers {
-		if r.Leader == nil || r.Leader.GetId() != peer.GetId() {
-			followers[peer.GetStoreId()] = peer
-		}
-	}
-	return followers
-}
-
-// GetFollower randomly return a follow peer
-func (r *RegionInfo) GetFollower() *metapb.Peer {
-	for _, peer := range r.GetPeers() {
-		if r.Leader == nil || r.Leader.GetId() != peer.GetId() {
-			return peer
-		}
-	}
-	return nil
-}
-
 var _ btree.Item = &regionItem{}
 
 type regionItem struct {
diff --git a/server/scheduler.go b/server/scheduler.go
index 31de0f371..78c854af6 100644
--- a/server/scheduler.go
+++ b/server/scheduler.go
@@ -256,12 +256,12 @@ func (s *shuffleRegionScheduler) Schedule(cluster *clusterInfo) Operator {
 	return newTransferPeer(region, core.RegionKind, oldPeer, newPeer)
 }
 
-func newAddPeer(region *RegionInfo, peer *metapb.Peer) Operator {
+func newAddPeer(region *core.RegionInfo, peer *metapb.Peer) Operator {
 	addPeer := newAddPeerOperator(region.GetId(), peer)
 	return newRegionOperator(region, core.RegionKind, addPeer)
 }
 
-func newRemovePeer(region *RegionInfo, peer *metapb.Peer) Operator {
+func newRemovePeer(region *core.RegionInfo, peer *metapb.Peer) Operator {
 	removePeer := newRemovePeerOperator(region.GetId(), peer)
 	if region.Leader != nil && region.Leader.GetId() == peer.GetId() {
 		if follower := region.GetFollower(); follower != nil {
@@ -273,7 +273,7 @@ func newRemovePeer(region *RegionInfo, peer *metapb.Peer) Operator {
 	return newRegionOperator(region, core.RegionKind, removePeer)
 }
 
-func newTransferPeer(region *RegionInfo, kind core.ResourceKind, oldPeer, newPeer *metapb.Peer) Operator {
+func newTransferPeer(region *core.RegionInfo, kind core.ResourceKind, oldPeer, newPeer *metapb.Peer) Operator {
 	addPeer := newAddPeerOperator(region.GetId(), newPeer)
 	removePeer := newRemovePeerOperator(region.GetId(), oldPeer)
 	if region.Leader != nil && region.Leader.GetId() == oldPeer.GetId() {
@@ -287,12 +287,12 @@ func newTransferPeer(region *RegionInfo, kind core.ResourceKind, oldPeer, newPee
 	return newRegionOperator(region, kind, addPeer, removePeer)
 }
 
-func newPriorityTransferLeader(region *RegionInfo, newLeader *metapb.Peer) Operator {
+func newPriorityTransferLeader(region *core.RegionInfo, newLeader *metapb.Peer) Operator {
 	transferLeader := newTransferLeaderOperator(region.GetId(), region.Leader, newLeader)
 	return newRegionOperator(region, core.PriorityKind, transferLeader)
 }
 
-func newTransferLeader(region *RegionInfo, newLeader *metapb.Peer) Operator {
+func newTransferLeader(region *core.RegionInfo, newLeader *metapb.Peer) Operator {
 	transferLeader := newTransferLeaderOperator(region.GetId(), region.Leader, newLeader)
 	return newRegionOperator(region, core.LeaderKind, transferLeader)
 }
@@ -316,7 +316,7 @@ func scheduleAddPeer(cluster *clusterInfo, s Selector, filters ...Filter) *metap
 }
 
 // scheduleRemovePeer schedules a region to remove the peer.
-func scheduleRemovePeer(cluster *clusterInfo, schedulerName string, s Selector, filters ...Filter) (*RegionInfo, *metapb.Peer) {
+func scheduleRemovePeer(cluster *clusterInfo, schedulerName string, s Selector, filters ...Filter) (*core.RegionInfo, *metapb.Peer) {
 	stores := cluster.getStores()
 
 	source := s.SelectSource(stores, filters...)
@@ -338,7 +338,7 @@ func scheduleRemovePeer(cluster *clusterInfo, schedulerName string, s Selector,
 }
 
 // scheduleTransferLeader schedules a region to transfer leader to the peer.
-func scheduleTransferLeader(cluster *clusterInfo, schedulerName string, s Selector, filters ...Filter) (*RegionInfo, *metapb.Peer) {
+func scheduleTransferLeader(cluster *clusterInfo, schedulerName string, s Selector, filters ...Filter) (*core.RegionInfo, *metapb.Peer) {
 	stores := cluster.getStores()
 	if len(stores) == 0 {
 		schedulerCounter.WithLabelValues(schedulerName, "no_store").Inc()
diff --git a/server/util.go b/server/util.go
index d498a0ba2..8f013dba1 100644
--- a/server/util.go
+++ b/server/util.go
@@ -30,6 +30,7 @@ import (
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
 	"github.com/pingcap/pd/pkg/etcdutil"
+	"github.com/pingcap/pd/server/core"
 	"golang.org/x/net/context"
 )
 
@@ -258,7 +259,7 @@ func minDuration(a, b time.Duration) time.Duration {
 	return b
 }
 
-func diffRegionPeersInfo(origin *RegionInfo, other *RegionInfo) string {
+func diffRegionPeersInfo(origin *core.RegionInfo, other *core.RegionInfo) string {
 	var ret []string
 	for _, a := range origin.Peers {
 		both := false
@@ -287,7 +288,7 @@ func diffRegionPeersInfo(origin *RegionInfo, other *RegionInfo) string {
 	return strings.Join(ret, ",")
 }
 
-func diffRegionKeyInfo(origin *RegionInfo, other *RegionInfo) string {
+func diffRegionKeyInfo(origin *core.RegionInfo, other *core.RegionInfo) string {
 	var ret []string
 	if !bytes.Equal(origin.Region.StartKey, other.Region.StartKey) {
 		originKey := &metapb.Region{StartKey: origin.Region.StartKey}


Test patch:
diff --git a/server/api/region_test.go b/server/api/region_test.go
index a5d91db69..dd1f0d09b 100644
--- a/server/api/region_test.go
+++ b/server/api/region_test.go
@@ -20,6 +20,7 @@ import (
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
 	"github.com/pingcap/pd/server"
+	"github.com/pingcap/pd/server/core"
 	"golang.org/x/net/context"
 )
 
@@ -53,13 +54,13 @@ func (s *testRegionSuite) TearDownSuite(c *C) {
 	s.cleanup()
 }
 
-func newTestRegionInfo(regionID, storeID uint64, start, end []byte) *server.RegionInfo {
+func newTestRegionInfo(regionID, storeID uint64, start, end []byte) *core.RegionInfo {
 	leader := &metapb.Peer{
 		Id:      regionID,
 		StoreId: storeID,
 	}
 
-	return &server.RegionInfo{
+	return &core.RegionInfo{
 		Region: &metapb.Region{
 			Id:       regionID,
 			StartKey: start,
@@ -76,13 +77,13 @@ func (s *testRegionSuite) TestRegion(c *C) {
 	r := newTestRegionInfo(2, 1, []byte("a"), []byte("b"))
 	mustRegionHeartBeat(c, s.regionHeartbeat, s.svr.ClusterID(), r)
 	url := fmt.Sprintf("%s/region/id/%d", s.urlPrefix, r.GetId())
-	r1 := &server.RegionInfo{}
+	r1 := &core.RegionInfo{}
 	err := readJSONWithURL(url, r1)
 	c.Assert(err, IsNil)
 	c.Assert(r1, DeepEquals, r)
 
 	url = fmt.Sprintf("%s/region/key/%s", s.urlPrefix, "a")
-	r2 := &server.RegionInfo{}
+	r2 := &core.RegionInfo{}
 	err = readJSONWithURL(url, r2)
 	c.Assert(err, IsNil)
 	c.Assert(r2, DeepEquals, r)
diff --git a/server/api/server_test.go b/server/api/server_test.go
index 3f821cee1..5d0ef91ff 100644
--- a/server/api/server_test.go
+++ b/server/api/server_test.go
@@ -24,6 +24,7 @@ import (
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
 	"github.com/pingcap/pd/server"
+	"github.com/pingcap/pd/server/core"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
 )
@@ -157,7 +158,7 @@ func mustPutStore(c *C, s *server.Server, store *metapb.Store) {
 	c.Assert(resp.GetHeader().GetError().GetType(), Equals, pdpb.ErrorType_OK)
 }
 
-func mustRegionHeartBeat(c *C, client pdpb.PD_RegionHeartbeatClient, clusterID uint64, region *server.RegionInfo) {
+func mustRegionHeartBeat(c *C, client pdpb.PD_RegionHeartbeatClient, clusterID uint64, region *core.RegionInfo) {
 	req := &pdpb.RegionHeartbeatRequest{
 		Header: newRequestHeader(clusterID),
 		Region: region.Region,
diff --git a/server/balancer_test.go b/server/balancer_test.go
index d01f0f547..ee2b19081 100644
--- a/server/balancer_test.go
+++ b/server/balancer_test.go
@@ -105,7 +105,7 @@ func (c *testClusterInfo) addLeaderRegion(regionID uint64, leaderID uint64, foll
 		peer, _ := c.allocPeer(id)
 		region.Peers = append(region.Peers, peer)
 	}
-	c.putRegion(newRegionInfo(region, leader))
+	c.putRegion(core.NewRegionInfo(region, leader))
 }
 
 func (c *testClusterInfo) LoadRegion(regionID uint64, followerIds ...uint64) {
@@ -116,7 +116,7 @@ func (c *testClusterInfo) LoadRegion(regionID uint64, followerIds ...uint64) {
 		peer, _ := c.allocPeer(id)
 		region.Peers = append(region.Peers, peer)
 	}
-	c.putRegion(newRegionInfo(region, nil))
+	c.putRegion(core.NewRegionInfo(region, nil))
 }
 
 func (c *testClusterInfo) addLeaderRegionWithWriteInfo(regionID uint64, leaderID uint64, writtenBytes uint64, followerIds ...uint64) {
@@ -127,7 +127,7 @@ func (c *testClusterInfo) addLeaderRegionWithWriteInfo(regionID uint64, leaderID
 		peer, _ := c.allocPeer(id)
 		region.Peers = append(region.Peers, peer)
 	}
-	r := newRegionInfo(region, leader)
+	r := core.NewRegionInfo(region, leader)
 	r.WrittenBytes = writtenBytes
 	c.updateWriteStatus(r)
 	c.putRegion(r)
diff --git a/server/cache_test.go b/server/cache_test.go
index 1ff8fde60..3b917bb3a 100644
--- a/server/cache_test.go
+++ b/server/cache_test.go
@@ -86,8 +86,8 @@ type testRegionsInfoSuite struct{}
 
 // Create n regions (0..n) of n stores (0..n).
 // Each region contains np peers, the first peer is the leader.
-func newTestRegions(n, np uint64) []*RegionInfo {
-	regions := make([]*RegionInfo, 0, n)
+func newTestRegions(n, np uint64) []*core.RegionInfo {
+	regions := make([]*core.RegionInfo, 0, n)
 	for i := uint64(0); i < n; i++ {
 		peers := make([]*metapb.Peer, 0, np)
 		for j := uint64(0); j < np; j++ {
@@ -103,7 +103,7 @@ func newTestRegions(n, np uint64) []*RegionInfo {
 			StartKey: []byte{byte(i)},
 			EndKey:   []byte{byte(i + 1)},
 		}
-		regions = append(regions, newRegionInfo(region, peers[0]))
+		regions = append(regions, core.NewRegionInfo(region, peers[0]))
 	}
 	return regions
 }
@@ -170,7 +170,7 @@ func (s *testRegionsInfoSuite) Test(c *C) {
 	}
 }
 
-func checkRegion(c *C, a *RegionInfo, b *RegionInfo) {
+func checkRegion(c *C, a *core.RegionInfo, b *core.RegionInfo) {
 	c.Assert(a.Region, DeepEquals, b.Region)
 	c.Assert(a.Leader, DeepEquals, b.Leader)
 	c.Assert(a.Peers, DeepEquals, b.Peers)
@@ -182,7 +182,7 @@ func checkRegion(c *C, a *RegionInfo, b *RegionInfo) {
 	}
 }
 
-func checkRegionsKV(c *C, kv *kv, regions []*RegionInfo) {
+func checkRegionsKV(c *C, kv *kv, regions []*core.RegionInfo) {
 	if kv != nil {
 		for _, region := range regions {
 			var meta metapb.Region
@@ -194,7 +194,7 @@ func checkRegionsKV(c *C, kv *kv, regions []*RegionInfo) {
 	}
 }
 
-func checkRegions(c *C, cache *regionsInfo, regions []*RegionInfo) {
+func checkRegions(c *C, cache *regionsInfo, regions []*core.RegionInfo) {
 	regionCount := make(map[uint64]int)
 	leaderCount := make(map[uint64]int)
 	followerCount := make(map[uint64]int)
@@ -354,7 +354,7 @@ func (s *testClusterInfoSuite) testRegionHeartbeat(c *C, cache *clusterInfo) {
 		checkRegions(c, cache.regions, regions[:i+1])
 		checkRegionsKV(c, cache.kv, regions[:i+1])
 
-		epoch := region.clone().GetRegionEpoch()
+		epoch := region.Clone().GetRegionEpoch()
 
 		// region is updated.
 		region.RegionEpoch = &metapb.RegionEpoch{
@@ -365,7 +365,7 @@ func (s *testClusterInfoSuite) testRegionHeartbeat(c *C, cache *clusterInfo) {
 		checkRegionsKV(c, cache.kv, regions[:i+1])
 
 		// region is stale (Version).
-		stale := region.clone()
+		stale := region.Clone()
 		stale.RegionEpoch = &metapb.RegionEpoch{
 			ConfVer: epoch.GetConfVer() + 1,
 		}
@@ -383,7 +383,7 @@ func (s *testClusterInfoSuite) testRegionHeartbeat(c *C, cache *clusterInfo) {
 		checkRegionsKV(c, cache.kv, regions[:i+1])
 
 		// region is stale (ConfVer).
-		stale = region.clone()
+		stale = region.Clone()
 		stale.RegionEpoch = &metapb.RegionEpoch{
 			Version: epoch.GetVersion() + 1,
 		}
@@ -464,7 +464,7 @@ func (s *testClusterInfoSuite) testRegionHeartbeat(c *C, cache *clusterInfo) {
 func heartbeatRegions(c *C, cache *clusterInfo, regions []*metapb.Region) {
 	// Heartbeat and check region one by one.
 	for _, region := range regions {
-		r := newRegionInfo(region, nil)
+		r := core.NewRegionInfo(region, nil)
 
 		c.Assert(cache.handleRegionHeartbeat(r), IsNil)
 
@@ -479,7 +479,7 @@ func heartbeatRegions(c *C, cache *clusterInfo, regions []*metapb.Region) {
 
 	// Check all regions after handling all heartbeats.
 	for _, region := range regions {
-		r := newRegionInfo(region, nil)
+		r := core.NewRegionInfo(region, nil)
 
 		checkRegion(c, cache.getRegion(r.GetId()), r)
 		checkRegion(c, cache.searchRegion(r.StartKey), r)
@@ -599,8 +599,8 @@ func (s *testRegionMapSuite) TestRegionMap(c *C) {
 	s.check(c, rm, 2, 3)
 }
 
-func (s *testRegionMapSuite) regionInfo(id uint64) *RegionInfo {
-	return &RegionInfo{
+func (s *testRegionMapSuite) regionInfo(id uint64) *core.RegionInfo {
+	return &core.RegionInfo{
 		Region: &metapb.Region{
 			Id: id,
 		},
diff --git a/server/cluster_test.go b/server/cluster_test.go
index 43a062105..05aab873d 100644
--- a/server/cluster_test.go
+++ b/server/cluster_test.go
@@ -20,6 +20,7 @@ import (
 	. "github.com/pingcap/check"
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
+	"github.com/pingcap/pd/server/core"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc"
 )
@@ -428,7 +429,7 @@ func (s *testClusterSuite) testCheckStores(c *C, clusterID uint64) {
 	// Add a region peer to store.
 	leader := &metapb.Peer{StoreId: store.GetId()}
 	region := s.newRegion(c, 0, []byte{'a'}, []byte{'b'}, []*metapb.Peer{leader}, nil)
-	regionInfo := newRegionInfo(region, leader)
+	regionInfo := core.NewRegionInfo(region, leader)
 	err := cluster.handleRegionHeartbeat(regionInfo)
 	c.Assert(err, IsNil)
 	c.Assert(cluster.cachedCluster.getStoreRegionCount(store.GetId()), Equals, 1)
diff --git a/server/coordinator_test.go b/server/coordinator_test.go
index 7abb97b49..5686abf71 100644
--- a/server/coordinator_test.go
+++ b/server/coordinator_test.go
@@ -30,7 +30,7 @@ type testOperator struct {
 }
 
 func newTestOperator(regionID uint64, kind core.ResourceKind) Operator {
-	region := newRegionInfo(&metapb.Region{Id: regionID}, nil)
+	region := core.NewRegionInfo(&metapb.Region{Id: regionID}, nil)
 	op := &testOperator{RegionID: regionID, Kind: kind, State: OperatorRunning}
 	return newRegionOperator(region, kind, op)
 }
@@ -40,7 +40,7 @@ func (op *testOperator) GetResourceKind() core.ResourceKind { return op.Kind }
 func (op *testOperator) GetState() OperatorState            { return op.State }
 func (op *testOperator) SetState(state OperatorState)       { op.State = state }
 func (op *testOperator) GetName() string                    { return "test" }
-func (op *testOperator) Do(region *RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
+func (op *testOperator) Do(region *core.RegionInfo) (*pdpb.RegionHeartbeatResponse, bool) {
 	return nil, false
 }
 
@@ -153,14 +153,14 @@ func (s *testCoordinatorSuite) TestDispatch(c *C) {
 	dispatchHeartbeatNoResp(c, co, region, stream)
 }
 
-func dispatchHeartbeatNoResp(c *C, co *coordinator, region *RegionInfo, stream *mockHeartbeatStream) {
+func dispatchHeartbeatNoResp(c *C, co *coordinator, region *core.RegionInfo, stream *mockHeartbeatStream) {
 	co.hbStreams.bindStream(region.Leader.GetStoreId(), stream)
 	co.dispatch(region)
 	res := stream.Recv()
 	c.Assert(res, IsNil)
 }
 
-func dispatchAndRecvHeartbeat(c *C, co *coordinator, region *RegionInfo, stream *mockHeartbeatStream) *pdpb.RegionHeartbeatResponse {
+func dispatchAndRecvHeartbeat(c *C, co *coordinator, region *core.RegionInfo, stream *mockHeartbeatStream) *pdpb.RegionHeartbeatResponse {
 	var res *pdpb.RegionHeartbeatResponse
 	testutil.WaitUntil(c, func(c *C) bool {
 		co.hbStreams.bindStream(region.Leader.GetStoreId(), stream)
diff --git a/server/operator_test.go b/server/operator_test.go
index f5bcfe92d..75f4d61d4 100644
--- a/server/operator_test.go
+++ b/server/operator_test.go
@@ -19,6 +19,7 @@ import (
 
 	. "github.com/pingcap/check"
 	"github.com/pingcap/kvproto/pkg/pdpb"
+	"github.com/pingcap/pd/server/core"
 )
 
 var _ = Suite(&testOperatorSuite{})
@@ -66,7 +67,7 @@ func (o *testOperatorSuite) TestOperatorStateMarshal(c *C) {
 	}
 }
 
-func doRegionHeartbeatResponse(region *RegionInfo, resp *pdpb.RegionHeartbeatResponse) {
+func doRegionHeartbeatResponse(region *core.RegionInfo, resp *pdpb.RegionHeartbeatResponse) {
 	if resp == nil {
 		return
 	}
diff --git a/server/region_test.go b/server/region_test.go
index 47d804f61..cb2635a00 100644
--- a/server/region_test.go
+++ b/server/region_test.go
@@ -20,6 +20,7 @@ import (
 	. "github.com/pingcap/check"
 	"github.com/pingcap/kvproto/pkg/metapb"
 	"github.com/pingcap/kvproto/pkg/pdpb"
+	"github.com/pingcap/pd/server/core"
 )
 
 var _ = Suite(&testRegionSuite{})
@@ -42,11 +43,11 @@ func (s *testRegionSuite) TestRegionInfo(c *C) {
 	}
 	downPeer, pendingPeer := peers[0], peers[1]
 
-	info := newRegionInfo(region, peers[0])
+	info := core.NewRegionInfo(region, peers[0])
 	info.DownPeers = []*pdpb.PeerStats{{Peer: downPeer}}
 	info.PendingPeers = []*metapb.Peer{pendingPeer}
 
-	r := info.clone()
+	r := info.Clone()
 	c.Assert(r, DeepEquals, info)
 
 	for i := uint64(0); i < n; i++ {