@@ -2,19 +2,20 @@ package cache
22
33import (
44 "context"
5- "sync "
5+ "errors "
66 "time"
77
88 "github.com/patrickmn/go-cache"
99 "github.com/studiolambda/cosmos/contract"
1010)
1111
12- // Memory implements contract.Cache using an in-memory store backed
13- // by patrickmn/go-cache. It is suitable for single-process
14- // applications and testing scenarios where persistence across
15- // restarts is not required.
12+ // Memory implements [contract.Cache] using an in-memory store backed by
13+ // patrickmn/go-cache. Note that this dependency is unmaintained since 2017;
14+ // consider migrating to a maintained alternative for production use.
15+ //
16+ // Memory is suitable for single-process applications and testing scenarios
17+ // where persistence across restarts is not required.
1618type Memory struct {
17- mux sync.Mutex
1819 store * cache.Cache
1920}
2021
@@ -43,6 +44,10 @@ func (memory *Memory) Get(_ context.Context, key string) (any, error) {
4344
4445// Put stores a value in the in-memory cache with the given TTL.
4546// A zero TTL uses the default expiration configured at creation.
47+ //
48+ // WARNING: Values are stored by reference. Callers must not mutate
49+ // values after storing or after retrieval. For safety with mutable
50+ // types, store copies or use value types.
4651func (memory * Memory ) Put (_ context.Context , key string , value any , ttl time.Duration ) error {
4752 memory .store .Set (key , value , ttl )
4853
@@ -64,12 +69,8 @@ func (memory *Memory) Has(_ context.Context, key string) (bool, error) {
6469 return found , nil
6570}
6671
67- // Pull atomically retrieves and removes the value for the given key.
68- // It holds a mutex to prevent races between the get and delete steps.
72+ // Pull retrieves and removes the value for the given key.
6973func (memory * Memory ) Pull (ctx context.Context , key string ) (any , error ) {
70- memory .mux .Lock ()
71- defer memory .mux .Unlock ()
72-
7374 val , err := memory .Get (ctx , key )
7475
7576 if err != nil {
@@ -84,38 +85,40 @@ func (memory *Memory) Pull(ctx context.Context, key string) (any, error) {
8485}
8586
8687// Forever stores a value permanently with no expiration.
88+ //
89+ // WARNING: Values are stored by reference. Callers must not mutate
90+ // values after storing or after retrieval. For safety with mutable
91+ // types, store copies or use value types.
8792func (memory * Memory ) Forever (_ context.Context , key string , value any ) error {
8893 memory .store .Set (key , value , cache .NoExpiration )
8994
9095 return nil
9196}
9297
9398// Increment atomically increases the integer value stored at key by
94- // the given amount. Returns contract.ErrCacheKeyNotFound if the key
99+ // the given amount. Returns [ contract.ErrCacheKeyNotFound] if the key
95100// does not exist.
96- func (memory * Memory ) Increment (ctx context.Context , key string , by int64 ) (int64 , error ) {
97- memory .mux .Lock ()
98- defer memory .mux .Unlock ()
101+ func (memory * Memory ) Increment (_ context.Context , key string , by int64 ) (int64 , error ) {
102+ result , err := memory .store .IncrementInt64 (key , by )
99103
100- if found , _ := memory . Has ( ctx , key ); ! found {
104+ if err != nil {
101105 return 0 , contract .ErrCacheKeyNotFound
102106 }
103107
104- return memory . store . IncrementInt64 ( key , by )
108+ return result , nil
105109}
106110
107111// Decrement atomically decreases the integer value stored at key by
108- // the given amount. Returns contract.ErrCacheKeyNotFound if the key
112+ // the given amount. Returns [ contract.ErrCacheKeyNotFound] if the key
109113// does not exist.
110- func (memory * Memory ) Decrement (ctx context.Context , key string , by int64 ) (int64 , error ) {
111- memory .mux .Lock ()
112- defer memory .mux .Unlock ()
114+ func (memory * Memory ) Decrement (_ context.Context , key string , by int64 ) (int64 , error ) {
115+ result , err := memory .store .DecrementInt64 (key , by )
113116
114- if found , _ := memory . Has ( ctx , key ); ! found {
117+ if err != nil {
115118 return 0 , contract .ErrCacheKeyNotFound
116119 }
117120
118- return memory . store . DecrementInt64 ( key , by )
121+ return result , nil
119122}
120123
121124// Remember retrieves the cached value for the given key, or
@@ -129,39 +132,43 @@ func (memory *Memory) Decrement(ctx context.Context, key string, by int64) (int6
129132// use golang.org/x/sync/singleflight to deduplicate concurrent
130133// calls for the same key.
131134func (memory * Memory ) Remember (ctx context.Context , key string , ttl time.Duration , compute func () (any , error )) (any , error ) {
132- val , err := memory .Get (ctx , key )
135+ value , err := memory .Get (ctx , key )
133136
134137 if err == nil {
135- return val , nil
138+ return value , nil
139+ }
140+
141+ if ! errors .Is (err , contract .ErrCacheKeyNotFound ) {
142+ return nil , err
136143 }
137144
138- val , err = compute ()
145+ value , err = compute ()
139146
140147 if err != nil {
141148 return nil , err
142149 }
143150
144- _ = memory .Put (ctx , key , val , ttl )
145-
146- return val , nil
151+ return value , memory .Put (ctx , key , value , ttl )
147152}
148153
149154// RememberForever retrieves the cached value for the given key, or
150155// computes and stores it permanently if the key is not found.
151156func (memory * Memory ) RememberForever (ctx context.Context , key string , compute func () (any , error )) (any , error ) {
152- val , err := memory .Get (ctx , key )
157+ value , err := memory .Get (ctx , key )
153158
154159 if err == nil {
155- return val , nil
160+ return value , nil
156161 }
157162
158- val , err = compute ()
163+ if ! errors .Is (err , contract .ErrCacheKeyNotFound ) {
164+ return nil , err
165+ }
166+
167+ value , err = compute ()
159168
160169 if err != nil {
161170 return nil , err
162171 }
163172
164- _ = memory .Forever (ctx , key , val )
165-
166- return val , nil
173+ return value , memory .Forever (ctx , key , value )
167174}
0 commit comments