Skip to content

Commit a233eac

Browse files
committed
isFull is set when count hits the max, but isDirty should only be true and returns error when another new entry is added after the cache is full + cache error should be dirty not full
1 parent fc55b61 commit a233eac

File tree

7 files changed

+229
-84
lines changed

7 files changed

+229
-84
lines changed

x/staking/cache/cache.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ package cache
22

33
import (
44
"context"
5-
"time"
65

76
corestoretypes "cosmossdk.io/core/store"
87
"cosmossdk.io/log"
98
"github.com/cosmos/cosmos-sdk/codec"
10-
sdk "github.com/cosmos/cosmos-sdk/types"
119
"github.com/cosmos/cosmos-sdk/x/staking/types"
1210
)
1311

@@ -74,8 +72,8 @@ func (c *ValidatorsQueueCache) GetUnbondingValidatorsQueueAll(ctx context.Contex
7472
return c.unbondingValidatorsQueue.getAll(ctx, c.cdc, c.logger)
7573
}
7674

77-
func (c *ValidatorsQueueCache) GetUnbondingValidatorsQueue(ctx context.Context, endTime time.Time, endHeight int64) ([]string, error) {
78-
return c.unbondingValidatorsQueue.get(ctx, c.cdc, types.GetCacheValidatorQueueKey(endTime, endHeight), c.logger)
75+
func (c *ValidatorsQueueCache) GetUnbondingValidatorsQueue(ctx context.Context, key string) ([]string, error) {
76+
return c.unbondingValidatorsQueue.get(ctx, c.cdc, key, c.logger)
7977
}
8078

8179
func (c *ValidatorsQueueCache) SetUnbondingValidatorsQueue(ctx context.Context, key string, addrs []string) error {
@@ -92,8 +90,8 @@ func (c *ValidatorsQueueCache) GetUnbondingDelegationsQueueAll(ctx context.Conte
9290
return c.unbondingDelegationsQueue.getAll(ctx, c.cdc, c.logger)
9391
}
9492

95-
func (c *ValidatorsQueueCache) GetUnbondingDelegationsQueue(ctx context.Context, endTime time.Time) ([]types.DVPair, error) {
96-
return c.unbondingDelegationsQueue.get(ctx, c.cdc, sdk.FormatTimeString(endTime), c.logger)
93+
func (c *ValidatorsQueueCache) GetUnbondingDelegationsQueue(ctx context.Context, key string) ([]types.DVPair, error) {
94+
return c.unbondingDelegationsQueue.get(ctx, c.cdc, key, c.logger)
9795
}
9896

9997
func (c *ValidatorsQueueCache) SetUnbondingDelegationsQueue(ctx context.Context, key string, delegations []types.DVPair) error {
@@ -110,8 +108,8 @@ func (c *ValidatorsQueueCache) GetRedelegationsQueueAll(ctx context.Context) (ma
110108
return c.redelegationsQueue.getAll(ctx, c.cdc, c.logger)
111109
}
112110

113-
func (c *ValidatorsQueueCache) GetRedelegationsQueue(ctx context.Context, endTime time.Time) ([]types.DVVTriplet, error) {
114-
return c.redelegationsQueue.get(ctx, c.cdc, sdk.FormatTimeString(endTime), c.logger)
111+
func (c *ValidatorsQueueCache) GetRedelegationsQueue(ctx context.Context, key string) ([]types.DVVTriplet, error) {
112+
return c.redelegationsQueue.get(ctx, c.cdc, key, c.logger)
115113
}
116114

117115
func (c *ValidatorsQueueCache) SetRedelegationsQueue(ctx context.Context, key string, redelegations []types.DVVTriplet) error {

x/staking/cache/cache_test.go

Lines changed: 176 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -194,17 +194,17 @@ func TestValidatorsQueueCache_FullPreventsLoad(t *testing.T) {
194194
// Try to load unbonding validators - should fail due to exceeding max
195195
_, err := cache.GetUnbondingValidatorsQueueAll(ctx)
196196
require.Error(t, err)
197-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
197+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
198198

199199
// Try to load unbonding delegations - should fail due to exceeding max
200200
_, err = cache.GetUnbondingDelegationsQueueAll(ctx)
201201
require.Error(t, err)
202-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
202+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
203203

204204
// Try to load redelegations - should fail due to exceeding max
205205
_, err = cache.GetRedelegationsQueueAll(ctx)
206206
require.Error(t, err)
207-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
207+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
208208
}
209209

210210
func TestValidatorsQueueCache_GetEntry(t *testing.T) {
@@ -222,7 +222,7 @@ func TestValidatorsQueueCache_GetEntry(t *testing.T) {
222222
valKey := types.GetCacheValidatorQueueKey(endTime, endHeight)
223223

224224
cache.SetUnbondingValidatorsQueue(ctx, valKey, []string{"val1", "val2"})
225-
valEntry, err := cache.GetUnbondingValidatorsQueue(ctx, endTime, endHeight)
225+
valEntry, err := cache.GetUnbondingValidatorsQueue(ctx, valKey)
226226
require.NoError(t, err)
227227
require.Equal(t, []string{"val1", "val2"}, valEntry)
228228

@@ -234,7 +234,7 @@ func TestValidatorsQueueCache_GetEntry(t *testing.T) {
234234
}
235235

236236
cache.SetUnbondingDelegationsQueue(ctx, delKey, delPairs)
237-
delEntry, err := cache.GetUnbondingDelegationsQueue(ctx, endTime)
237+
delEntry, err := cache.GetUnbondingDelegationsQueue(ctx, delKey)
238238
require.NoError(t, err)
239239
require.Equal(t, delPairs, delEntry)
240240

@@ -246,7 +246,7 @@ func TestValidatorsQueueCache_GetEntry(t *testing.T) {
246246
}
247247

248248
cache.SetRedelegationsQueue(ctx, redKey, redTriplets)
249-
redEntry, err := cache.GetRedelegationsQueue(ctx, endTime)
249+
redEntry, err := cache.GetRedelegationsQueue(ctx, redKey)
250250
require.NoError(t, err)
251251
require.Equal(t, redTriplets, redEntry)
252252
}
@@ -320,7 +320,7 @@ func TestValidatorsQueueCache_SetAndDelete(t *testing.T) {
320320
func TestValidatorsQueueCache_FullMarkedDirty(t *testing.T) {
321321
ctx := createTestContext(t)
322322

323-
cache := noOpLoadNewTestingCache(3)
323+
cache := noOpLoadNewTestingCache(2)
324324

325325
// clear dirty flags first
326326
errs := clearDirtyFlags(ctx, cache)
@@ -335,7 +335,14 @@ func TestValidatorsQueueCache_FullMarkedDirty(t *testing.T) {
335335
// Try to add one more - should mark as full and dirty
336336
err = cache.SetUnbondingValidatorsQueue(ctx, "key3", []string{"val3"})
337337
require.Error(t, err)
338-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
338+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
339+
340+
_, err = cache.GetUnbondingValidatorsQueueAll(ctx)
341+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
342+
343+
_, err = cache.GetUnbondingValidatorsQueue(ctx, "key1")
344+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
345+
339346
// Test unbonding delegations queue
340347
err = cache.SetUnbondingDelegationsQueue(ctx, "time1", []types.DVPair{
341348
{DelegatorAddress: "del1", ValidatorAddress: "val1"},
@@ -351,7 +358,13 @@ func TestValidatorsQueueCache_FullMarkedDirty(t *testing.T) {
351358
{DelegatorAddress: "del3", ValidatorAddress: "val3"},
352359
})
353360
require.Error(t, err)
354-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
361+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
362+
363+
_, err = cache.GetUnbondingDelegationsQueueAll(ctx)
364+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
365+
366+
_, err = cache.GetUnbondingDelegationsQueue(ctx, "time1")
367+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
355368

356369
// Test redelegations queue
357370
err = cache.SetRedelegationsQueue(ctx, "time1", []types.DVVTriplet{
@@ -368,7 +381,83 @@ func TestValidatorsQueueCache_FullMarkedDirty(t *testing.T) {
368381
{DelegatorAddress: "del3", ValidatorSrcAddress: "val3", ValidatorDstAddress: "val4"},
369382
})
370383
require.Error(t, err)
371-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
384+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
385+
386+
_, err = cache.GetRedelegationsQueueAll(ctx)
387+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
388+
389+
_, err = cache.GetRedelegationsQueue(ctx, "time1")
390+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
391+
}
392+
393+
func TestValidatorsQueueCache_FullNoErrorIfReplaced(t *testing.T) {
394+
ctx := createTestContext(t)
395+
396+
cache := noOpLoadNewTestingCache(2)
397+
398+
// clear dirty flags first
399+
errs := clearDirtyFlags(ctx, cache)
400+
require.Len(t, errs, 0)
401+
402+
// Test unbonding validators queue
403+
err := cache.SetUnbondingValidatorsQueue(ctx, "key1", []string{"val1"})
404+
require.NoError(t, err)
405+
err = cache.SetUnbondingValidatorsQueue(ctx, "key2", []string{"val2"})
406+
require.NoError(t, err)
407+
408+
// Replace existing entry - should not mark as dirty, ie no error
409+
err = cache.SetUnbondingValidatorsQueue(ctx, "key2", []string{"newVal2"})
410+
require.NoError(t, err)
411+
412+
_, err = cache.GetUnbondingValidatorsQueueAll(ctx)
413+
require.NoError(t, err)
414+
data, err := cache.GetUnbondingValidatorsQueue(ctx, "key2")
415+
require.NoError(t, err)
416+
require.Equal(t, []string{"newVal2"}, data)
417+
418+
// Test unbonding delegations queue
419+
err = cache.SetUnbondingDelegationsQueue(ctx, "time1", []types.DVPair{
420+
{DelegatorAddress: "del1", ValidatorAddress: "val1"},
421+
})
422+
require.NoError(t, err)
423+
err = cache.SetUnbondingDelegationsQueue(ctx, "time2", []types.DVPair{
424+
{DelegatorAddress: "del2", ValidatorAddress: "val2"},
425+
})
426+
require.NoError(t, err)
427+
428+
// Replace existing entry - should not mark as dirty, ie no error
429+
err = cache.SetUnbondingDelegationsQueue(ctx, "time2", []types.DVPair{
430+
{DelegatorAddress: "del2", ValidatorAddress: "newVal2"},
431+
})
432+
require.NoError(t, err)
433+
434+
_, err = cache.GetUnbondingDelegationsQueueAll(ctx)
435+
require.NoError(t, err)
436+
data2, err := cache.GetUnbondingDelegationsQueue(ctx, "time2")
437+
require.NoError(t, err)
438+
require.Equal(t, []types.DVPair{{DelegatorAddress: "del2", ValidatorAddress: "newVal2"}}, data2)
439+
440+
// Test redelegations queue
441+
err = cache.SetRedelegationsQueue(ctx, "time1", []types.DVVTriplet{
442+
{DelegatorAddress: "del1", ValidatorSrcAddress: "val1", ValidatorDstAddress: "val2"},
443+
})
444+
require.NoError(t, err)
445+
err = cache.SetRedelegationsQueue(ctx, "time2", []types.DVVTriplet{
446+
{DelegatorAddress: "del2", ValidatorSrcAddress: "val2", ValidatorDstAddress: "val3"},
447+
})
448+
require.NoError(t, err)
449+
450+
// Replace existing entry - should not mark as dirty, ie no error
451+
err = cache.SetRedelegationsQueue(ctx, "time2", []types.DVVTriplet{
452+
{DelegatorAddress: "del2", ValidatorSrcAddress: "val2", ValidatorDstAddress: "newVal3"},
453+
})
454+
require.NoError(t, err)
455+
456+
_, err = cache.GetRedelegationsQueueAll(ctx)
457+
require.NoError(t, err)
458+
data3, err := cache.GetRedelegationsQueue(ctx, "time2")
459+
require.NoError(t, err)
460+
require.Equal(t, []types.DVVTriplet{{DelegatorAddress: "del2", ValidatorSrcAddress: "val2", ValidatorDstAddress: "newVal3"}}, data3)
372461
}
373462

374463
func TestValidatorsQueueCache_UnbondingValidators(t *testing.T) {
@@ -423,7 +512,7 @@ func TestValidatorsQueueCache_UnbondingValidatorsEntry(t *testing.T) {
423512
require.NoError(t, err)
424513

425514
// Get specific entry
426-
entry, err := cache.GetUnbondingValidatorsQueue(ctx, endTime, endHeight)
515+
entry, err := cache.GetUnbondingValidatorsQueue(ctx, keyStr)
427516
require.NoError(t, err)
428517
require.Equal(t, validators, entry)
429518
}
@@ -486,7 +575,7 @@ func TestValidatorsQueueCache_UnbondingDelegationsEntry(t *testing.T) {
486575
require.NoError(t, err)
487576

488577
// Get specific entry
489-
entry, err := cache.GetUnbondingDelegationsQueue(ctx, endTime)
578+
entry, err := cache.GetUnbondingDelegationsQueue(ctx, keyStr)
490579
require.NoError(t, err)
491580
require.Equal(t, pairs, entry)
492581
}
@@ -547,7 +636,7 @@ func TestValidatorsQueueCache_RedelegationsEntry(t *testing.T) {
547636
require.NoError(t, err)
548637

549638
// Get specific entry
550-
entry, err := cache.GetRedelegationsQueue(ctx, endTime)
639+
entry, err := cache.GetRedelegationsQueue(ctx, keyStr)
551640
require.NoError(t, err)
552641
require.Equal(t, triplets, entry)
553642
}
@@ -565,9 +654,16 @@ func TestValidatorsQueueCache_FullAndDirtyBehaviorWithSizeLimit_SizeOne(t *testi
565654
return map[string][]string{}, nil
566655
}
567656
reloadCount++
568-
// Return one entry from persistent store
657+
658+
if reloadCount == 1 {
659+
return map[string][]string{
660+
"key0": {"newVal0"},
661+
"key1": {"val1"},
662+
"key2": {"val2"},
663+
}, nil
664+
}
569665
return map[string][]string{
570-
"key0": {"val0"},
666+
"key1": {"val1"},
571667
}, nil
572668
}
573669

@@ -576,49 +672,98 @@ func TestValidatorsQueueCache_FullAndDirtyBehaviorWithSizeLimit_SizeOne(t *testi
576672
errs := clearDirtyFlags(ctx, cache)
577673
require.Len(t, errs, 0)
578674

579-
// Step 1: Add one entry - cache becomes full immediately
675+
// Step 1: Add one entry - cache becomes full immediately, but not marked dirty
580676
err := cache.SetUnbondingValidatorsQueue(ctx, "key0", []string{"val0"})
677+
require.NoError(t, err)
678+
679+
// Step 2: Get All entries - cache is full but not marked dirty, so should return the entry
680+
data, err := cache.GetUnbondingValidatorsQueueAll(ctx)
681+
require.NoError(t, err)
682+
require.Len(t, data, 1)
683+
require.Equal(t, "val0", data["key0"][0])
684+
685+
// Step 3: Replace one entry - cache is already full, but this is a replace so it's allowed
686+
err = cache.SetUnbondingValidatorsQueue(ctx, "key0", []string{"newVal0"})
687+
require.NoError(t, err)
688+
689+
// Step 4: Reads should succeed because cache is not dirty
690+
newData, err := cache.GetUnbondingValidatorsQueueAll(ctx)
691+
require.NoError(t, err)
692+
require.Len(t, newData, 1)
693+
require.Equal(t, "newVal0", newData["key0"][0])
694+
695+
// Step 5: Add one new entry - cache is already full, so will be marked dirty
696+
err = cache.SetUnbondingValidatorsQueue(ctx, "key1", []string{"val1"})
697+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
698+
699+
// Step 6: Reads should fail because cache is dirty
700+
_, err = cache.GetUnbondingValidatorsQueueAll(ctx)
701+
require.Error(t, err)
702+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
703+
704+
// Step 7: Add one new entry - cache is already full, so will be marked dirty
705+
err = cache.SetUnbondingValidatorsQueue(ctx, "key2", []string{"val2"})
706+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
707+
708+
// Step 8: Reads should fail because cache is still dirty
709+
_, err = cache.GetUnbondingValidatorsQueue(ctx, "key2")
581710
require.Error(t, err)
582-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
711+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
583712

584-
// Step 2: Delete the entry - cache is no longer full but is marked dirty
713+
// Step 9: Delete the entry - cache is no longer full but is still dirty
585714
err = cache.DeleteUnbondingValidatorsQueue(ctx, "key0")
586715
require.NoError(t, err)
587716

588-
// Step 3: Try to read - should trigger reload because cache was marked dirty. Reload should fail because cache is instantly full upon reload.
717+
// Step 10: Try to read - should trigger reload because cache was marked dirty. Reload should fail because cache is instantly full upon reload.
589718
_, err = cache.GetUnbondingValidatorsQueueAll(ctx)
590719
require.Error(t, err)
591-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
720+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
592721
require.Equal(t, 1, reloadCount, "should have reloaded after deletion")
593722

594-
// Step 4: Subsequent reads would always return ErrCacheMaxSizeReached
595-
_, err = cache.GetUnbondingValidatorsQueueAll(ctx)
596-
require.Error(t, err)
597-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
723+
// Step 11: Simulate some empty deletions progagated from persistent store - Should do nothing since cache does not contain these entries
724+
err = cache.DeleteUnbondingValidatorsQueue(ctx, "key1")
725+
require.NoError(t, err)
726+
err = cache.DeleteUnbondingValidatorsQueue(ctx, "key2")
727+
require.NoError(t, err)
728+
729+
// Step 12: Try to read - should trigger reload because cache was marked dirty. Reload should pass now since no of entries in store <= max
730+
data3, err := cache.GetUnbondingValidatorsQueueAll(ctx)
731+
require.NoError(t, err)
732+
require.Len(t, data3, 1)
733+
require.Equal(t, "val1", data3["key1"][0])
734+
require.Equal(t, 2, reloadCount, "should have reloaded after deletion")
598735
}
599736

600737
func TestValidatorsQueueCache_FullAndDirtyBehaviorWithSizeLimit(t *testing.T) {
601738
testCases := []struct {
602739
name string
603740
cacheSize uint
604741
numEntries int
605-
expectFull bool
742+
expectDirty bool
606743
expectReload bool
607744
description string
608745
}{
609746
{
610-
name: "cache size 5 - becomes full and reloads on delete",
747+
name: "cache size 5 - becomes full but not marked dirty and hence won't reload",
611748
cacheSize: 5,
612749
numEntries: 5,
613-
expectFull: true,
750+
expectDirty: false,
751+
expectReload: false,
752+
description: "Cache with max=5 should become full, but not marked dirty, and should not reload",
753+
},
754+
{
755+
name: "cache size 5 - becomes full and reloads on delete",
756+
cacheSize: 5,
757+
numEntries: 6,
758+
expectDirty: true,
614759
expectReload: true,
615-
description: "Cache with max=5 should become full, get marked dirty as soon as the fifth one is set, and reload upon deletion",
760+
description: "Cache with max=5 should become full, marked dirty on the 6th, and should reload",
616761
},
617762
{
618763
name: "cache size 0 - unlimited, never full, never reloads",
619764
cacheSize: 0,
620765
numEntries: 10,
621-
expectFull: false,
766+
expectDirty: false,
622767
expectReload: false,
623768
description: "Cache with max=0 (unlimited) should never become full, never get marked dirty, and never reload",
624769
},
@@ -660,9 +805,9 @@ func TestValidatorsQueueCache_FullAndDirtyBehaviorWithSizeLimit(t *testing.T) {
660805

661806
// Step 2: Try to add another entry - should fail if cache is full
662807
err := cache.SetUnbondingValidatorsQueue(ctx, "extra_key", []string{"extra_val"})
663-
if tc.expectFull {
808+
if tc.expectDirty {
664809
require.Error(t, err)
665-
require.Equal(t, types.ErrCacheMaxSizeReached, err)
810+
require.Equal(t, types.ErrCacheIsFullAndDirty, err)
666811
} else {
667812
require.NoError(t, err)
668813
}

0 commit comments

Comments
 (0)