mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-28 00:21:56 +00:00
99 lines
2.4 KiB
Go
99 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/percona/percona-toolkit/src/go/pt-galera-log-explainer/regex"
|
|
"github.com/percona/percona-toolkit/src/go/pt-galera-log-explainer/utils"
|
|
"golang.org/x/exp/slices"
|
|
"gopkg.in/yaml.v2"
|
|
)
|
|
|
|
type conflicts struct {
|
|
Paths []string `arg:"" name:"paths" help:"paths of the log to use"`
|
|
Yaml bool `xor:"format"`
|
|
Json bool `xor:"format"`
|
|
}
|
|
|
|
func (c *conflicts) Help() string {
|
|
return "Summarize every replication conflicts, from every node's point of view"
|
|
}
|
|
|
|
func (c *conflicts) Run() error {
|
|
|
|
regexes := regex.IdentsMap.Merge(regex.ApplicativeMap)
|
|
timeline, err := timelineFromPaths(c.Paths, regexes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
logCtxs := timeline.GetLatestContextsByNodes()
|
|
for _, logCtx := range logCtxs {
|
|
if len(logCtx.Conflicts) == 0 {
|
|
continue
|
|
}
|
|
var out string
|
|
|
|
switch {
|
|
case c.Yaml:
|
|
tmp, err := yaml.Marshal(logCtx.Conflicts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = string(tmp)
|
|
case c.Json:
|
|
tmp, err := json.Marshal(logCtx.Conflicts)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out = string(tmp)
|
|
|
|
default:
|
|
var b strings.Builder
|
|
for _, conflict := range logCtx.Conflicts {
|
|
b.WriteString("\n\n")
|
|
b.WriteString(utils.Paint(utils.BlueText, "seqno: "))
|
|
b.WriteString(conflict.Seqno)
|
|
b.WriteString("\n\t")
|
|
b.WriteString(utils.Paint(utils.BlueText, "winner: "))
|
|
b.WriteString(conflict.Winner)
|
|
b.WriteString("\n\t")
|
|
b.WriteString(utils.Paint(utils.BlueText, "votes per nodes:"))
|
|
|
|
nodes := []string{}
|
|
for node := range conflict.VotePerNode {
|
|
nodes = append(nodes, node)
|
|
}
|
|
// do not iterate over VotePerNode map
|
|
// map accesses are random, it will make regression tests harder
|
|
slices.Sort(nodes)
|
|
|
|
for _, node := range nodes {
|
|
vote := conflict.VotePerNode[node]
|
|
displayVote := utils.Paint(utils.RedText, vote.MD5)
|
|
if vote.MD5 == conflict.Winner {
|
|
displayVote = utils.Paint(utils.GreenText, vote.MD5)
|
|
}
|
|
b.WriteString("\n\t\t")
|
|
b.WriteString(utils.Paint(utils.BlueText, node))
|
|
b.WriteString(": (")
|
|
b.WriteString(displayVote)
|
|
b.WriteString(") ")
|
|
b.WriteString(vote.Error)
|
|
}
|
|
b.WriteString("\n\t")
|
|
b.WriteString(utils.Paint(utils.BlueText, "initiated by: "))
|
|
b.WriteString(fmt.Sprintf("%v", conflict.InitiatedBy))
|
|
out = b.String()[2:]
|
|
}
|
|
|
|
}
|
|
fmt.Println(out)
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|