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