Enhance DiskFS initialization and error handling

- Updated `disk.New` to support asynchronous initialization for large caches, improving responsiveness during startup.
- Introduced an eviction function parameter to `disk.New`, ensuring proper handling of over-capacity scenarios.
- Enhanced error handling in various components, including memory and disk tests, to ensure robustness and clarity.
- Refactored tests to validate new behaviors, including checks for delayed attachment and proper error propagation.
- Removed obsolete error handling code and tests related to the now-deleted errors package, streamlining the codebase.
This commit is contained in:
2026-05-27 13:15:33 -05:00
parent 4861f93e6f
commit feda55e225
18 changed files with 584 additions and 1380 deletions
+71 -14
View File
@@ -3,6 +3,7 @@ package memory
import (
"fmt"
"io"
"strings"
"sync"
"sync/atomic"
"testing"
@@ -11,7 +12,10 @@ import (
func TestMemoryFS_Basic(t *testing.T) {
t.Parallel()
m := New(1024 * 1024)
m, err := New(1024 * 1024)
if err != nil {
t.Fatal(err)
}
if m.Name() != "MemoryFS" {
t.Error("bad name")
}
@@ -52,7 +56,10 @@ func TestMemoryFS_Basic(t *testing.T) {
func TestMemoryFS_EvictUnderPressure(t *testing.T) {
t.Parallel()
m := New(500)
m, err := New(500)
if err != nil {
t.Fatal(err)
}
// create 3x200 = 600 >500, should trigger internal? but direct evict call
for i := 0; i < 3; i++ {
w, _ := m.Create("f"+string(rune('0'+i)), 200)
@@ -69,7 +76,10 @@ func TestMemoryFS_EvictUnderPressure(t *testing.T) {
func TestMemoryFS_SizeNeverExceedsAfterEvict(t *testing.T) {
t.Parallel()
cap := int64(1000)
m := New(cap)
m, err := New(cap)
if err != nil {
t.Fatal(err)
}
// Cycle through strategies (randomized feel via mod), use testKey, stricter post-evict with documented epsilon.
strats := []func(uint) uint{m.EvictLRU, func(n uint) uint { return m.EvictBySize(n, true) }, m.EvictFIFO, m.EvictLFU, m.EvictHybrid}
for i := 0; i < 50; i++ { // more cycles
@@ -97,7 +107,10 @@ func TestMemoryFS_ConcurrentCreateOpenDelete(t *testing.T) {
t.Skip()
}
t.Parallel()
m := New(10 * 1024 * 1024)
m, err := New(10 * 1024 * 1024)
if err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
const N = 50
var ops int64
@@ -137,7 +150,10 @@ func TestMemoryFS_ConcurrentCreateOpenDelete(t *testing.T) {
}
func BenchmarkMemoryFS_CreateOpen(b *testing.B) {
m := New(64 * 1024 * 1024)
m, err := New(64 * 1024 * 1024)
if err != nil {
b.Fatal(err)
}
data := make([]byte, 4096)
b.ReportAllocs()
b.ResetTimer()
@@ -162,7 +178,10 @@ func BenchmarkMemoryFS_CreateOpen(b *testing.B) {
// BenchmarkMemoryFS_EvictionUnderPressure exercises memory eviction under synthetic pressure (parallels BenchmarkDiskFS_EvictionUnderPressure).
// Uses cycling keys via testKey for stable behavior; exercises LRU path (other strategies lightly covered via existing tests + EvictHybrid uses DecayedScore).
func BenchmarkMemoryFS_EvictionUnderPressure(b *testing.B) {
m := New(1 * 1024 * 1024)
m, err := New(1 * 1024 * 1024)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -183,7 +202,10 @@ func BenchmarkMemoryFS_EvictionUnderPressure(b *testing.B) {
// BenchmarkMemoryFS_EvictBySizeUnderPressure parallels the disk eviction strategy testing.
// Exercises EvictBySize under repeated pressure.
func BenchmarkMemoryFS_EvictBySizeUnderPressure(b *testing.B) {
m := New(1 * 1024 * 1024)
m, err := New(1 * 1024 * 1024)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -195,7 +217,7 @@ func BenchmarkMemoryFS_EvictBySizeUnderPressure(b *testing.B) {
w.Write(make([]byte, 64*1024))
w.Close()
}
m.EvictBySize(512 * 1024, true) // ascending = evict smallest first
m.EvictBySize(512*1024, true) // ascending = evict smallest first
}
_ = m // keep
}
@@ -203,7 +225,10 @@ func BenchmarkMemoryFS_EvictBySizeUnderPressure(b *testing.B) {
// BenchmarkMemoryFS_EvictHybridUnderPressure exercises the hybrid strategy (which uses
// the centralized DecayedScore) under pressure. Provides coverage for the time-decayed scoring.
func BenchmarkMemoryFS_EvictHybridUnderPressure(b *testing.B) {
m := New(1 * 1024 * 1024)
m, err := New(1 * 1024 * 1024)
if err != nil {
b.Fatal(err)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
@@ -222,7 +247,10 @@ func BenchmarkMemoryFS_EvictHybridUnderPressure(b *testing.B) {
func TestMemoryFS_Stats(t *testing.T) {
t.Parallel()
m := New(1024)
m, err := New(1024)
if err != nil {
t.Fatal(err)
}
stats := m.GetFragmentationStats()
if stats["buffer_count"] != 0 {
t.Error("initial buffers >0?")
@@ -242,7 +270,10 @@ func TestMemoryFS_ConcurrentCloseAndEvict_RaceFree(t *testing.T) {
t.Skip()
}
t.Parallel()
m := New(2 * 1024 * 1024) // 2MB
m, err := New(2 * 1024 * 1024) // 2MB
if err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
stopCh := make(chan struct{})
const writers = 3
@@ -317,7 +348,10 @@ func TestMemoryFS_ConcurrentCloseAndEvict_RaceFree(t *testing.T) {
func TestMemoryFS_EvictVariantsAndErrors(t *testing.T) {
t.Parallel()
m := New(800)
m, err := New(800)
if err != nil {
t.Fatal(err)
}
// populate
for i := 0; i < 4; i++ {
w, _ := m.Create("ev"+string(rune('0'+i)), 150)
@@ -361,7 +395,10 @@ func TestMemoryFS_EvictVariantsAndErrors(t *testing.T) {
func TestMemoryFS_AllEvictStrategies(t *testing.T) {
t.Parallel()
m := New(300)
m, err := New(300)
if err != nil {
t.Fatal(err)
}
for i := 0; i < 3; i++ {
w, _ := m.Create("s"+string(rune(i)), 120)
w.Write(make([]byte, 120))
@@ -387,7 +424,10 @@ func TestMemoryFS_EvictBoundedLargeN(t *testing.T) {
}
t.Parallel()
cap := int64(128 * 1024)
m := New(cap)
m, err := New(cap)
if err != nil {
t.Fatal(err)
}
const nFiles = 3000 // >> maxEvictBatch
const fSize = 128
for i := 0; i < nFiles; i++ {
@@ -417,3 +457,20 @@ func TestMemoryFS_EvictBoundedLargeN(t *testing.T) {
}
_ = totalEvicted
}
// TestMemoryFS_NewInvalidCapacity exercises the new error return (was panic) for ctor hygiene (Item 3 coverage).
func TestMemoryFS_NewInvalidCapacity(t *testing.T) {
t.Parallel()
_, err := New(0)
if err == nil {
t.Fatal("expected error for capacity=0")
}
if !strings.Contains(err.Error(), "must be greater than 0") {
t.Errorf("err %q missing 'must be greater than 0'", err)
}
_, err = New(-1)
if err == nil || !strings.Contains(err.Error(), "must be greater than 0") {
t.Errorf("negative capacity should return error containing phrase, got %v", err)
}
}