feat: initialize project structure and error types

- go.mod with github.com/dailz/go-kv, Go 1.26.3, testify
- config/config.go with WalConfig, Validate() with checked arithmetic
- errors.go with sentinel errors (ErrCommitUnknown, ErrWriteStopped, etc.)
- wal/constants.go with all WAL format constants and enums
- wal/header.go with WAL File Header encode/decode (CRC32 IEEE)
- wal/record.go with Physical Record codec, block boundary, SplitIntoRecords
- wal/entry.go with WAL Entry codec (varint keys/values, OpType, ValueKind)
- wal/sequence.go with SequenceManager (atomic, CAS, overflow-safe)
- manifest/manifest.go with MANIFEST stub (Load/Save atomic)
- manifest/current.go with CURRENT file (WriteCurrent/ReadCurrent)
- Comprehensive tests for all modules
- .golangci.yml configuration
This commit is contained in:
dailz
2026-06-12 13:23:27 +08:00
parent 59de99ca0b
commit cf913b1d52
52 changed files with 4538 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
package manifest
import (
"fmt"
"os"
"strconv"
"strings"
)
// WriteCurrent writes the CURRENT file to dir with a best-effort atomic rename.
// The file contains the active WAL segment filename (e.g. "segment-5.wal").
// CURRENT is only a write-side hint; it may be missing or stale after a crash.
func WriteCurrent(dir string, segmentID uint64) error {
content := fmt.Sprintf("segment-%d.wal\n", segmentID)
tmpPath := dir + "/CURRENT.tmp"
if err := os.WriteFile(tmpPath, []byte(content), 0o644); err != nil {
return fmt.Errorf("write current tmp: %w", err)
}
if err := os.Rename(tmpPath, dir+"/CURRENT"); err != nil {
return fmt.Errorf("rename current: %w", err)
}
return nil
}
// ReadCurrent reads the CURRENT file from dir and returns the segment ID.
// If the file does not exist or cannot be parsed, it returns 0, false with no error.
func ReadCurrent(dir string) (segmentID uint64, ok bool) {
data, err := os.ReadFile(dir + "/CURRENT")
if err != nil {
return 0, false
}
line := strings.TrimSpace(string(data))
// Expected format: "segment-N.wal"
if !strings.HasPrefix(line, "segment-") || !strings.HasSuffix(line, ".wal") {
return 0, false
}
idStr := strings.TrimSuffix(strings.TrimPrefix(line, "segment-"), ".wal")
id, err := strconv.ParseUint(idStr, 10, 64)
if err != nil {
return 0, false
}
return id, true
}