@@ -7,10 +7,21 @@ import (
77 "time"
88)
99
10+ // Config allows to alter the configuration of a Cache.
11+ //
12+ // Context is by default an empty background context.
13+ // DefaultTTL is by default 15 minutes.
14+ // CleanupInterval is by default 5 minutes.
15+ type Config struct {
16+ Context context.Context
17+ DefaultTTL time.Duration
18+ CleanupInterval time.Duration
19+ }
20+
1021// Cache holds the data you want to cache in memory.
11- type Cache struct {
22+ type Cache [ K comparable , T any ] struct {
1223 mut sync.RWMutex
13- data map [string ]Item
24+ data map [K ]Item [ T ]
1425
1526 defaultTTL time.Duration
1627 cleanupInterval time.Duration
@@ -19,137 +30,118 @@ type Cache struct {
1930 cancel context.CancelFunc
2031}
2132
22- // Item is a unit of data which can be cached and has an expiration as Unix epoch.
23- type Item struct {
24- Data any
33+ // Item is a unit of typed data which can be cached and has an expiration as Unix epoch.
34+ type Item [ T any ] struct {
35+ Data T
2536 TTL int64
2637}
2738
2839// Expired returns true if the data of the Item has expired.
29- func (i * Item ) Expired () bool {
40+ func (i * Item [ T ] ) Expired () bool {
3041 return time .Now ().Unix () > i .TTL
3142}
3243
33- // Option can alter the behavior of a Cache.
34- type Option func (* Cache )
35-
36- // New create a new Cache instance.
37- func New (opts ... Option ) * Cache {
38- c := & Cache {
39- data : make (map [string ]Item ),
44+ // NewCache create a new Cache instance with K as key and T as data.
45+ func NewCache [K comparable , T any ](cfg Config ) * Cache [K , T ] {
46+ c := & Cache [K , T ]{
47+ data : make (map [K ]Item [T ]),
4048 defaultTTL : time .Minute * 15 ,
4149 cleanupInterval : time .Minute * 5 ,
4250 }
4351
4452 c .ctx , c .cancel = context .WithCancel (context .Background ())
4553
46- for _ , opt := range opts {
47- opt ( c )
54+ if cfg . Context != nil {
55+ c . ctx = cfg . Context
4856 }
4957
50- go c .cleanup ()
51-
52- return c
53- }
54-
55- // WithDefaultTTL changes the default time-to-live for an Item in the Cache.
56- // Default is 15m.
57- func WithDefaultTTL (ttl time.Duration ) Option {
58- return func (c * Cache ) {
59- c .defaultTTL = ttl
58+ if cfg .DefaultTTL > 0 {
59+ c .defaultTTL = cfg .DefaultTTL
6060 }
61- }
6261
63- // WithCleanupInterval changes the default interval at which expired Items are removed from the Cache.
64- // Default is 5m.
65- func WithCleanupInterval (interval time.Duration ) Option {
66- return func (c * Cache ) {
67- c .cleanupInterval = interval
62+ if cfg .CleanupInterval > 0 {
63+ c .cleanupInterval = cfg .CleanupInterval
6864 }
69- }
7065
71- // WithContext adds a custom context for the Cache.
72- // If the context is canceled, the cleanup ticker will stop.
73- func WithContext (ctx context.Context ) Option {
74- return func (c * Cache ) {
75- c .ctx = ctx
76- }
66+ go c .cleanup ()
67+
68+ return c
7769}
7870
7971// Set will add an Item to the Cache with the default time-to-live.
80- func (c * Cache ) Set (key string , value any ) {
72+ func (c * Cache [ K , T ] ) Set (key K , value T ) {
8173 c .SetWithTTL (key , value , c .defaultTTL )
8274}
8375
8476// SetWithTTL will add an Item to the Cache with the given time-to-live.
85- func (c * Cache ) SetWithTTL (key string , data any , ttl time.Duration ) {
77+ func (c * Cache [ K , T ] ) SetWithTTL (key K , data T , ttl time.Duration ) {
8678 c .mut .Lock ()
87- c .data [key ] = Item {Data : data , TTL : time .Now ().Add (ttl ).Unix ()}
79+ c .data [key ] = Item [ T ] {Data : data , TTL : time .Now ().Add (ttl ).Unix ()}
8880 c .mut .Unlock ()
8981}
9082
9183// Get returns an Item and true if the Item was found in the Cache and has not been expired.
9284// An empty Item and false is returned when the Item was not found or has been expired.
93- func (c * Cache ) Get (key string ) (Item , bool ) {
85+ func (c * Cache [ K , T ] ) Get (key K ) (Item [ T ] , bool ) {
9486 c .mut .RLock ()
9587 item , ok := c .data [key ]
9688 c .mut .RUnlock ()
9789
9890 if item .Expired () {
99- return Item {}, false
91+ return Item [ T ] {}, false
10092 }
10193
10294 return item , ok
10395}
10496
10597// QueryFunc is a function to retrieve data which will be put into the Cache.
106- type QueryFunc func (key string ) (any , error )
98+ type QueryFunc [ K comparable , T any ] func (key K ) (T , error )
10799
108100// Remember tries to get the Item from the Cache, if the Item is not found or expired QueryFunc is called
109101// to retrieve the data from source and put it into the Cache.
110- func (c * Cache ) Remember (key string , query QueryFunc ) (Item , error ) {
102+ func (c * Cache [ K , T ] ) Remember (key K , query QueryFunc [ K , T ] ) (Item [ T ] , error ) {
111103 return c .RememberWithTTL (key , query , c .defaultTTL )
112104}
113105
114106// RememberWithTTL tries to get the Item from the Cache, if the Item is not found or expired QueryFunc is called
115107// to retrieve the data from source and put it into the Cache with the given time-to-live.
116- func (c * Cache ) RememberWithTTL (key string , query QueryFunc , ttl time.Duration ) (Item , error ) {
108+ func (c * Cache [ K , T ] ) RememberWithTTL (key K , query QueryFunc [ K , T ], ttl time.Duration ) (Item [ T ] , error ) {
117109 item , ok := c .Get (key )
118110 if ok {
119111 return item , nil
120112 }
121113
122114 data , err := query (key )
123115 if err != nil {
124- return Item {}, fmt .Errorf ("failed to query data: %w" , err )
116+ return Item [ T ] {}, fmt .Errorf ("failed to query data: %w" , err )
125117 }
126118
127119 c .SetWithTTL (key , data , ttl )
128120
129- return Item {Data : data , TTL : time .Now ().Add (c .defaultTTL ).Unix ()}, nil
121+ return Item [ T ] {Data : data , TTL : time .Now ().Add (c .defaultTTL ).Unix ()}, nil
130122}
131123
132124// Delete removes an Item from the Cache.
133- func (c * Cache ) Delete (key string ) {
125+ func (c * Cache [ K , T ] ) Delete (key K ) {
134126 c .mut .Lock ()
135127 delete (c .data , key )
136128 c .mut .Unlock ()
137129}
138130
139131// Reset removes all Items from the Cache.
140- func (c * Cache ) Reset () {
132+ func (c * Cache [ K , T ] ) Reset () {
141133 c .mut .Lock ()
142- c .data = make (map [string ]Item )
134+ c .data = make (map [K ]Item [ T ] )
143135 c .mut .Unlock ()
144136}
145137
146138// Cancel will cancel the default Context of the Cache which stops the cleanup ticker.
147139// This only has an effect when the Cache has not been created with a custom context.
148- func (c * Cache ) Cancel () {
140+ func (c * Cache [ K , T ] ) Cancel () {
149141 c .cancel ()
150142}
151143
152- func (c * Cache ) cleanup () {
144+ func (c * Cache [ K , T ] ) cleanup () {
153145 ticker := time .NewTicker (c .cleanupInterval )
154146
155147 for {
@@ -158,7 +150,7 @@ func (c *Cache) cleanup() {
158150 ticker .Stop ()
159151 return
160152 case <- ticker .C :
161- toBeDeleted := make ([]string , 0 )
153+ toBeDeleted := make ([]K , 0 )
162154
163155 c .mut .RLock ()
164156 for key , item := range c .data {
0 commit comments