github.com/grafana/pyroscope@v1.18.0/pkg/metastore/metastore_raft.go (about)

     1  package metastore
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"go.etcd.io/bbolt"
     8  	"google.golang.org/protobuf/proto"
     9  
    10  	"github.com/grafana/pyroscope/pkg/metastore/fsm"
    11  	"github.com/grafana/pyroscope/pkg/metastore/raftnode"
    12  	"github.com/grafana/pyroscope/pkg/metastore/raftnode/raftnodepb"
    13  )
    14  
    15  // Raft represents a Raft consensus protocol interface. Any modifications to
    16  // the state should be proposed through the Raft interface.
    17  type Raft interface {
    18  	Propose(context.Context, fsm.RaftLogEntryType, proto.Message) (proto.Message, error)
    19  }
    20  
    21  // State represents a consistent read-only view of the metastore.
    22  // The write interface is provided through the FSM raft command handlers.
    23  type State interface {
    24  	ConsistentRead(context.Context, func(*bbolt.Tx, raftnode.ReadIndex)) error
    25  }
    26  
    27  // newFollowerReader creates a new follower reader – implementation of the
    28  // Follower Read pattern. See raftnode.StateReader for details.
    29  // The provided client is used to communicate with the leader node.
    30  func (m *Metastore) newFollowerReader(
    31  	client raftnodepb.RaftNodeServiceClient,
    32  	node *raftnode.Node,
    33  	fsm *fsm.FSM,
    34  ) *raftnode.StateReader[*bbolt.Tx] {
    35  	return raftnode.NewStateReader[*bbolt.Tx](
    36  		// NOTE(kolesnikovae): replace the client with the local
    37  		// raft node to implement Leader Read pattern.
    38  		&leaderNode{client: client, timeout: m.config.Raft.ApplyTimeout},
    39  		&localNode{node: node, fsm: fsm},
    40  		m.config.Raft.LogIndexCheckInterval,
    41  		m.config.Raft.ReadIndexMaxDistance,
    42  	)
    43  }
    44  
    45  // newLeaderReader creates a new leader reader – implementation of the
    46  // Leader Read pattern. See raftnode.StateReader for details.
    47  // The provided node is treated as the leader, attempt to read from
    48  // the local node in follower state will fail.
    49  func (m *Metastore) newLeaderReader(
    50  	node *raftnode.Node,
    51  	fsm *fsm.FSM,
    52  ) *raftnode.StateReader[*bbolt.Tx] {
    53  	return raftnode.NewStateReader[*bbolt.Tx](
    54  		node,
    55  		&localNode{node: node, fsm: fsm},
    56  		m.config.Raft.LogIndexCheckInterval,
    57  		m.config.Raft.ReadIndexMaxDistance,
    58  	)
    59  }
    60  
    61  // leaderNode is an implementation of raftnode.Leader interface that
    62  // communicates with the leader using the RaftNode service client to
    63  // acquire its commit index (ReadIndex).
    64  type leaderNode struct {
    65  	client  raftnodepb.RaftNodeServiceClient
    66  	timeout time.Duration
    67  }
    68  
    69  func (l *leaderNode) ReadIndex() (read raftnode.ReadIndex, err error) {
    70  	ctx, cancel := context.WithTimeout(context.Background(), l.timeout)
    71  	defer cancel()
    72  	resp, err := l.client.ReadIndex(ctx, new(raftnodepb.ReadIndexRequest))
    73  	if err != nil {
    74  		return read, err
    75  	}
    76  	read.CommitIndex = resp.CommitIndex
    77  	read.Term = resp.Term
    78  	return read, nil
    79  }
    80  
    81  // localNode represents the state machine of the local node.
    82  // In the current implementation, fsm.FSM does keep track of
    83  // the applied index, therefore we consult to raft to get it.
    84  type localNode struct {
    85  	node *raftnode.Node
    86  	fsm  *fsm.FSM
    87  }
    88  
    89  func (f *localNode) AppliedIndex() uint64 { return f.node.AppliedIndex() }
    90  
    91  func (f *localNode) Read(fn func(*bbolt.Tx)) error { return f.fsm.Read(fn) }