@@ -10,6 +10,7 @@ import (
1010 "github.com/stretchr/testify/assert"
1111 "github.com/stretchr/testify/require"
1212 "go.temporal.io/server/common/clock"
13+ "go.temporal.io/server/common/dynamicconfig"
1314 "go.temporal.io/server/common/metrics"
1415 "go.temporal.io/server/common/metrics/metricstest"
1516)
@@ -138,8 +139,7 @@ func TestLRUWithTTL(t *testing.T) {
138139 assert .Equal (t , 2 , len (snapshot [metrics .CacheUsage .Name ()]))
139140 assert .Equal (t , float64 (0 ), snapshot [metrics .CacheUsage .Name ()][1 ].Value )
140141 assert .Equal (t , 0 , cache .Size ())
141- assert .Equal (t , 2 , len (snapshot [metrics .CacheEntryAgeOnGet .Name ()]))
142- assert .Equal (t , time .Millisecond * 300 , snapshot [metrics .CacheEntryAgeOnGet .Name ()][1 ].Value )
142+ assert .Equal (t , 1 , len (snapshot [metrics .CacheEntryAgeOnGet .Name ()]))
143143 assert .Equal (t , time .Millisecond * 300 , snapshot [metrics .CacheEntryAgeOnEviction .Name ()][0 ].Value )
144144}
145145
@@ -735,3 +735,117 @@ func TestCache_InvokeLifecycleCallbacks(t *testing.T) {
735735 assert .Nil (t , cache .Get ("key" ))
736736 require .Equal (t , 2 , onEvict , "expected OnEvict callback to be invoked" )
737737}
738+
739+ func TestCache_UnusedExpiry (t * testing.T ) {
740+ t .Parallel ()
741+ r := require .New (t )
742+
743+ ttl := 10 * time .Minute
744+ loopInterval := 1 * time .Minute
745+ timeSource := clock .NewEventTimeSource ()
746+
747+ cache := New (5 ,
748+ & Options {
749+ TTL : ttl ,
750+ TimeSource : timeSource ,
751+ BackgroundEvict : func () dynamicconfig.CacheBackgroundEvictSettings {
752+ return dynamicconfig.CacheBackgroundEvictSettings {
753+ Enabled : true ,
754+ LoopInterval : loopInterval ,
755+ MaxEntryPerCall : 1 ,
756+ }
757+ },
758+ },
759+ )
760+
761+ cache .Put (1 , 1 )
762+ r .Equal (1 , cache .Size ())
763+
764+ r .Eventually (func () bool {
765+ timeSource .Advance (loopInterval )
766+ return cache .Size () == 0
767+ }, 2 * time .Second , 100 * time .Millisecond )
768+
769+ cache .Put (2 , 2 )
770+ timeSource .Advance (ttl / 2 )
771+ cache .Put (3 , 3 )
772+ r .Equal (2 , cache .Size ())
773+
774+ r .Eventually (func () bool {
775+ timeSource .Advance (loopInterval )
776+ return cache .Size () == 1 && cache .Get (2 ) == nil && cache .Get (3 ) == 3
777+ }, 2 * time .Second , 100 * time .Millisecond )
778+
779+ r .Eventually (func () bool {
780+ timeSource .Advance (loopInterval )
781+ return cache .Size () == 0 && cache .Get (2 ) == nil && cache .Get (3 ) == nil
782+ }, 2 * time .Second , 100 * time .Millisecond )
783+
784+ // Stop the background goroutine, confirm no active expiration.
785+ cache .Put (4 , 4 )
786+ cache .Stop ()
787+ l , ok := cache .(* lru )
788+ r .True (ok )
789+ c := make (chan struct {})
790+ go func () {
791+ l .loops .Wait ()
792+ close (c )
793+ }()
794+ r .Eventually (func () bool {
795+ select {
796+ case <- c :
797+ return true
798+ default :
799+ return false
800+ }
801+ }, 2 * time .Second , 100 * time .Millisecond )
802+ timeSource .Advance (ttl + 1 * time .Second )
803+ // The cache should still have entry 4,
804+ r .Equal (1 , cache .Size ())
805+ // but this Get call will check the (hard) ttl & expire it.
806+ r .Equal (nil , cache .Get (4 ))
807+ }
808+
809+ func TestCache_UnusedExpiryPin (t * testing.T ) {
810+ t .Parallel ()
811+ r := require .New (t )
812+
813+ ttl := 10 * time .Minute
814+ loopInterval := 1 * time .Minute
815+ timeSource := clock .NewEventTimeSource ()
816+
817+ cache := New (5 ,
818+ & Options {
819+ TTL : ttl ,
820+ Pin : true ,
821+ TimeSource : timeSource ,
822+ BackgroundEvict : func () dynamicconfig.CacheBackgroundEvictSettings {
823+ return dynamicconfig.CacheBackgroundEvictSettings {
824+ Enabled : true ,
825+ LoopInterval : loopInterval ,
826+ MaxEntryPerCall : 1 ,
827+ }
828+ },
829+ },
830+ )
831+
832+ _ , err := cache .PutIfNotExist (1 , 1 )
833+ r .NoError (err )
834+ timeSource .Advance (ttl / 2 )
835+ cache .Release (1 )
836+ _ , err = cache .PutIfNotExist (2 , 2 )
837+ r .NoError (err )
838+ r .Equal (2 , cache .Size ())
839+
840+ r .Eventually (func () bool {
841+ timeSource .Advance (loopInterval )
842+ return cache .Size () == 1 && cache .Get (1 ) == nil
843+ }, 1 * time .Second , 100 * time .Millisecond )
844+
845+ cache .Release (2 )
846+
847+ r .Eventually (func () bool {
848+ timeSource .Advance (loopInterval )
849+ return cache .Size () == 0
850+ }, 1 * time .Second , 100 * time .Millisecond )
851+ }
0 commit comments