feat(metanode): Add raft members consistent check with master hosts in metanode member check

. #1000165698

Signed-off-by: leonrayang <changliang@oppo.com>
This commit is contained in:
leonrayang 2025-06-06 09:39:11 +08:00 committed by zhumingze1108
parent e944cecec5
commit 7d5ad3adf4
10 changed files with 276 additions and 35 deletions

View File

@ -412,6 +412,14 @@ var (
partitionInfoTableHeader = fmt.Sprintf(partitionInfoTablePattern, partitionInfoTableHeader = fmt.Sprintf(partitionInfoTablePattern,
"ID", "VOLUME", "REPLICAS", "STATUS", "MediaType", "MEMBERS") "ID", "VOLUME", "REPLICAS", "STATUS", "MediaType", "MEMBERS")
PeerAbnormalRaftPartitionInfoTablePattern = "%-8v %-32v %-8v %-12v %-12v %-12v %v%v%v"
PeerAbnormalRaftPartitionInfoHeader = fmt.Sprintf(PeerAbnormalRaftPartitionInfoTablePattern,
"ID", "VOLUME", "REPLICAS", "STATUS", "MediaType", "MEMBERS", "DIFF-RAFT-MEMBERS", "|DOWN-MEMBERS", "|PEND-MEMBERS")
badMpReplicaPartitionInfoTablePattern = "%-8v %-32v %-8v %-12v %-64v %-24v"
badMpReplicaPartitionInfoTableHeader = fmt.Sprintf(badMpReplicaPartitionInfoTablePattern,
"ID", "VOLUME", "REPLICAS", "STATUS", "MEMBERS", "UNAVAILABLE_REPLICAS")
badReplicaPartitionInfoTablePattern = "%-8v %-32v %-8v %-12v %-64v %-24v" badReplicaPartitionInfoTablePattern = "%-8v %-32v %-8v %-12v %-64v %-24v"
badReplicaPartitionInfoTableHeader = fmt.Sprintf(badReplicaPartitionInfoTablePattern, badReplicaPartitionInfoTableHeader = fmt.Sprintf(badReplicaPartitionInfoTablePattern,
"ID", "VOLUME", "REPLICAS", "STATUS", "MEMBERS", "UNAVAILABLE_REPLICAS") "ID", "VOLUME", "REPLICAS", "STATUS", "MEMBERS", "UNAVAILABLE_REPLICAS")
@ -553,6 +561,65 @@ func formatMetaPartitionInfoRow(partition *proto.MetaPartitionInfo) string {
formatDataPartitionStatus(partition.Status), "N/A", strings.Join(partition.Hosts, ", ")) formatDataPartitionStatus(partition.Status), "N/A", strings.Join(partition.Hosts, ", "))
} }
func formatMetaPartitionInfoRowWithRaft(partition *proto.MetaPartitionInfo) string {
var (
info *proto.MetaPartitionLoadResponse
addrArr = make(map[uint64]string)
diffHosts []string
downHosts []string
pendingHosts []string
)
for _, peer := range partition.Peers {
addrArr[peer.ID] = peer.Addr
}
for _, info = range partition.LoadResponse {
if info.RaftInfo.RaftStatus.Leader == info.RaftInfo.RaftStatus.NodeID {
break
}
}
if info == nil {
return ""
}
for _, peer := range info.RaftInfo.Hosts {
if _, ok := addrArr[peer.ID]; ok {
continue
}
diffHosts = append(diffHosts, peer.Addr)
addrArr[peer.ID] = peer.Addr
}
for _, dr := range info.RaftInfo.DownReplicas {
if host, ok := addrArr[dr.NodeID]; ok {
downHosts = append(downHosts, host)
}
}
for _, pr := range info.RaftInfo.PendingPeers {
if host, ok := addrArr[pr]; ok {
pendingHosts = append(pendingHosts, host)
}
}
var (
diffInfo = "N/A"
downInfo = "N/A"
pendingInfo = "N/A"
)
if len(diffHosts) != 0 {
diffInfo = strings.Join(diffHosts, ", ")
}
if len(downHosts) != 0 {
downInfo = strings.Join(downHosts, ", ")
}
if len(pendingHosts) != 0 {
pendingInfo = strings.Join(pendingHosts, ", ")
}
return fmt.Sprintf(PeerAbnormalRaftPartitionInfoTablePattern,
partition.PartitionID, partition.VolName, partition.ReplicaNum,
formatDataPartitionStatus(partition.Status), "N/A", strings.Join(partition.Hosts, ", "),
diffInfo+"|", downInfo+"|", pendingInfo)
}
func formatBadReplicaMpInfoRow(partition *proto.MetaPartitionInfo) string { func formatBadReplicaMpInfoRow(partition *proto.MetaPartitionInfo) string {
sb := strings.Builder{} sb := strings.Builder{}
sb.WriteString("[") sb.WriteString("[")

View File

@ -90,7 +90,7 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
"reset" command will be released in next version.`, "reset" command will be released in next version.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
var ( var (
diagnosis *proto.MetaPartitionDiagnosis diagnosis *proto.MetaPartitionDiagnosisV1
metaNodes []*proto.MetaNodeInfo metaNodes []*proto.MetaNodeInfo
err error err error
) )
@ -120,10 +120,10 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
stdout("\n") stdout("\n")
stdout("[Corrupt meta partitions](no leader):\n") stdout("[Corrupt meta partitions](no leader):\n")
stdout("%v\n", partitionInfoTableHeader) stdout("%v\n", partitionInfoTableHeader)
sort.SliceStable(diagnosis.CorruptMetaPartitionIDs, func(i, j int) bool { sort.SliceStable(diagnosis.NoLeaderMetaPartitionIDs, func(i, j int) bool {
return diagnosis.CorruptMetaPartitionIDs[i] < diagnosis.CorruptMetaPartitionIDs[j] return diagnosis.NoLeaderMetaPartitionIDs[i] < diagnosis.NoLeaderMetaPartitionIDs[j]
}) })
for _, pid := range diagnosis.CorruptMetaPartitionIDs { for _, pid := range diagnosis.NoLeaderMetaPartitionIDs {
var partition *proto.MetaPartitionInfo var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil { if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err) err = fmt.Errorf("Partition not found, err:[%v] ", err)
@ -149,6 +149,23 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
} }
} }
stdout("\n")
stdout("%v\n", "[Meta partition Abnormal Raft Info]:")
stdout("%v\n", PeerAbnormalRaftPartitionInfoHeader)
sort.SliceStable(diagnosis.AbnormalRaftIDs, func(i, j int) bool {
return diagnosis.AbnormalRaftIDs[i] < diagnosis.AbnormalRaftIDs[j]
})
for _, pid := range diagnosis.AbnormalRaftIDs {
var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err)
return
}
if partition != nil {
stdout("%v\n", formatMetaPartitionInfoRowWithRaft(partition))
}
}
stdout("\n") stdout("\n")
stdout("%v\n", "[Bad meta partitions(decommission not completed)]:") stdout("%v\n", "[Bad meta partitions(decommission not completed)]:")
badPartitionTablePattern := "%-8v %-10v\n" badPartitionTablePattern := "%-8v %-10v\n"
@ -164,11 +181,11 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
stdout("\n") stdout("\n")
stdout("%v\n", "[Meta Partition has unavailable replica]:") stdout("%v\n", "[Meta Partition has unavailable replica]:")
stdout("%v\n", badReplicaPartitionInfoTableHeader) stdout("%v\n", badMpReplicaPartitionInfoTableHeader)
sort.SliceStable(diagnosis.BadReplicaMetaPartitionIDs, func(i, j int) bool { sort.SliceStable(diagnosis.UnavailableMetaPartitionIDs, func(i, j int) bool {
return diagnosis.BadReplicaMetaPartitionIDs[i] < diagnosis.BadReplicaMetaPartitionIDs[j] return diagnosis.UnavailableMetaPartitionIDs[i] < diagnosis.UnavailableMetaPartitionIDs[j]
}) })
for _, pid := range diagnosis.BadReplicaMetaPartitionIDs { for _, pid := range diagnosis.UnavailableMetaPartitionIDs {
var partition *proto.MetaPartitionInfo var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil { if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err) err = fmt.Errorf("Partition not found, err:[%v] ", err)
@ -185,10 +202,10 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
stdout("\n") stdout("\n")
stdout("%v\n", "[Partition with replica inode count not equal]:") stdout("%v\n", "[Partition with replica inode count not equal]:")
stdout("%v\n", inodeCountNotEqualInfoTableHeader) stdout("%v\n", inodeCountNotEqualInfoTableHeader)
sort.SliceStable(diagnosis.InodeCountNotEqualReplicaMetaPartitionIDs, func(i, j int) bool { sort.SliceStable(diagnosis.InodeCountNotEqualIDs, func(i, j int) bool {
return diagnosis.InodeCountNotEqualReplicaMetaPartitionIDs[i] < diagnosis.InodeCountNotEqualReplicaMetaPartitionIDs[j] return diagnosis.InodeCountNotEqualIDs[i] < diagnosis.InodeCountNotEqualIDs[j]
}) })
for _, pid := range diagnosis.InodeCountNotEqualReplicaMetaPartitionIDs { for _, pid := range diagnosis.InodeCountNotEqualIDs {
var partition *proto.MetaPartitionInfo var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil { if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err) err = fmt.Errorf("Partition not found, err:[%v] ", err)
@ -202,10 +219,10 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
stdout("\n") stdout("\n")
stdout("%v\n", "[Partition with replica max inode not equal]:") stdout("%v\n", "[Partition with replica max inode not equal]:")
stdout("%v\n", maxInodeNotEqualInfoTableHeader) stdout("%v\n", maxInodeNotEqualInfoTableHeader)
sort.SliceStable(diagnosis.MaxInodeNotEqualReplicaMetaPartitionIDs, func(i, j int) bool { sort.SliceStable(diagnosis.MaxInodeNotEqualIDs, func(i, j int) bool {
return diagnosis.MaxInodeNotEqualReplicaMetaPartitionIDs[i] < diagnosis.MaxInodeNotEqualReplicaMetaPartitionIDs[j] return diagnosis.MaxInodeNotEqualIDs[i] < diagnosis.MaxInodeNotEqualIDs[j]
}) })
for _, pid := range diagnosis.MaxInodeNotEqualReplicaMetaPartitionIDs { for _, pid := range diagnosis.MaxInodeNotEqualIDs {
var partition *proto.MetaPartitionInfo var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil { if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err) err = fmt.Errorf("Partition not found, err:[%v] ", err)
@ -219,10 +236,10 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
stdout("\n") stdout("\n")
stdout("%v\n", "[Partition with replica dentry count not equal]:") stdout("%v\n", "[Partition with replica dentry count not equal]:")
stdout("%v\n", dentryCountNotEqualInfoTableHeader) stdout("%v\n", dentryCountNotEqualInfoTableHeader)
sort.SliceStable(diagnosis.DentryCountNotEqualReplicaMetaPartitionIDs, func(i, j int) bool { sort.SliceStable(diagnosis.DentryCountNotEqualIDs, func(i, j int) bool {
return diagnosis.DentryCountNotEqualReplicaMetaPartitionIDs[i] < diagnosis.DentryCountNotEqualReplicaMetaPartitionIDs[j] return diagnosis.DentryCountNotEqualIDs[i] < diagnosis.DentryCountNotEqualIDs[j]
}) })
for _, pid := range diagnosis.DentryCountNotEqualReplicaMetaPartitionIDs { for _, pid := range diagnosis.DentryCountNotEqualIDs {
var partition *proto.MetaPartitionInfo var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil { if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err) err = fmt.Errorf("Partition not found, err:[%v] ", err)
@ -236,10 +253,10 @@ the corrupt nodes, the few remaining replicas can not reach an agreement with on
stdout("\n") stdout("\n")
stdout("%v\n", "[Partition with excessive replicas]:") stdout("%v\n", "[Partition with excessive replicas]:")
stdout("%v\n", partitionInfoTableHeader) stdout("%v\n", partitionInfoTableHeader)
sort.SliceStable(diagnosis.ExcessReplicaMetaPartitionIDs, func(i, j int) bool { sort.SliceStable(diagnosis.InConsistRreplicaCntMetaPartitionIDs, func(i, j int) bool {
return diagnosis.ExcessReplicaMetaPartitionIDs[i] < diagnosis.ExcessReplicaMetaPartitionIDs[j] return diagnosis.InConsistRreplicaCntMetaPartitionIDs[i] < diagnosis.InConsistRreplicaCntMetaPartitionIDs[j]
}) })
for _, pid := range diagnosis.ExcessReplicaMetaPartitionIDs { for _, pid := range diagnosis.InConsistRreplicaCntMetaPartitionIDs {
var partition *proto.MetaPartitionInfo var partition *proto.MetaPartitionInfo
if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil { if partition, err = client.ClientAPI().GetMetaPartition(pid); err != nil {
err = fmt.Errorf("Partition not found, err:[%v] ", err) err = fmt.Errorf("Partition not found, err:[%v] ", err)

View File

@ -4583,7 +4583,7 @@ func (m *Server) getNodeInfoHandler(w http.ResponseWriter, r *http.Request) {
sendOkReply(w, r, newSuccessHTTPReply(resp)) sendOkReply(w, r, newSuccessHTTPReply(resp))
} }
func (m *Server) diagnoseMetaPartition(w http.ResponseWriter, r *http.Request) { func (m *Server) diagnoseMetaPartitionDelayDelted(w http.ResponseWriter, r *http.Request) {
var ( var (
err error err error
rstMsg *proto.MetaPartitionDiagnosis rstMsg *proto.MetaPartitionDiagnosis
@ -4608,17 +4608,14 @@ func (m *Server) diagnoseMetaPartition(w http.ResponseWriter, r *http.Request) {
defer func() { defer func() {
doStatAndMetric(proto.AdminDiagnoseMetaPartition, metric, err, nil) doStatAndMetric(proto.AdminDiagnoseMetaPartition, metric, err, nil)
}() }()
corruptMpIDs = make([]uint64, 0) corruptMpIDs = make([]uint64, 0)
lackReplicaMpIDs = make([]uint64, 0) lackReplicaMpIDs = make([]uint64, 0)
badReplicaMpIDs = make([]uint64, 0) badReplicaMpIDs = make([]uint64, 0)
excessReplicaMpIDs = make([]uint64, 0) excessReplicaMpIDs = make([]uint64, 0)
if inactiveNodes, err = m.cluster.checkInactiveMetaNodes(); err != nil { if inactiveNodes, err = m.cluster.checkInactiveMetaNodes(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err)) sendErrReply(w, r, newErrHTTPReply(err))
return return
} }
if lackReplicaMps, noLeaderMps, badReplicaMps, excessReplicaMPs, if lackReplicaMps, noLeaderMps, badReplicaMps, excessReplicaMPs,
inodeCountNotEqualReplicaMps, maxInodeNotEqualMPs, dentryCountNotEqualReplicaMps, err = m.cluster.checkReplicaMetaPartitions(); err != nil { inodeCountNotEqualReplicaMps, maxInodeNotEqualMPs, dentryCountNotEqualReplicaMps, err = m.cluster.checkReplicaMetaPartitions(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err)) sendErrReply(w, r, newErrHTTPReply(err))
@ -4636,14 +4633,12 @@ func (m *Server) diagnoseMetaPartition(w http.ResponseWriter, r *http.Request) {
for _, mp := range excessReplicaMPs { for _, mp := range excessReplicaMPs {
excessReplicaMpIDs = append(excessReplicaMpIDs, mp.PartitionID) excessReplicaMpIDs = append(excessReplicaMpIDs, mp.PartitionID)
} }
for _, mp := range inodeCountNotEqualReplicaMps { for _, mp := range inodeCountNotEqualReplicaMps {
inodeCountNotEqualReplicaMpIDs = append(inodeCountNotEqualReplicaMpIDs, mp.PartitionID) inodeCountNotEqualReplicaMpIDs = append(inodeCountNotEqualReplicaMpIDs, mp.PartitionID)
} }
for _, mp := range maxInodeNotEqualMPs { for _, mp := range maxInodeNotEqualMPs {
maxInodeNotEqualReplicaMpIDs = append(maxInodeNotEqualReplicaMpIDs, mp.PartitionID) maxInodeNotEqualReplicaMpIDs = append(maxInodeNotEqualReplicaMpIDs, mp.PartitionID)
} }
for _, mp := range dentryCountNotEqualReplicaMps { for _, mp := range dentryCountNotEqualReplicaMps {
dentryCountNotEqualReplicaMpIDs = append(dentryCountNotEqualReplicaMpIDs, mp.PartitionID) dentryCountNotEqualReplicaMpIDs = append(dentryCountNotEqualReplicaMpIDs, mp.PartitionID)
} }
@ -4667,6 +4662,30 @@ func (m *Server) diagnoseMetaPartition(w http.ResponseWriter, r *http.Request) {
sendOkReply(w, r, newSuccessHTTPReply(rstMsg)) sendOkReply(w, r, newSuccessHTTPReply(rstMsg))
} }
func (m *Server) diagnoseMetaPartition(w http.ResponseWriter, r *http.Request) {
var (
err error
diagnosis interface{}
)
if err = r.ParseForm(); err != nil {
return
}
if v1 := r.FormValue("v1"); v1 == "" {
m.diagnoseMetaPartitionDelayDelted(w, r)
return
} else {
metric := exporter.NewTPCnt(apiToMetricsName(proto.AdminDiagnoseMetaPartition))
defer func() {
doStatAndMetric(proto.AdminDiagnoseMetaPartition, metric, err, nil)
}()
if diagnosis, err = m.cluster.checkReplicaMetaPartitionsV1(); err != nil {
sendErrReply(w, r, newErrHTTPReply(err))
return
}
}
sendOkReply(w, r, newSuccessHTTPReply(diagnosis))
}
// Decommission a disk. This will decommission all the data partitions on this disk. // Decommission a disk. This will decommission all the data partitions on this disk.
// If parameter diskDisable is true, creating data partitions on this disk will be not allowed. // If parameter diskDisable is true, creating data partitions on this disk will be not allowed.
func (m *Server) decommissionDisk(w http.ResponseWriter, r *http.Request) { func (m *Server) decommissionDisk(w http.ResponseWriter, r *http.Request) {
@ -5991,6 +6010,7 @@ func (m *Server) getMetaPartition(w http.ResponseWriter, r *http.Request) {
for i := 0; i < len(replicas); i++ { for i := 0; i < len(replicas); i++ {
replicas[i] = &proto.MetaReplicaInfo{ replicas[i] = &proto.MetaReplicaInfo{
Addr: mp.Replicas[i].Addr, Addr: mp.Replicas[i].Addr,
NodeID: mp.Replicas[i].nodeID,
DomainAddr: mp.Replicas[i].metaNode.DomainAddr, DomainAddr: mp.Replicas[i].metaNode.DomainAddr,
MaxInodeID: mp.Replicas[i].MaxInodeID, MaxInodeID: mp.Replicas[i].MaxInodeID,
ReportTime: mp.Replicas[i].ReportTime, ReportTime: mp.Replicas[i].ReportTime,

View File

@ -81,6 +81,7 @@ type ClusterTopoSubItem struct {
inodeCountNotEqualMP *sync.Map inodeCountNotEqualMP *sync.Map
maxInodeNotEqualMP *sync.Map maxInodeNotEqualMP *sync.Map
dentryCountNotEqualMP *sync.Map dentryCountNotEqualMP *sync.Map
AbnormalRaftMP *sync.Map
mnMutex sync.RWMutex // meta node mutex mnMutex sync.RWMutex // meta node mutex
dnMutex sync.RWMutex // data node mutex dnMutex sync.RWMutex // data node mutex
@ -465,6 +466,7 @@ func newCluster(name string, leaderInfo *LeaderInfo, fsm *MetadataFsm, partition
c.inodeCountNotEqualMP = new(sync.Map) c.inodeCountNotEqualMP = new(sync.Map)
c.maxInodeNotEqualMP = new(sync.Map) c.maxInodeNotEqualMP = new(sync.Map)
c.dentryCountNotEqualMP = new(sync.Map) c.dentryCountNotEqualMP = new(sync.Map)
c.AbnormalRaftMP = new(sync.Map)
c.lcMgr = newLifecycleManager() c.lcMgr = newLifecycleManager()
c.lcMgr.cluster = c c.lcMgr.cluster = c
c.snapshotMgr = newSnapshotManager() c.snapshotMgr = newSnapshotManager()

View File

@ -282,30 +282,24 @@ func (c *Cluster) checkReplicaMetaPartitions() (
inodeCountNotEqualMPs = make([]*MetaPartition, 0) inodeCountNotEqualMPs = make([]*MetaPartition, 0)
maxInodeNotEqualMPs = make([]*MetaPartition, 0) maxInodeNotEqualMPs = make([]*MetaPartition, 0)
dentryCountNotEqualMPs = make([]*MetaPartition, 0) dentryCountNotEqualMPs = make([]*MetaPartition, 0)
markDeleteVolNames := make(VolNameSet) markDeleteVolNames := make(VolNameSet)
vols := c.copyVols() vols := c.copyVols()
for _, vol := range vols { for _, vol := range vols {
if vol.Status == proto.VolStatusMarkDelete { if vol.Status == proto.VolStatusMarkDelete {
markDeleteVolNames[vol.Name] = struct{}{} markDeleteVolNames[vol.Name] = struct{}{}
continue continue
} }
vol.mpsLock.RLock() vol.mpsLock.RLock()
for _, mp := range vol.MetaPartitions { for _, mp := range vol.MetaPartitions {
if uint8(len(mp.Hosts)) < mp.ReplicaNum || uint8(len(mp.getActiveAddrs(defaultMetaPartitionTimeOutSec))) < mp.ReplicaNum { if uint8(len(mp.Hosts)) < mp.ReplicaNum || uint8(len(mp.getActiveAddrs(defaultMetaPartitionTimeOutSec))) < mp.ReplicaNum {
lackReplicaMetaPartitions = append(lackReplicaMetaPartitions, mp) lackReplicaMetaPartitions = append(lackReplicaMetaPartitions, mp)
} }
if !mp.isLeaderExist() && (time.Now().Unix()-mp.LeaderReportTime > c.cfg.MpNoLeaderReportIntervalSec) { if !mp.isLeaderExist() && (time.Now().Unix()-mp.LeaderReportTime > c.cfg.MpNoLeaderReportIntervalSec) {
noLeaderMetaPartitions = append(noLeaderMetaPartitions, mp) noLeaderMetaPartitions = append(noLeaderMetaPartitions, mp)
} }
if uint8(len(mp.Hosts)) > mp.ReplicaNum || uint8(len(mp.Replicas)) > mp.ReplicaNum { if uint8(len(mp.Hosts)) > mp.ReplicaNum || uint8(len(mp.Replicas)) > mp.ReplicaNum {
excessReplicaMetaPartitions = append(excessReplicaMetaPartitions, mp) excessReplicaMetaPartitions = append(excessReplicaMetaPartitions, mp)
} }
for _, replica := range mp.Replicas { for _, replica := range mp.Replicas {
if replica.Status == proto.Unavailable { if replica.Status == proto.Unavailable {
unavailableReplicaMPs = append(unavailableReplicaMPs, mp) unavailableReplicaMPs = append(unavailableReplicaMPs, mp)
@ -343,6 +337,69 @@ func (c *Cluster) checkReplicaMetaPartitions() (
return return
} }
func (c *Cluster) checkReplicaMetaPartitionsV1() (diagnosis *proto.MetaPartitionDiagnosisV1, err error) {
diagnosis = &proto.MetaPartitionDiagnosisV1{}
markDeleteVolNames := make(VolNameSet)
vols := c.copyVols()
for _, vol := range vols {
if vol.Status == proto.VolStatusMarkDelete {
markDeleteVolNames[vol.Name] = struct{}{}
continue
}
vol.mpsLock.RLock()
for _, mp := range vol.MetaPartitions {
if uint8(len(mp.Hosts)) < mp.ReplicaNum || uint8(len(mp.getActiveAddrs(defaultMetaPartitionTimeOutSec))) < mp.ReplicaNum {
diagnosis.LackReplicaMetaPartitionIDs = append(diagnosis.LackReplicaMetaPartitionIDs, mp.PartitionID)
}
if !mp.isLeaderExist() && (time.Now().Unix()-mp.LeaderReportTime > c.cfg.MpNoLeaderReportIntervalSec) {
diagnosis.NoLeaderMetaPartitionIDs = append(diagnosis.NoLeaderMetaPartitionIDs, mp.PartitionID)
}
if uint8(len(mp.Hosts)) > mp.ReplicaNum || uint8(len(mp.Replicas)) > mp.ReplicaNum {
diagnosis.InConsistRreplicaCntMetaPartitionIDs = append(diagnosis.InConsistRreplicaCntMetaPartitionIDs, mp.PartitionID)
}
for _, replica := range mp.Replicas {
if replica.Status == proto.Unavailable {
diagnosis.UnavailableMetaPartitionIDs = append(diagnosis.UnavailableMetaPartitionIDs, mp.PartitionID)
break
}
}
}
vol.mpsLock.RUnlock()
}
setAbnormalIDs := func(mpMap *sync.Map) []uint64 {
var resultIDs []uint64
mpMap.Range(func(key, value interface{}) bool {
mp := value.(*MetaPartition)
if _, ok := markDeleteVolNames[mp.volName]; !ok {
resultIDs = append(resultIDs, mp.PartitionID)
}
return true
})
return resultIDs
}
diagnosis.InodeCountNotEqualIDs = setAbnormalIDs(c.inodeCountNotEqualMP)
diagnosis.MaxInodeNotEqualIDs = setAbnormalIDs(c.maxInodeNotEqualMP)
diagnosis.DentryCountNotEqualIDs = setAbnormalIDs(c.dentryCountNotEqualMP)
diagnosis.AbnormalRaftIDs = setAbnormalIDs(c.AbnormalRaftMP)
log.LogInfof("clusterID[%v], lackReplicaMetaPartitions count:[%v], noLeaderMetaPartitions count[%v]"+
"unavailableReplicaMPs count:[%v], excessReplicaMp count:[%v], AbnormalRaftIDs count:[%v]",
c.Name, len(diagnosis.LackReplicaMetaPartitionIDs), len(diagnosis.NoLeaderMetaPartitionIDs),
len(diagnosis.UnavailableMetaPartitionIDs), len(diagnosis.InConsistRreplicaCntMetaPartitionIDs), len(diagnosis.AbnormalRaftIDs))
if diagnosis.InactiveMetaNodes, err = c.checkInactiveMetaNodes(); err != nil {
return
}
return
}
func (c *Cluster) deleteMetaReplica(partition *MetaPartition, addr string, validate bool, forceDel bool) (err error) { func (c *Cluster) deleteMetaReplica(partition *MetaPartition, addr string, validate bool, forceDel bool) (err error) {
defer func() { defer func() {
if err != nil { if err != nil {
@ -619,6 +676,7 @@ func (c *Cluster) doLoadMetaPartition(mp *MetaPartition) {
}(host) }(host)
} }
wg.Wait() wg.Wait()
mp.checkPeerDiffWithRaft(c)
select { select {
case err := <-errChannel: case err := <-errChannel:
msg := fmt.Sprintf("action[doLoadMetaPartition] vol[%v],mpID[%v],err[%v]", mp.volName, mp.PartitionID, err.Error()) msg := fmt.Sprintf("action[doLoadMetaPartition] vol[%v],mpID[%v],err[%v]", mp.volName, mp.PartitionID, err.Error())

View File

@ -52,6 +52,48 @@ func (c *Cluster) checkLoadMetaPartitions() {
} }
} }
func (mp *MetaPartition) checkPeerDiffWithRaft(c *Cluster) {
if len(mp.LoadResponse) == 0 {
return
}
if !mp.doCompare() {
return
}
var (
addrArr = make(map[uint64]string)
leaderID uint64
)
for _, mr := range mp.Replicas {
addrArr[mr.nodeID] = mr.Addr
if mr.IsLeader {
leaderID = mr.nodeID
}
}
for _, info := range mp.LoadResponse {
if info.RaftInfo.RaftStatus.NodeID != leaderID {
continue
}
for peer := range info.RaftInfo.RaftStatus.Replicas {
if _, ok := addrArr[peer]; !ok {
c.AbnormalRaftMP.Store(mp.PartitionID, mp)
return
}
}
if len(info.RaftInfo.PendingPeers) != 0 {
c.AbnormalRaftMP.Store(mp.PartitionID, mp)
return
}
if len(info.RaftInfo.DownReplicas) != 0 {
c.AbnormalRaftMP.Store(mp.PartitionID, mp)
return
}
c.AbnormalRaftMP.Delete(mp.PartitionID)
return
}
c.AbnormalRaftMP.Delete(mp.PartitionID)
}
func (mp *MetaPartition) checkSnapshot(c *Cluster) { func (mp *MetaPartition) checkSnapshot(c *Cluster) {
if len(mp.LoadResponse) == 0 { if len(mp.LoadResponse) == 0 {
return return

View File

@ -1431,6 +1431,14 @@ func (mp *metaPartition) ResponseLoadMetaPartition(p *Packet) (err error) {
resp.DentryCount = uint64(mp.GetDentryTreeLen()) resp.DentryCount = uint64(mp.GetDentryTreeLen())
resp.ApplyID = mp.getApplyID() resp.ApplyID = mp.getApplyID()
resp.CommittedID = mp.getCommittedID() resp.CommittedID = mp.getCommittedID()
resp.RaftInfo.DownReplicas = mp.config.RaftStore.RaftServer().GetDownReplicas(mp.config.PartitionId)
resp.RaftInfo.PendingPeers = mp.config.RaftStore.RaftServer().GetPendingReplica(mp.config.PartitionId)
if rStatus := mp.config.RaftStore.RaftStatus(mp.config.PartitionId); rStatus != nil {
resp.RaftInfo.RaftStatus = *rStatus
}
resp.RaftInfo.Hosts = mp.config.Peers
if err != nil { if err != nil {
err = errors.Trace(err, err = errors.Trace(err,
"[ResponseLoadMetaPartition] check snapshot") "[ResponseLoadMetaPartition] check snapshot")

View File

@ -23,6 +23,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/cubefs/cubefs/depends/tiglabs/raft"
"github.com/cubefs/cubefs/util" "github.com/cubefs/cubefs/util"
"github.com/cubefs/cubefs/util/log" "github.com/cubefs/cubefs/util/log"
) )
@ -1092,6 +1094,13 @@ type MetaPartitionLoadRequest struct {
PartitionID uint64 PartitionID uint64
} }
type RaftInfo struct {
RaftStatus raft.Status
PendingPeers []uint64
DownReplicas []raft.DownReplica
Hosts []Peer
}
// MetaPartitionLoadResponse defines the response to the request of loading meta partition. // MetaPartitionLoadResponse defines the response to the request of loading meta partition.
type MetaPartitionLoadResponse struct { type MetaPartitionLoadResponse struct {
PartitionID uint64 PartitionID uint64
@ -1102,6 +1111,7 @@ type MetaPartitionLoadResponse struct {
DentryCount uint64 DentryCount uint64
InodeCount uint64 InodeCount uint64
Addr string Addr string
RaftInfo RaftInfo
} }
// DataPartitionResponse defines the response from a data node to the master that is related to a data partition. // DataPartitionResponse defines the response from a data node to the master that is related to a data partition.

View File

@ -120,6 +120,7 @@ type MetaPartitionInfo struct {
// MetaReplica defines the replica of a meta partition // MetaReplica defines the replica of a meta partition
type MetaReplicaInfo struct { type MetaReplicaInfo struct {
Addr string Addr string
NodeID uint64
DomainAddr string DomainAddr string
MaxInodeID uint64 MaxInodeID uint64
ReportTime int64 ReportTime int64
@ -400,6 +401,7 @@ type DataPartitionDiagnosis struct {
} }
// meta partition diagnosis represents the inactive meta nodes, corrupt meta partitions, and meta partitions lack of replicas // meta partition diagnosis represents the inactive meta nodes, corrupt meta partitions, and meta partitions lack of replicas
type MetaPartitionDiagnosis struct { type MetaPartitionDiagnosis struct {
InactiveMetaNodes []string InactiveMetaNodes []string
CorruptMetaPartitionIDs []uint64 CorruptMetaPartitionIDs []uint64
@ -412,6 +414,19 @@ type MetaPartitionDiagnosis struct {
DentryCountNotEqualReplicaMetaPartitionIDs []uint64 DentryCountNotEqualReplicaMetaPartitionIDs []uint64
} }
type MetaPartitionDiagnosisV1 struct {
InactiveMetaNodes []string
NoLeaderMetaPartitionIDs []uint64
LackReplicaMetaPartitionIDs []uint64
BadMetaPartitionIDs []BadPartitionView
UnavailableMetaPartitionIDs []uint64
InConsistRreplicaCntMetaPartitionIDs []uint64
InodeCountNotEqualIDs []uint64
MaxInodeNotEqualIDs []uint64
DentryCountNotEqualIDs []uint64
AbnormalRaftIDs []uint64
}
type FailedDpInfo struct { type FailedDpInfo struct {
PartitionID uint64 PartitionID uint64
ErrMsg string ErrMsg string

View File

@ -161,9 +161,11 @@ func (api *AdminAPI) DiagnoseDataPartition(ignoreDiscardDp bool) (diagnosis *pro
return return
} }
func (api *AdminAPI) DiagnoseMetaPartition() (diagnosis *proto.MetaPartitionDiagnosis, err error) { func (api *AdminAPI) DiagnoseMetaPartition() (diagnosis *proto.MetaPartitionDiagnosisV1, err error) {
diagnosis = &proto.MetaPartitionDiagnosis{} diagnosis = &proto.MetaPartitionDiagnosisV1{}
err = api.mc.requestWith(diagnosis, newRequest(get, proto.AdminDiagnoseMetaPartition).Header(api.h)) err = api.mc.requestWith(diagnosis, newRequest(get, proto.AdminDiagnoseMetaPartition).Header(api.h).Param(
anyParam{"v1", "true"},
))
return return
} }