From a0dc746ab1dbec94bc0e19c5c56fcb4c0c35a1c2 Mon Sep 17 00:00:00 2001
From: molon <3739161+molon@users.noreply.github.com>
Date: Thu, 27 Nov 2025 10:14:13 +0800
Subject: [PATCH 1/2] refactor(state): rename StateTooStale to StateRotten for
clarity
---
README.md | 10 +++++-----
README_ZH.md | 10 +++++-----
client.go | 10 +++++-----
client_test.go | 18 +++++++++---------
entry.go | 2 +-
entry_test.go | 10 +++++-----
error.go | 4 ++--
types.go | 6 +++---
8 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/README.md b/README.md
index e5ea3d9..11d56f2 100644
--- a/README.md
+++ b/README.md
@@ -113,8 +113,8 @@ sequenceDiagram
Upstream-->>SF: new value
SF->>NFCache: Del(key)
SF->>Cache: Set(key, value)
- else Cache Hit + Stale (serveStale=false) or TooStale
- Cache-->>Client: value (stale/too stale)
+ else Cache Hit + Stale (serveStale=false) or Rotten
+ Cache-->>Client: value (stale/rotten)
Note over Client: Skip NotFoundCache, fetch directly
(backend has data)
Client->>SF: Fetch(key)
SF->>Upstream: Fetch(key)
@@ -143,8 +143,8 @@ sequenceDiagram
SF->>NFCache: Del(key)
SF->>Cache: Set(key, value)
end
- else NotFound Hit + Stale (serveStale=false) or TooStale or Miss
- NFCache-->>Client: stale/too stale/miss
+ else NotFound Hit + Stale (serveStale=false) or Rotten or Miss
+ NFCache-->>Client: stale/rotten/miss
Client->>SF: Fetch(key)
SF->>Upstream: Fetch(key)
alt Key Exists
@@ -343,7 +343,7 @@ client := cachex.NewClient(
if age < 5*time.Second + 25*time.Second {
return cachex.StateStale
}
- return cachex.StateTooStale
+ return cachex.StateRotten
}),
cachex.WithServeStale[*Product](true),
)
diff --git a/README_ZH.md b/README_ZH.md
index ab81757..dcf7230 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -113,8 +113,8 @@ sequenceDiagram
Upstream-->>SF: new value
SF->>NFCache: Del(key)
SF->>Cache: Set(key, value)
- else Cache Hit + Stale (serveStale=false) or TooStale
- Cache-->>Client: value (stale/too stale)
+ else Cache Hit + Stale (serveStale=false) or Rotten
+ Cache-->>Client: value (stale/rotten)
Note over Client: Skip NotFoundCache, fetch directly
(backend has data)
Client->>SF: Fetch(key)
SF->>Upstream: Fetch(key)
@@ -143,8 +143,8 @@ sequenceDiagram
SF->>NFCache: Del(key)
SF->>Cache: Set(key, value)
end
- else NotFound Hit + Stale (serveStale=false) or TooStale or Miss
- NFCache-->>Client: stale/too stale/miss
+ else NotFound Hit + Stale (serveStale=false) or Rotten or Miss
+ NFCache-->>Client: stale/rotten/miss
Client->>SF: Fetch(key)
SF->>Upstream: Fetch(key)
alt Key Exists
@@ -343,7 +343,7 @@ client := cachex.NewClient(
if age < 5*time.Second + 25*time.Second {
return cachex.StateStale
}
- return cachex.StateTooStale
+ return cachex.StateRotten
}),
cachex.WithServeStale[*Product](true),
)
diff --git a/client.go b/client.go
index f774dd5..9962478 100644
--- a/client.go
+++ b/client.go
@@ -114,8 +114,8 @@ func (c *Client[T]) get(ctx context.Context, key string, doubleCheck bool) (T, e
return value, nil
}
- case StateTooStale:
- // Too stale, must refresh
+ case StateRotten:
+ // Rotten, must refresh
}
} else if !IsErrKeyNotFound(err) {
return zero, errors.Wrapf(err, "get from backend failed for key: %s", key)
@@ -147,8 +147,8 @@ func (c *Client[T]) get(ctx context.Context, key string, doubleCheck bool) (T, e
}, "key not found in cache for key: %s", key)
}
- case StateTooStale:
- // Too stale, must refresh
+ case StateRotten:
+ // Rotten, must refresh
}
} else if !IsErrKeyNotFound(err) {
return zero, errors.Wrapf(err, "get from notFoundCache failed for key: %s", key)
@@ -415,7 +415,7 @@ func NotFoundWithTTL[T any](cache Cache[time.Time], freshTTL time.Duration, stal
if staleTTL > 0 && age < freshTTL+staleTTL {
return StateStale
}
- return StateTooStale
+ return StateRotten
})
}
diff --git a/client_test.go b/client_test.go
index 39f854d..72ab10e 100644
--- a/client_test.go
+++ b/client_test.go
@@ -86,7 +86,7 @@ func TestClientStaleHandling(t *testing.T) {
if age < 150*time.Millisecond {
return StateStale
}
- return StateTooStale
+ return StateRotten
}
t.Run("without serve stale", func(t *testing.T) {
@@ -142,7 +142,7 @@ func TestClientStaleHandling(t *testing.T) {
value, err = cli.Get(ctx, "key2")
require.NoError(t, err)
- assert.Equal(t, "fetch-4", value.Data, "should refetch when too stale")
+ assert.Equal(t, "fetch-4", value.Data, "should refetch when rotten")
assert.Equal(t, 4, fetchCount)
})
}
@@ -331,12 +331,12 @@ func TestStaleDataCleanupWhenUpstreamDeletes(t *testing.T) {
return nil, &ErrKeyNotFound{}
})
- // Stale check: fresh for 100ms, then TooStale (force refetch)
+ // Stale check: fresh for 100ms, then Rotten (force refetch)
checkStale := func(v *timestampedValue) State {
if clock.Now().Before(v.ExpiresAt) {
return StateFresh
}
- return StateTooStale
+ return StateRotten
}
notFoundCache := newRistrettoCache[time.Time](t)
@@ -361,7 +361,7 @@ func TestStaleDataCleanupWhenUpstreamDeletes(t *testing.T) {
clock.Advance(150 * time.Millisecond)
// Verify cached data is now stale
- assert.Equal(t, StateTooStale, checkStale(cachedValue), "cached data should be stale")
+ assert.Equal(t, StateRotten, checkStale(cachedValue), "cached data should be stale")
// Step 3: Meanwhile, data was deleted from upstream
realDataExists = false
@@ -537,7 +537,7 @@ func TestDoFetchDoesNotTouchUpstream(t *testing.T) {
if clock.Now().Before(v.ExpiresAt) {
return StateFresh
}
- return StateTooStale
+ return StateRotten
}
client := NewClient(backend, trackedUpstream,
@@ -641,15 +641,15 @@ func TestNotFoundCacheStale(t *testing.T) {
assert.Equal(t, 2, fetchCount, "async refresh should have happened")
})
- t.Run("too stale triggers immediate fetch", func(t *testing.T) {
+ t.Run("rotten triggers immediate fetch", func(t *testing.T) {
clock.Advance(600 * time.Millisecond) // Beyond stale TTL
_, err := cli.Get(ctx, "not-exist")
var e *ErrKeyNotFound
assert.True(t, errors.As(err, &e))
// After refetch, error comes from upstream (not cached)
- assert.False(t, e.Cached, "too stale refetch returns fresh upstream error")
- assert.Equal(t, 3, fetchCount, "should refetch immediately when too stale")
+ assert.False(t, e.Cached, "rotten refetch returns fresh upstream error")
+ assert.Equal(t, 3, fetchCount, "should refetch immediately when rotten")
})
}
diff --git a/entry.go b/entry.go
index d9540dc..77a3e3a 100644
--- a/entry.go
+++ b/entry.go
@@ -21,6 +21,6 @@ func EntryWithTTL[T any](freshTTL, staleTTL time.Duration) ClientOption[*Entry[T
if age < freshTTL+staleTTL {
return StateStale
}
- return StateTooStale
+ return StateRotten
})
}
diff --git a/entry_test.go b/entry_test.go
index fa75770..465e5b0 100644
--- a/entry_test.go
+++ b/entry_test.go
@@ -108,14 +108,14 @@ func TestEntryWithTTL(t *testing.T) {
assert.Equal(t, StateStale, state)
})
- t.Run("too stale entry", func(t *testing.T) {
+ t.Run("rotten entry", func(t *testing.T) {
now := time.Now()
oldNowFunc := NowFunc
NowFunc = func() time.Time { return now }
defer func() { NowFunc = oldNowFunc }()
entry := &Entry[string]{
- Data: "too stale data",
+ Data: "rotten data",
CachedAt: now.Add(-20 * time.Second), // 20 seconds ago
}
@@ -124,7 +124,7 @@ func TestEntryWithTTL(t *testing.T) {
option(client)
state := client.checkDataStale(entry)
- assert.Equal(t, StateTooStale, state)
+ assert.Equal(t, StateRotten, state)
})
t.Run("exact boundary - fresh to stale", func(t *testing.T) {
@@ -146,7 +146,7 @@ func TestEntryWithTTL(t *testing.T) {
assert.Equal(t, StateStale, state)
})
- t.Run("exact boundary - stale to too stale", func(t *testing.T) {
+ t.Run("exact boundary - stale to rotten", func(t *testing.T) {
now := time.Now()
oldNowFunc := NowFunc
NowFunc = func() time.Time { return now }
@@ -162,7 +162,7 @@ func TestEntryWithTTL(t *testing.T) {
option(client)
state := client.checkDataStale(entry)
- assert.Equal(t, StateTooStale, state)
+ assert.Equal(t, StateRotten, state)
})
}
diff --git a/error.go b/error.go
index 702d786..8b91e74 100644
--- a/error.go
+++ b/error.go
@@ -22,8 +22,8 @@ func (e *ErrKeyNotFound) Error() string {
return "key not found (cached, fresh)"
case StateStale:
return "key not found (cached, stale)"
- case StateTooStale:
- return "key not found (cached, too stale)"
+ case StateRotten:
+ return "key not found (cached, rotten)"
default:
return fmt.Sprintf("key not found (cached, state=%d)", e.CacheState)
}
diff --git a/types.go b/types.go
index 9ac22c5..11d4efc 100644
--- a/types.go
+++ b/types.go
@@ -8,9 +8,9 @@ import (
type State int8
const (
- StateFresh State = iota // Data is fresh and valid
- StateStale // Data is stale but usable
- StateTooStale // Data is too stale and must be refreshed
+ StateFresh State = iota // Data is fresh and valid
+ StateStale // Data is stale but usable
+ StateRotten // Data is rotten and must be refreshed
)
// Upstream defines the interface for a data source that can retrieve values
From 69e36d97a8fb963dafdb959e1ad8569acf4139dd Mon Sep 17 00:00:00 2001
From: molon <3739161+molon@users.noreply.github.com>
Date: Thu, 27 Nov 2025 10:20:15 +0800
Subject: [PATCH 2/2] fix(tests): update assertion message for stale data check
to reflect new naming convention
---
client_test.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/client_test.go b/client_test.go
index 72ab10e..91acbaf 100644
--- a/client_test.go
+++ b/client_test.go
@@ -361,7 +361,7 @@ func TestStaleDataCleanupWhenUpstreamDeletes(t *testing.T) {
clock.Advance(150 * time.Millisecond)
// Verify cached data is now stale
- assert.Equal(t, StateRotten, checkStale(cachedValue), "cached data should be stale")
+ assert.Equal(t, StateRotten, checkStale(cachedValue), "cached data should be rotten")
// Step 3: Meanwhile, data was deleted from upstream
realDataExists = false