Repository: etcd-io/etcd
File: server/etcdserver/bootstrap.go (Go)

Code before:
		)
	}
	cl := membership.NewCluster(cfg.Logger, membership.WithMaxLearners(cfg.MaxLearners))

	scaleUpLearners := false
	if err := membership.ValidateMaxLearnerConfig(cfg.MaxLearners, cl.Members(), scaleUpLearners); err != nil {
		return nil, err
	}

	cl.SetID(meta.nodeID, meta.clusterID)
	return &bootstrappedCluster{
		cl:     cl,
		nodeID: meta.nodeID,
	}, nil
}

func recoverSnapshot(cfg config.ServerConfig, be backend.Backend, beExist bool, beHooks *serverstorage.BackendHooks, ci cindex.ConsistentIndexer, ss *snap.Snapshotter) (*raftpb.Snapshot, backend.Backend, error) {
	// Find a snapshot to start/restart a raft node
	walSnaps, err := wal.ValidSnapshotEntries(cfg.Logger, cfg.WALDir())
	if err != nil {
		return nil, be, err
	}
	// snapshot files can be orphaned if etcd crashes after writing them but before writing the corresponding
	// bwal log entries
	snapshot, err := ss.LoadNewestAvailable(walSnaps)
	if err != nil && !errors.Is(err, snap.ErrNoSnapshot) {
		return nil, be, err
	}

	if snapshot != nil {
		cfg.Logger.Info(
			"recovered v2 store from snapshot",
			zap.Uint64("snapshot-index", snapshot.Metadata.Index),
			zap.String("snapshot-size", humanize.Bytes(uint64(snapshot.Size()))),
		)

		if be, err = serverstorage.RecoverSnapshotBackend(cfg, be, *snapshot, beExist, beHooks); err != nil {
			cfg.Logger.Panic("failed to recover v3 backend from snapshot", zap.Error(err))
		}
		// A snapshot db may have already been recovered, and the old db should have
		// already been closed in this case, so we should set the backend again.
		ci.SetBackend(be)

		if beExist {
			// TODO: remove kvindex != 0 checking when we do not expect users to upgrade
			// etcd from pre-3.0 release.
			kvindex := ci.ConsistentIndex()
			if kvindex < snapshot.Metadata.Index {
				if kvindex != 0 {
					return nil, be, fmt.Errorf("database file (%v index %d) does not match with snapshot (index %d)", cfg.BackendPath(), kvindex, snapshot.Metadata.Index)
				}

Review comment (suggestion):
ETCD commits the snapshot to disk first and then appends the WAL. It should be fine and it won't break test cases.

Code after:
		)
	}
	cl := membership.NewCluster(cfg.Logger, membership.WithMaxLearners(cfg.MaxLearners))

	scaleUpLearners := false
	if err := membership.ValidateMaxLearnerConfig(cfg.MaxLearners, cl.Members(), scaleUpLearners); err != nil {
		return nil, err
	}

	cl.SetID(meta.nodeID, meta.clusterID)
	return &bootstrappedCluster{
		cl:     cl,
		nodeID: meta.nodeID,
	}, nil
}

func recoverSnapshot(cfg config.ServerConfig, be backend.Backend, beExist bool, beHooks *serverstorage.BackendHooks, ci cindex.ConsistentIndexer) (*raftpb.Snapshot, backend.Backend, error) {
	// Find a snapshot to start/restart a raft node
	walSnaps, err := wal.ValidSnapshotEntries(cfg.Logger, cfg.WALDir())
	if err != nil {
		return nil, be, err
	}

	var snapshot *raftpb.Snapshot
	if len(walSnaps) > 0 {
		idx := len(walSnaps) - 1
		snapshot = &raftpb.Snapshot{
			Metadata: raftpb.SnapshotMetadata{
				Term:  walSnaps[idx].Term,
				Index: walSnaps[idx].Index,
			},
		}
		if walSnaps[idx].ConfState != nil {
			snapshot.Metadata.ConfState = *walSnaps[idx].ConfState
		}
		cfg.Logger.Info("constructed a snapshot from WAL record",
			zap.Uint64("snapshot-index", snapshot.Metadata.Index),
			zap.String("snapshot-size", humanize.Bytes(uint64(snapshot.Size()))),
			zap.String("confState", snapshot.Metadata.ConfState.String()),
		)
	}

	if snapshot != nil {
		if be, err = serverstorage.RecoverSnapshotBackend(cfg, be, *snapshot, beExist, beHooks); err != nil {
			cfg.Logger.Panic("failed to recover v3 backend from snapshot", zap.Error(err))
		}
		// A snapshot db may have already been recovered, and the old db should have
		// already been closed in this case, so we should set the backend again.
		ci.SetBackend(be)

		if beExist {