github.com/grafana/pyroscope@v1.18.0/pkg/metastore/raftnode/errors.go (about) 1 package raftnode 2 3 import ( 4 "errors" 5 6 "github.com/hashicorp/raft" 7 "google.golang.org/grpc/codes" 8 "google.golang.org/grpc/status" 9 10 "github.com/grafana/pyroscope/pkg/metastore/raftnode/raftnodepb" 11 ) 12 13 func IsRaftLeadershipError(err error) bool { 14 return errors.Is(err, raft.ErrLeadershipLost) || 15 errors.Is(err, raft.ErrNotLeader) || 16 errors.Is(err, raft.ErrLeadershipTransferInProgress) || 17 errors.Is(err, raft.ErrRaftShutdown) 18 } 19 20 type RaftLeaderLocator interface { 21 LeaderWithID() (raft.ServerAddress, raft.ServerID) 22 } 23 24 func WithRaftLeaderStatusDetails(err error, raft RaftLeaderLocator) error { 25 if err == nil || !IsRaftLeadershipError(err) { 26 return err 27 } 28 serverAddress, serverID := raft.LeaderWithID() 29 s := status.New(codes.Unavailable, err.Error()) 30 if serverID != "" { 31 s, _ = s.WithDetails(&raftnodepb.RaftNode{ 32 Id: string(serverID), 33 Address: string(serverAddress), 34 }) 35 } 36 return s.Err() 37 } 38 39 func RaftLeaderFromStatusDetails(err error) (*raftnodepb.RaftNode, bool) { 40 s, ok := status.FromError(err) 41 if !ok { 42 return nil, false 43 } 44 if s.Code() != codes.Unavailable { 45 return nil, false 46 } 47 for _, d := range s.Details() { 48 if n, ok := d.(*raftnodepb.RaftNode); ok { 49 return n, true 50 } 51 } 52 return nil, false 53 }