// processInternalRaftRequest sends a message to nodes participating // in the raft to apply a log entry and then waits for it to be applied // on the server. It will block until the update is performed, there is // an error or until the raft node finalizes all the proposals on node // shutdown. func (n *Node) processInternalRaftRequest(ctx context.Context, r *api.InternalRaftRequest, cb func()) (proto.Message, error) { n.stopMu.RLock() if !n.canSubmitProposal() { n.stopMu.RUnlock() return nil, ErrStopped } n.waitProp.Add(1) defer n.waitProp.Done() n.stopMu.RUnlock() r.ID = n.reqIDGen.Next() ch := n.wait.register(r.ID, cb) // Do this check after calling register to avoid a race. if atomic.LoadUint32(&n.signalledLeadership) != 1 { n.wait.cancel(r.ID) return nil, ErrLostLeadership } data, err := r.Marshal() if err != nil { n.wait.cancel(r.ID) return nil, err } if len(data) > store.MaxTransactionBytes { n.wait.cancel(r.ID) return nil, ErrRequestTooLarge } // This must use the context which is cancelled by stop() to avoid a // deadlock on shutdown. err = n.Propose(n.Ctx, data) if err != nil { n.wait.cancel(r.ID) return nil, err } select { case x, ok := <-ch: if ok { res := x.(*applyResult) return res.resp, res.err } return nil, ErrLostLeadership case <-n.Ctx.Done(): n.wait.cancel(r.ID) return nil, ErrStopped case <-ctx.Done(): n.wait.cancel(r.ID) return nil, ctx.Err() } }
// processInternalRaftRequest sends a message to nodes participating // in the raft to apply a log entry and then waits for it to be applied // on the server. It will block until the update is performed, there is // an error or until the raft node finalizes all the proposals on node // shutdown. func (n *Node) processInternalRaftRequest(ctx context.Context, r *api.InternalRaftRequest, cb func()) (proto.Message, error) { n.waitProp.Add(1) defer n.waitProp.Done() if !n.canSubmitProposal() { return nil, ErrStopped } r.ID = n.reqIDGen.Next() ch := n.wait.register(r.ID, cb) // Do this check after calling register to avoid a race. if !n.IsLeader() { n.wait.cancel(r.ID) return nil, ErrLostLeadership } data, err := r.Marshal() if err != nil { n.wait.cancel(r.ID) return nil, err } if len(data) > store.MaxTransactionBytes { n.wait.cancel(r.ID) return nil, ErrRequestTooLarge } err = n.Propose(ctx, data) if err != nil { n.wait.cancel(r.ID) return nil, err } select { case x, ok := <-ch: if ok { res := x.(*applyResult) return res.resp, res.err } return nil, ErrLostLeadership case <-n.Ctx.Done(): n.wait.cancel(r.ID) return nil, ErrStopped case <-ctx.Done(): n.wait.cancel(r.ID) return nil, ctx.Err() } }