aboutsummaryrefslogtreecommitdiffstats
path: root/store.go
diff options
context:
space:
mode:
authorMarc Pervaz Boocha <marcpervaz@qburst.com>2025-02-26 15:18:02 +0530
committerMarc Pervaz Boocha <marcpervaz@qburst.com>2025-02-27 13:38:36 +0530
commit7b6c99bbc293cbf268b7be7f684577dbf668d895 (patch)
tree2b5c2fee0094b6dc55276186721d2e39a9cc846d /store.go
parentUpdate store to use EvictList instead of Evict (diff)
downloadcache-7b6c99bbc293cbf268b7be7f684577dbf668d895.tar
cache-7b6c99bbc293cbf268b7be7f684577dbf668d895.tar.gz
cache-7b6c99bbc293cbf268b7be7f684577dbf668d895.tar.bz2
cache-7b6c99bbc293cbf268b7be7f684577dbf668d895.tar.lz
cache-7b6c99bbc293cbf268b7be7f684577dbf668d895.tar.xz
cache-7b6c99bbc293cbf268b7be7f684577dbf668d895.tar.zst
cache-7b6c99bbc293cbf268b7be7f684577dbf668d895.zip
Added Memorize and UpdateInPlace
Diffstat (limited to 'store.go')
-rw-r--r--store.go128
1 files changed, 100 insertions, 28 deletions
diff --git a/store.go b/store.go
index dc38c8b..4933b96 100644
--- a/store.go
+++ b/store.go
@@ -77,8 +77,8 @@ func (s *store) Clear() {
s.EvictList.EvictPrev = &s.EvictList
}
-// lookup calculates the hash and index for a given key.
-func lookup(s *store, key []byte) (uint64, uint64) {
+// lookupIdx calculates the hash and index for a given key.
+func lookupIdx(s *store, key []byte) (uint64, uint64) {
hash := hash(key)
return hash % uint64(len(s.Bucket)), hash
@@ -94,7 +94,7 @@ func lazyInitBucket(n *node) {
// lookup finds a node in the store by key.
func (s *store) lookup(key []byte) (*node, uint64, uint64) {
- idx, hash := lookup(s, key)
+ idx, hash := lookupIdx(s, key)
bucket := &s.Bucket[idx]
@@ -154,10 +154,12 @@ func (s *store) Cleanup() {
s.mu.Lock()
defer s.mu.Unlock()
- for v := s.EvictList.EvictNext; v != &s.EvictList; v = v.EvictNext {
+ for v := s.EvictList.EvictNext; v != &s.EvictList; {
+ n := v.EvictNext
if !v.IsValid() {
deleteNode(s, v)
}
+ v = n
}
}
@@ -166,62 +168,78 @@ func (s *store) Evict() bool {
s.mu.Lock()
defer s.mu.Unlock()
- for s.MaxCost != 0 && s.MaxCost < s.Cost {
+ if s.MaxCost == 0 {
+ return true
+ }
+
+ for s.MaxCost < s.Cost {
n := s.Policy.Evict()
if n == nil {
break
}
-
deleteNode(s, n)
}
return true
}
-// Set adds or updates a key-value pair in the store with locking.
-func (s *store) Set(key []byte, value []byte, ttl time.Duration) {
- s.mu.Lock()
- defer s.mu.Unlock()
-
- v, idx, hash := s.lookup(key)
- if v != nil {
- s.Cost = s.Cost + uint64(len(value)) - uint64(len(v.Value))
- v.Value = value
- v.Expiration = time.Now().Add(ttl)
- s.Policy.OnUpdate(v)
- }
-
+// insert adds a new key-value pair to the store.
+func (s *store) insert(key []byte, value []byte, ttl time.Duration) {
+ idx, hash := lookupIdx(s, key)
bucket := &s.Bucket[idx]
if float64(s.Length)/float64(len(s.Bucket)) > 0.75 {
s.Resize()
- // resize may invidate pointer to bucket
- _, idx, _ := s.lookup(key)
+ // resize may invalidate pointer to bucket
+ _, idx, _ = s.lookup(key)
bucket = &s.Bucket[idx]
lazyInitBucket(bucket)
}
- node := &node{
+ v := &node{
Hash: hash,
Key: key,
Value: value,
}
if ttl != 0 {
- node.Expiration = time.Now().Add(ttl)
+ v.Expiration = time.Now().Add(ttl)
+ } else {
+ v.Expiration = zero[time.Time]()
}
- node.HashPrev = bucket
- node.HashNext = node.HashPrev.HashNext
- node.HashNext.HashPrev = node
- node.HashPrev.HashNext = node
+ v.HashPrev = bucket
+ v.HashNext = v.HashPrev.HashNext
+ v.HashNext.HashPrev = v
+ v.HashPrev.HashNext = v
- s.Policy.OnInsert(node)
+ s.Policy.OnInsert(v)
s.Cost = s.Cost + uint64(len(key)) + uint64(len(value))
s.Length = s.Length + 1
}
+// Set adds or updates a key-value pair in the store with locking.
+func (s *store) Set(key []byte, value []byte, ttl time.Duration) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ v, _, _ := s.lookup(key)
+ if v != nil {
+ s.Cost = s.Cost + uint64(len(value)) - uint64(len(v.Value))
+ v.Value = value
+ if ttl != 0 {
+ v.Expiration = time.Now().Add(ttl)
+ } else {
+ v.Expiration = zero[time.Time]()
+ }
+ s.Policy.OnUpdate(v)
+ return
+ }
+
+ s.insert(key, value, ttl)
+}
+
// deleteNode removes a node from the store.
func deleteNode(s *store, v *node) {
v.HashNext.HashPrev = v.HashPrev
@@ -252,3 +270,57 @@ func (s *store) Delete(key []byte) bool {
return false
}
+
+// UpdateInPlace retrieves a value from the store, processes it using the provided function,
+// and then sets the result back into the store with the same key.
+func (s *store) UpdateInPlace(key []byte, processFunc func([]byte) ([]byte, error), ttl time.Duration) error {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ v, _, _ := s.lookup(key)
+ if v == nil {
+ return ErrKeyNotFound
+ }
+
+ if !v.IsValid() {
+ deleteNode(s, v)
+ return ErrKeyNotFound
+ }
+
+ processedValue, err := processFunc(v.Value)
+ if err != nil {
+ return err
+ }
+
+ s.Cost = s.Cost + uint64(len(processedValue)) - uint64(len(v.Value))
+ v.Value = processedValue
+ if ttl != 0 {
+ v.Expiration = time.Now().Add(ttl)
+ } else {
+ v.Expiration = zero[time.Time]()
+ }
+ s.Policy.OnUpdate(v)
+
+ return nil
+}
+
+// Memorize attempts to retrieve a value from the store. If the retrieval fails,
+// it sets the result of the factory function into the store and returns that result.
+func (s *store) Memorize(key []byte, factoryFunc func() ([]byte, error), ttl time.Duration) ([]byte, error) {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ v, _, _ := s.lookup(key)
+ if v != nil && v.IsValid() {
+ s.Policy.OnAccess(v)
+ return v.Value, nil
+ }
+
+ factoryValue, err := factoryFunc()
+ if err != nil {
+ return nil, err
+ }
+
+ s.insert(key, factoryValue, ttl)
+ return factoryValue, nil
+}