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 ") 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 = "" } 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) } }