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  }