| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- package main
- import (
- "encoding/binary"
- "fmt"
- "io"
- "os"
- "text/tabwriter"
- "igit.com/xbase/raft/db"
- )
- const (
- RecordTypePut = 0x01
- RecordTypeDelete = 0x02
- // CRC(4) + Type(1) + KeyLen(2) + ValLen(4) + CommitIndex(8)
- HeaderSize = 19
- )
- func main() {
- if len(os.Args) < 2 {
- fmt.Println("Usage: go run main.go <data_dir>")
- return
- }
- dataDir := os.Args[1]
- dataFile := dataDir + "/values.data"
- f, err := os.Open(dataFile)
- if err != nil {
- fmt.Printf("Error opening file: %v\n", err)
- return
- }
- defer f.Close()
- fmt.Printf("Scanning storage file: %s\n\n", dataFile)
- w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', tabwriter.Debug)
- fmt.Fprintln(w, "Offset\tType\tCRC\tKeyLen\tValLen\tCommit\tKey\tValue")
- fmt.Fprintln(w, "------\t----\t---\t------\t------\t------\t---\t-----")
- offset := int64(0)
- reader := f
- for {
- // Read Header
- header := make([]byte, HeaderSize)
- if _, err := reader.ReadAt(header, offset); err != nil {
- if err == io.EOF {
- break
- }
- // If we hit EOF during read, it might be partial read at end
- if n, _ := reader.ReadAt(header, offset); n == 0 {
- break
- }
- fmt.Printf("Error reading header at offset %d: %v\n", offset, err)
- break
- }
- // Parse Header
- crc := binary.LittleEndian.Uint32(header[0:4])
- recType := header[4]
- keyLen := binary.LittleEndian.Uint16(header[5:7])
- valLen := binary.LittleEndian.Uint32(header[7:11])
- commitIndex := binary.LittleEndian.Uint64(header[11:19])
- typeStr := "PUT"
- if recType == RecordTypeDelete {
- typeStr = "DEL"
- } else if recType != RecordTypePut {
- typeStr = fmt.Sprintf("UNK(%d)", recType)
- }
- // Read Data (Key + Value)
- totalDataLen := int(keyLen) + int(valLen)
- data := make([]byte, totalDataLen)
- if _, err := reader.ReadAt(data, offset+HeaderSize); err != nil {
- fmt.Printf("Error reading data at offset %d: %v\n", offset+HeaderSize, err)
- break
- }
- key := string(data[:keyLen])
- val := string(data[keyLen:])
- // Truncate long values for display
- displayVal := val
- if len(displayVal) > 30 {
- displayVal = displayVal[:27] + "..."
- }
- if recType == RecordTypeDelete {
- displayVal = "<tombstone>"
- }
- fmt.Fprintf(w, "%d\t%s\t%08X\t%d\t%d\t%d\t%s\t%q\n",
- offset, typeStr, crc, keyLen, valLen, commitIndex, key, displayVal)
- offset += int64(HeaderSize + totalDataLen)
- }
- w.Flush()
- fmt.Println()
- // Also try to open Engine to verify Index Rebuild
- e, err := db.NewEngine(dataDir)
- if err == nil {
- fmt.Println("Engine Load Check: SUCCESS (Index rebuilt from disk)")
- e.Close()
- } else {
- fmt.Printf("Engine Load Check: FAILED (%v)\n", err)
- }
- }
|