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) }