package manifest import ( "fmt" "os" "strconv" "strings" ) // Manifest holds database metadata used for recovery. // The MANIFEST file stores the recovery checkpoint so that // recovery knows which segments are already confirmed durable. type Manifest struct { RecoverySegmentID uint64 } // Load reads the MANIFEST file from dir. // If the file does not exist, it returns a zero-value Manifest with no error (fresh DB). func Load(dir string) (*Manifest, error) { path := dir + "/MANIFEST" data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return &Manifest{RecoverySegmentID: 0}, nil } return nil, fmt.Errorf("read manifest: %w", err) } line := strings.TrimSpace(string(data)) if !strings.HasPrefix(line, "recovery_segment_id:") { return nil, fmt.Errorf("manifest: invalid format: %q", line) } idStr := strings.TrimPrefix(line, "recovery_segment_id:") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { return nil, fmt.Errorf("manifest: parse recovery_segment_id: %w", err) } return &Manifest{RecoverySegmentID: id}, nil } // Save atomically writes the MANIFEST file to dir with the given recoverySegmentID. func Save(dir string, recoverySegmentID uint64) error { content := fmt.Sprintf("recovery_segment_id:%d\n", recoverySegmentID) tmpPath := dir + "/MANIFEST.tmp" if err := os.WriteFile(tmpPath, []byte(content), 0o644); err != nil { return fmt.Errorf("write manifest tmp: %w", err) } if err := os.Rename(tmpPath, dir+"/MANIFEST"); err != nil { return fmt.Errorf("rename manifest: %w", err) } return nil }