Repository: etcd-io/etcd
File: tools/etcd-dump-logs/main.go (Go)

Code before:
			fmt.Print("Snapshot:\nempty\n")
		default:
			log.Fatalf("Failed loading snapshot: %v", err)
		}
		fmt.Println("Start dumping log entries from snapshot.")
	}

	wd := *waldir
	if wd == "" {
		wd = walDir(dataDir)
	}

	w, err := wal.OpenForRead(zap.NewExample(), wd, walsnap)
	if err != nil {
		log.Fatalf("Failed opening WAL: %v", err)
	}
	wmetadata, state, ents, err := w.ReadAll()
	w.Close()
	if err != nil && (!startFromIndex || !errors.Is(err, wal.ErrSnapshotNotFound)) {
		// ignore ErrSliceOutOfRange if just requesting a specific range of entries
		if !endAtIndex || !errors.Is(err, wal.ErrSliceOutOfRange) {
			log.Fatalf("Failed reading WAL: %v", err)
		}
		log.Printf("Failed reading all WAL: %v", err)
	}
	id, cid := parseWALMetadata(wmetadata)
	vid := types.ID(state.Vote)
	fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n",
		id, cid, state.Term, state.Commit, vid)
	if endAtIndex {
		entries := make([]raftpb.Entry, 0)
		for _, e := range ents {
			if e.Index > *endIndex {
				continue
			}
			entries = append(entries, e)
		}
		return entries
	}
	return ents
}

func walDir(dataDir string) string { return filepath.Join(dataDir, "member", "wal") }

func snapDir(dataDir string) string { return filepath.Join(dataDir, "member", "snap") }

func parseWALMetadata(b []byte) (id, cid types.ID) {
	var metadata etcdserverpb.Metadata
	pbutil.MustUnmarshal(&metadata, b)
	id = types.ID(metadata.NodeID)
	cid = types.ID(metadata.ClusterID)

Review comment (suggestion):
I recall we see similar error `wal.ErrSliceOutOfRange` in downgrade e2e test. Please add a comment to explain why we ignore wal.ErrSliceOutOfRange here.

Code after:
			}
			fmt.Printf("Snapshot:\nterm=%d index=%d nodes=%s confstate=%s\n",
				walsnap.Term, walsnap.Index, nodes, confStateJSON)
		case errors.Is(err, snap.ErrNoSnapshot):
			fmt.Print("Snapshot:\nempty\n")
		default:
			log.Fatalf("Failed loading snapshot: %v", err)
		}
		fmt.Println("Start dumping log entries from snapshot.")
	}

	wd := *waldir
	if wd == "" {
		wd = walDir(dataDir)
	}

	w, err := wal.OpenForRead(zap.NewExample(), wd, walsnap)
	if err != nil {
		log.Fatalf("Failed opening WAL: %v", err)
	}
	wmetadata, state, ents, err := w.ReadAll()
	w.Close()
	if err != nil && (!startFromIndex || !errors.Is(err, wal.ErrSnapshotNotFound)) {
		// ReadAll might return ErrSliceOutOfRange and the first series of entries if the server is offline for a while and receives a snapshot from leader.
		// It is ok to ignore ErrSliceOutOfRange if just requesting a specific range of entries
		if !endAtIndex || !errors.Is(err, wal.ErrSliceOutOfRange) {
			log.Fatalf("Failed reading WAL: %v", err)
		}
		log.Printf("Failed reading all WAL: %v", err)
	}
	id, cid := parseWALMetadata(wmetadata)
	vid := types.ID(state.Vote)
	fmt.Printf("WAL metadata:\nnodeID=%s clusterID=%s term=%d commitIndex=%d vote=%s\n",
		id, cid, state.Term, state.Commit, vid)
	if endAtIndex {
		entries := make([]raftpb.Entry, 0)
		for _, e := range ents {
			// WAL might contain entries with e.Index >= *endIndex from prev term, then e.Index < *endIndex in the next term.
			// We cannot break when e.Index >= *endIndex.
			if e.Index >= *endIndex {
				continue
			}
			entries = append(entries, e)
		}
		return entries
	}
	return ents
}

func walDir(dataDir string) string { return filepath.Join(dataDir, "member", "wal") }