github.com/aergoio/aergo@v1.3.1/consensus/impl/raftv2/waldb.go (about)

     1  package raftv2
     2  
     3  import (
     4  	"errors"
     5  	"github.com/aergoio/aergo/consensus"
     6  	"github.com/aergoio/aergo/types"
     7  	"github.com/aergoio/etcd/raft"
     8  	"github.com/aergoio/etcd/raft/raftpb"
     9  )
    10  
    11  var (
    12  	ErrInvalidEntry       = errors.New("Invalid raftpb.entry")
    13  	ErrWalEntryTooLowTerm = errors.New("term of wal entry is too low")
    14  )
    15  
    16  type WalDB struct {
    17  	consensus.ChainWAL
    18  }
    19  
    20  func NewWalDB(chainWal consensus.ChainWAL) *WalDB {
    21  	return &WalDB{chainWal}
    22  }
    23  
    24  func (wal *WalDB) SaveEntry(state raftpb.HardState, entries []raftpb.Entry) error {
    25  	if len(entries) != 0 {
    26  		walEnts, blocks, confChanges := wal.convertFromRaft(entries)
    27  
    28  		if err := wal.WriteRaftEntry(walEnts, blocks, confChanges); err != nil {
    29  			return err
    30  		}
    31  	}
    32  
    33  	// hardstate must save after entries since entries may include commited one
    34  	if !raft.IsEmptyHardState(state) {
    35  		// save hardstate
    36  		if err := wal.WriteHardState(&state); err != nil {
    37  			return err
    38  		}
    39  	}
    40  
    41  	return nil
    42  }
    43  
    44  func (wal *WalDB) convertFromRaft(entries []raftpb.Entry) ([]*consensus.WalEntry, []*types.Block, []*raftpb.ConfChange) {
    45  	lenEnts := len(entries)
    46  	if lenEnts == 0 {
    47  		return nil, nil, nil
    48  	}
    49  
    50  	getWalEntryType := func(entry *raftpb.Entry) consensus.EntryType {
    51  		switch entry.Type {
    52  		case raftpb.EntryNormal:
    53  			if entry.Data != nil {
    54  				return consensus.EntryBlock
    55  			} else {
    56  				return consensus.EntryEmpty
    57  			}
    58  		case raftpb.EntryConfChange:
    59  			return consensus.EntryConfChange
    60  		default:
    61  			panic("not support raftpb entrytype")
    62  		}
    63  	}
    64  
    65  	getWalData := func(entry *raftpb.Entry) (*types.Block, []byte, error) {
    66  		if entry.Type == raftpb.EntryNormal && entry.Data != nil {
    67  			block, err := unmarshalEntryData(entry.Data)
    68  			if err != nil {
    69  				logger.Error().Str("entry", types.RaftEntryToString(entry)).Msg("failed to unmarshal entry")
    70  				return nil, nil, ErrInvalidEntry
    71  			}
    72  
    73  			return block, block.BlockHash(), nil
    74  		} else {
    75  			return nil, entry.Data, nil
    76  		}
    77  	}
    78  
    79  	getConfChange := func(entry *raftpb.Entry) (*raftpb.ConfChange, error) {
    80  		if entry.Type == raftpb.EntryConfChange {
    81  			cc, _, err := unmarshalConfChangeEntry(entry)
    82  			if err != nil {
    83  				logger.Error().Str("entry", types.RaftEntryToString(entry)).Msg("failed to unmarshal entry")
    84  				return nil, ErrInvalidEntry
    85  			}
    86  			return cc, nil
    87  		}
    88  
    89  		return nil, nil
    90  	}
    91  
    92  	blocks := make([]*types.Block, lenEnts)
    93  	walents := make([]*consensus.WalEntry, lenEnts)
    94  	confChanges := make([]*raftpb.ConfChange, lenEnts)
    95  
    96  	var (
    97  		data []byte
    98  		err  error
    99  	)
   100  	for i, entry := range entries {
   101  		if blocks[i], data, err = getWalData(&entry); err != nil {
   102  			panic("entry unmarshalEntryData error")
   103  		}
   104  
   105  		if confChanges[i], err = getConfChange(&entry); err != nil {
   106  			panic("entry unmarshalEntryConfChange error")
   107  		}
   108  
   109  		walents[i] = &consensus.WalEntry{
   110  			Type:  getWalEntryType(&entry),
   111  			Term:  entry.Term,
   112  			Index: entry.Index,
   113  			Data:  data,
   114  		}
   115  	}
   116  
   117  	return walents, blocks, confChanges
   118  }
   119  
   120  var ErrInvalidWalEntry = errors.New("invalid wal entry")
   121  var ErrWalConvBlock = errors.New("failed to convert bytes of block from wal entry")
   122  
   123  func (wal *WalDB) convertWalToRaft(walEntry *consensus.WalEntry) (*raftpb.Entry, error) {
   124  	var raftEntry = &raftpb.Entry{Term: walEntry.Term, Index: walEntry.Index}
   125  
   126  	getDataFromWalEntry := func(walEntry *consensus.WalEntry) ([]byte, error) {
   127  		if walEntry.Type != consensus.EntryBlock {
   128  			return nil, ErrWalConvBlock
   129  		}
   130  		block, err := wal.GetBlock(walEntry.Data)
   131  		if err != nil {
   132  			return nil, err
   133  		}
   134  		data, err := marshalEntryData(block)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  
   139  		return data, nil
   140  	}
   141  
   142  	switch walEntry.Type {
   143  	case consensus.EntryConfChange:
   144  		raftEntry.Type = raftpb.EntryConfChange
   145  		raftEntry.Data = walEntry.Data
   146  
   147  	case consensus.EntryEmpty:
   148  		raftEntry.Type = raftpb.EntryNormal
   149  		raftEntry.Data = nil
   150  
   151  	case consensus.EntryBlock:
   152  		data, err := getDataFromWalEntry(walEntry)
   153  		if err != nil {
   154  			return nil, err
   155  		}
   156  		raftEntry.Data = data
   157  	default:
   158  		return nil, ErrInvalidWalEntry
   159  	}
   160  
   161  	return raftEntry, nil
   162  }
   163  
   164  var (
   165  	ErrWalGetHardState = errors.New("failed to read hard state")
   166  	ErrWalGetLastIdx   = errors.New("failed to read last Idx")
   167  )
   168  
   169  // ReadAll returns hard state, all uncommitted entries
   170  // - read last hard state
   171  // - read  all uncommited entries after snapshot index
   172  func (wal *WalDB) ReadAll(snapshot *raftpb.Snapshot) (id *consensus.RaftIdentity, state *raftpb.HardState, ents []raftpb.Entry, err error) {
   173  	if id, err = wal.GetIdentity(); err != nil {
   174  		return nil, state, ents, err
   175  	}
   176  
   177  	state, err = wal.GetHardState()
   178  	if err != nil {
   179  		return id, state, ents, ErrWalGetHardState
   180  	}
   181  
   182  	commitIdx := state.Commit
   183  	lastIdx, err := wal.GetRaftEntryLastIdx()
   184  	if err != nil {
   185  		return id, state, ents, ErrWalGetLastIdx
   186  	}
   187  
   188  	var snapIdx, snapTerm uint64
   189  	if snapshot != nil {
   190  		snapIdx = snapshot.Metadata.Index
   191  		snapTerm = snapshot.Metadata.Term
   192  	}
   193  
   194  	logger.Info().Uint64("snapidx", snapIdx).Uint64("snapterm", snapTerm).Uint64("commit", commitIdx).Uint64("last", lastIdx).Msg("read all entries of wal")
   195  
   196  	start := snapIdx + 1
   197  
   198  	for i := start; i <= lastIdx; i++ {
   199  		walEntry, err := wal.GetRaftEntry(i)
   200  		// if snapshot is nil, initial confchange entry isn't saved to db
   201  		if err != nil {
   202  			logger.Error().Err(err).Uint64("idx", i).Msg("failed to get raft entry")
   203  			return id, state, nil, err
   204  		}
   205  
   206  		if walEntry.Term < snapTerm {
   207  			logger.Error().Str("wal", walEntry.ToString()).Err(ErrWalEntryTooLowTerm).Msg("invalid wal entry")
   208  			return id, state, nil, ErrWalEntryTooLowTerm
   209  		}
   210  
   211  		raftEntry, err := wal.convertWalToRaft(walEntry)
   212  		if err != nil {
   213  			return id, state, nil, err
   214  		}
   215  
   216  		logger.Debug().Str("walentry", walEntry.ToString()).Msg("read wal entry")
   217  		ents = append(ents, *raftEntry)
   218  	}
   219  
   220  	return id, state, ents, nil
   221  }