...

Source file src/go.etcd.io/etcd/raft/v3/bootstrap.go

Documentation: go.etcd.io/etcd/raft/v3

     1  // Copyright 2015 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package raft
    16  
    17  import (
    18  	"errors"
    19  
    20  	pb "go.etcd.io/etcd/raft/v3/raftpb"
    21  )
    22  
    23  // Bootstrap initializes the RawNode for first use by appending configuration
    24  // changes for the supplied peers. This method returns an error if the Storage
    25  // is nonempty.
    26  //
    27  // It is recommended that instead of calling this method, applications bootstrap
    28  // their state manually by setting up a Storage that has a first index > 1 and
    29  // which stores the desired ConfState as its InitialState.
    30  func (rn *RawNode) Bootstrap(peers []Peer) error {
    31  	if len(peers) == 0 {
    32  		return errors.New("must provide at least one peer to Bootstrap")
    33  	}
    34  	lastIndex, err := rn.raft.raftLog.storage.LastIndex()
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	if lastIndex != 0 {
    40  		return errors.New("can't bootstrap a nonempty Storage")
    41  	}
    42  
    43  	// We've faked out initial entries above, but nothing has been
    44  	// persisted. Start with an empty HardState (thus the first Ready will
    45  	// emit a HardState update for the app to persist).
    46  	rn.prevHardSt = emptyState
    47  
    48  	// TODO(tbg): remove StartNode and give the application the right tools to
    49  	// bootstrap the initial membership in a cleaner way.
    50  	rn.raft.becomeFollower(1, None)
    51  	ents := make([]pb.Entry, len(peers))
    52  	for i, peer := range peers {
    53  		cc := pb.ConfChange{Type: pb.ConfChangeAddNode, NodeID: peer.ID, Context: peer.Context}
    54  		data, err := cc.Marshal()
    55  		if err != nil {
    56  			return err
    57  		}
    58  
    59  		ents[i] = pb.Entry{Type: pb.EntryConfChange, Term: 1, Index: uint64(i + 1), Data: data}
    60  	}
    61  	rn.raft.raftLog.append(ents...)
    62  
    63  	// Now apply them, mainly so that the application can call Campaign
    64  	// immediately after StartNode in tests. Note that these nodes will
    65  	// be added to raft twice: here and when the application's Ready
    66  	// loop calls ApplyConfChange. The calls to addNode must come after
    67  	// all calls to raftLog.append so progress.next is set after these
    68  	// bootstrapping entries (it is an error if we try to append these
    69  	// entries since they have already been committed).
    70  	// We do not set raftLog.applied so the application will be able
    71  	// to observe all conf changes via Ready.CommittedEntries.
    72  	//
    73  	// TODO(bdarnell): These entries are still unstable; do we need to preserve
    74  	// the invariant that committed < unstable?
    75  	rn.raft.raftLog.committed = uint64(len(ents))
    76  	for _, peer := range peers {
    77  		rn.raft.applyConfChange(pb.ConfChange{NodeID: peer.ID, Type: pb.ConfChangeAddNode}.AsV2())
    78  	}
    79  	return nil
    80  }
    81  

View as plain text