11package cache
22
33import (
4+ "context"
45 "runtime"
56 "sync"
67 "time"
@@ -13,15 +14,6 @@ import (
1314 "github.com/Code-Hex/go-generics-cache/policy/simple"
1415)
1516
16- // janitor for collecting expired items and cleaning them
17- // this object is inspired from
18- // https://github.com/patrickmn/go-cache/blob/46f407853014144407b6c2ec7ccc76bf67958d93/cache.go
19- // many thanks to go-cache project
20- type janitor struct {
21- Interval time.Duration
22- stop chan bool
23- }
24-
2517// Interface is a common-cache interface.
2618type Interface [K comparable , V any ] interface {
2719 Get (key K ) (value V , ok bool )
4537type Item [K comparable , V any ] struct {
4638 Key K
4739 Value V
48- Expiration int64
40+ Expiration time.Time
41+ }
42+
43+ // Expired returns true if the item has expired.
44+ func (item * Item [K , V ]) Expired () bool {
45+ if item .Expiration .IsZero () {
46+ return false
47+ }
48+ return nowFunc ().After (item .Expiration )
4949}
5050
5151var nowFunc = time .Now
@@ -54,23 +54,14 @@ var nowFunc = time.Now
5454type ItemOption func (* itemOptions )
5555
5656type itemOptions struct {
57- expiration int64 // default none
58- }
59-
60- // Expired returns true if the item has expired.
61- func (item itemOptions ) Expired () bool {
62- if item .expiration == 0 {
63- return false
64- }
65-
66- return nowFunc ().UnixNano () > item .expiration
57+ expiration time.Time // default none
6758}
6859
6960// WithExpiration is an option to set expiration time for any items.
7061// If the expiration is zero or negative value, it treats as w/o expiration.
7162func WithExpiration (exp time.Duration ) ItemOption {
7263 return func (o * itemOptions ) {
73- o .expiration = nowFunc ().Add (exp ). UnixNano ()
64+ o .expiration = nowFunc ().Add (exp )
7465 }
7566}
7667
@@ -100,12 +91,14 @@ type Cache[K comparable, V any] struct {
10091type Option [K comparable , V any ] func (* options [K , V ])
10192
10293type options [K comparable , V any ] struct {
103- cache Interface [K , * Item [K , V ]]
94+ cache Interface [K , * Item [K , V ]]
95+ janitorInterval time.Duration
10496}
10597
10698func newOptions [K comparable , V any ]() * options [K , V ] {
10799 return & options [K , V ]{
108- cache : simple .NewCache [K , * Item [K , V ]](),
100+ cache : simple .NewCache [K , * Item [K , V ]](),
101+ janitorInterval : time .Minute ,
109102 }
110103}
111104
@@ -144,56 +137,48 @@ func AsClock[K comparable, V any](opts ...clock.Option) Option[K, V] {
144137 }
145138}
146139
140+ // WithJanitorInterval is an option to specify how often cache should delete expired items.
141+ //
142+ // Default is 1 minute.
143+ func WithJanitorInterval [K comparable , V any ](d time.Duration ) Option [K , V ] {
144+ return func (o * options [K , V ]) {
145+ o .janitorInterval = d
146+ }
147+ }
148+
147149// New creates a new thread safe Cache.
150+ // This function will be stopped an internal janitor when the cache is
151+ // no longer referenced anywhere.
148152//
149153// There are several Cache replacement policies available with you specified any options.
150154func New [K comparable , V any ](opts ... Option [K , V ]) * Cache [K , V ] {
151155 o := newOptions [K , V ]()
152156 for _ , optFunc := range opts {
153157 optFunc (o )
154158 }
155-
159+ ctx , cancel := context . WithCancel ( context . Background ())
156160 cache := & Cache [K , V ]{
157- cache : o .cache ,
161+ cache : o .cache ,
162+ janitor : newJanitor (ctx , o .janitorInterval ),
158163 }
159-
160- // @TODO change the ticker timer default value
161- cache .runJanitor (cache , time .Minute )
162- runtime .SetFinalizer (cache , cache .stopJanitor )
163-
164+ runtime .SetFinalizer (cache , func (self * Cache [K , V ]) {
165+ cancel ()
166+ })
164167 return cache
165168}
166169
167- func (_ * Cache [K , V ]) stopJanitor (c * Cache [K , V ]) {
168- if c .janitor != nil {
169- c .janitor .stop <- true
170+ // NewContext creates a new thread safe Cache with context.
171+ //
172+ // There are several Cache replacement policies available with you specified any options.
173+ func NewContext [K comparable , V any ](ctx context.Context , opts ... Option [K , V ]) * Cache [K , V ] {
174+ o := newOptions [K , V ]()
175+ for _ , optFunc := range opts {
176+ optFunc (o )
170177 }
171-
172- c .janitor = nil
173- }
174-
175- func (_ * Cache [K , V ]) runJanitor (c * Cache [K , V ], ci time.Duration ) {
176- c .stopJanitor (c )
177-
178- j := & janitor {
179- Interval : ci ,
180- stop : make (chan bool ),
178+ return & Cache [K , V ]{
179+ cache : o .cache ,
180+ janitor : newJanitor (ctx , o .janitorInterval ),
181181 }
182-
183- c .janitor = j
184-
185- go func () {
186- ticker := time .NewTicker (j .Interval )
187- for {
188- select {
189- case <- ticker .C :
190- c .DeleteExpired ()
191- case <- j .stop :
192- ticker .Stop ()
193- return
194- }
195- }
196- }()
197182}
198183
199184// Get looks up a key's value from the cache.
@@ -206,9 +191,9 @@ func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
206191 return
207192 }
208193
209- // if is expired, delete is and return nil instead
210- if item . Expiration > 0 && nowFunc (). UnixNano () > item . Expiration {
211- c . cache . Delete ( key )
194+ // Returns nil if the item has been expired.
195+ // Do not delete here and leave it to an external process such as Janitor.
196+ if item . Expired () {
212197 return value , false
213198 }
214199
@@ -217,9 +202,12 @@ func (c *Cache[K, V]) Get(key K) (value V, ok bool) {
217202
218203// DeleteExpired all expired items from the cache.
219204func (c * Cache [K , V ]) DeleteExpired () {
220- for _ , keys := range c .cache .Keys () {
221- // delete all expired items by using get method
222- _ , _ = c .Get (keys )
205+ for _ , key := range c .cache .Keys () {
206+ // if is expired, delete it and return nil instead
207+ item , ok := c .cache .Get (key )
208+ if ok && item .Expired () {
209+ c .cache .Delete (key )
210+ }
223211 }
224212}
225213
@@ -228,11 +216,6 @@ func (c *Cache[K, V]) Set(key K, val V, opts ...ItemOption) {
228216 c .mu .Lock ()
229217 defer c .mu .Unlock ()
230218 item := newItem (key , val , opts ... )
231- if item .Expiration <= 0 {
232- c .cache .Set (key , item )
233- return
234- }
235-
236219 c .cache .Set (key , item )
237220}
238221
0 commit comments