@@ -122,6 +122,10 @@ func (c *Cache[K, _]) Keys() []K {
122
122
// NumberCache is a in-memory cache which is able to store only Number constraint.
123
123
type NumberCache [K comparable , V Number ] struct {
124
124
* Cache [K , V ]
125
+ // nmu is used to do lock in Increment/Decrement process.
126
+ // Note that this must be here as a separate mutex because mu in Cache struct is Locked in GetItem,
127
+ // and if we call mu.Lock in Increment/Decrement, it will cause deadlock.
128
+ nmu sync.Mutex
125
129
}
126
130
127
131
// NewNumber creates a new cache for Number constraint.
@@ -135,6 +139,9 @@ func NewNumber[K comparable, V Number]() *NumberCache[K, V] {
135
139
// Returns an error if the item was not found or expired. If there is no error, the
136
140
// incremented value is returned.
137
141
func (nc * NumberCache [K , V ]) Increment (k K , n V ) (val V , err error ) {
142
+ // In order to avoid lost update, we must lock whole Increment/Decrement process.
143
+ nc .nmu .Lock ()
144
+ defer nc .nmu .Unlock ()
138
145
got , err := nc .Cache .GetItem (k )
139
146
if err != nil {
140
147
return val , err
@@ -151,6 +158,8 @@ func (nc *NumberCache[K, V]) Increment(k K, n V) (val V, err error) {
151
158
// Returns an error if the item was not found or expired. If there is no error, the
152
159
// decremented value is returned.
153
160
func (nc * NumberCache [K , V ]) Decrement (k K , n V ) (val V , err error ) {
161
+ nc .nmu .Lock ()
162
+ defer nc .nmu .Unlock ()
154
163
got , err := nc .Cache .GetItem (k )
155
164
if err != nil {
156
165
return val , err
0 commit comments