Skip to content

Commit

Permalink
Add more tests cases
Browse files Browse the repository at this point in the history
Signed-off-by: Soule BA <[email protected]>
  • Loading branch information
souleb committed May 3, 2024
1 parent 1e86776 commit f5a5a36
Show file tree
Hide file tree
Showing 7 changed files with 391 additions and 101 deletions.
99 changes: 50 additions & 49 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,19 @@ type Cache[T any] struct {
keyFunc KeyFunc[T]
}

// Item is an item stored in the cache.
type Item[T any] struct {
// Object is the item's object.
Object T
// Expiration is the item's expiration time.
Expiration int64
// item is an item stored in the cache.
type item[T any] struct {
// object is the item's object.
object T
// expiration is the item's expiration time.
expiration int64
}

type cache[T any] struct {
// Items holds the elements in the cache.
Items map[string]Item[T]
// MaxItems is the maximum number of items the cache can hold.
MaxItems int
// items holds the elements in the cache.
items map[string]item[T]
// capacity is the maximum number of items the cache can hold.
capacity int
metrics *cacheMetrics
labelsFunc GetLvsFunc[T]
janitor *janitor[T]
Expand All @@ -54,8 +54,8 @@ type cache[T any] struct {
var _ Expirable[any] = &Cache[any]{}

// New creates a new cache with the given configuration.
func New[T any](maxItems int, keyFunc KeyFunc[T], opts ...Options[T]) (*Cache[T], error) {
opt := cacheOptions[T]{}
func New[T any](capacity int, keyFunc KeyFunc[T], opts ...Options[T]) (*Cache[T], error) {
opt := storeOptions[T]{}
for _, o := range opts {
err := o(&opt)
if err != nil {
Expand All @@ -66,9 +66,10 @@ func New[T any](maxItems int, keyFunc KeyFunc[T], opts ...Options[T]) (*Cache[T]
return nil, ErrNoRegisterer
}
c := &cache[T]{
Items: make(map[string]Item[T]),
MaxItems: maxItems,
metrics: newCacheMetrics(opt.registerer, opt.extraLabels...),
items: make(map[string]item[T]),
capacity: capacity,
metrics: newCacheMetrics(opt.registerer, opt.extraLabels...),
labelsFunc: opt.labelsFunc,
janitor: &janitor[T]{
interval: opt.interval,
stop: make(chan bool),
Expand Down Expand Up @@ -111,14 +112,14 @@ func (c *Cache[T]) Add(object T) error {
c.metrics.incCacheRequests("failed")
return KeyError{object, ErrClosed}
}
_, found := c.Items[key]
_, found := c.items[key]
if found {
c.mu.Unlock()
c.metrics.incCacheRequests("failed")
return KeyError{object, ErrAlreadyExists}
}

if c.MaxItems > 0 && len(c.Items) < c.MaxItems {
if c.capacity > 0 && len(c.items) < c.capacity {
c.set(key, object)
c.mu.Unlock()
c.metrics.incCacheRequests("success")
Expand All @@ -143,15 +144,15 @@ func (c *Cache[T]) Update(object T) error {
c.metrics.incCacheRequests("failed")
return KeyError{object, ErrClosed}
}
_, found := c.Items[key]
_, found := c.items[key]
if found {
c.set(key, object)
c.mu.Unlock()
c.metrics.incCacheRequests("success")
return nil
}

if c.MaxItems > 0 && len(c.Items) < c.MaxItems {
if c.capacity > 0 && len(c.items) < c.capacity {
c.set(key, object)
c.mu.Unlock()
c.metrics.incCacheRequests("success")
Expand All @@ -164,9 +165,9 @@ func (c *Cache[T]) Update(object T) error {

func (c *cache[T]) set(key string, object T) {
var e int64
c.Items[key] = Item[T]{
Object: object,
Expiration: e,
c.items[key] = item[T]{
object: object,
expiration: e,
}
}

Expand All @@ -176,7 +177,7 @@ func (c *Cache[T]) Get(object T) (item T, exists bool, err error) {
var res T
lvs := []string{}
if c.labelsFunc != nil {
lvs, err = c.labelsFunc(object, len(c.metrics.getLabels()))
lvs, err = c.labelsFunc(object, len(c.metrics.getExtraLabels()))
if err != nil {
return res, false, KeyError{object, err}
}
Expand Down Expand Up @@ -221,22 +222,22 @@ func (c *cache[T]) get(key string) (T, bool, error) {
c.metrics.incCacheRequests("failed")
return res, false, ErrClosed
}
item, found := c.Items[key]
item, found := c.items[key]
if !found {
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return res, false, nil
}
if item.Expiration > 0 {
if item.Expiration < time.Now().UnixNano() {
if item.expiration > 0 {
if item.expiration < time.Now().UnixNano() {
c.mu.RUnlock()
c.metrics.incCacheRequests("succes")
return res, false, nil
}
}
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return item.Object, true, nil
return item.object, true, nil
}

// Delete an item from the cache. Does nothing if the key is not in the cache.
Expand All @@ -251,7 +252,7 @@ func (c *Cache[T]) Delete(object T) error {
c.metrics.incCacheRequests("failed")
return KeyError{object, ErrClosed}
}
delete(c.Items, key)
delete(c.items, key)
c.mu.Unlock()
c.metrics.incCacheRequests("success")
c.metrics.decCacheItems()
Expand All @@ -268,7 +269,7 @@ func (c *cache[T]) Clear() {
c.mu.Unlock()
return
}
c.Items = make(map[string]Item[T])
c.items = make(map[string]item[T])
c.mu.Unlock()
}

Expand All @@ -281,8 +282,8 @@ func (c *cache[T]) ListKeys() []string {
c.metrics.incCacheRequests("failed")
return nil
}
keys := make([]string, 0, len(c.Items))
for k := range c.Items {
keys := make([]string, 0, len(c.items))
for k := range c.items {
keys = append(keys, k)
}
c.mu.RUnlock()
Expand All @@ -303,14 +304,14 @@ func (c *Cache[T]) HasExpired(object T) (bool, error) {
c.metrics.incCacheRequests("failed")
return false, KeyError{object, ErrClosed}
}
item, ok := c.Items[key]
item, ok := c.items[key]
if !ok {
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return true, nil
}
if item.Expiration > 0 {
if item.Expiration < time.Now().UnixNano() {
if item.expiration > 0 {
if item.expiration < time.Now().UnixNano() {
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return true, nil
Expand All @@ -335,10 +336,10 @@ func (c *Cache[T]) SetExpiration(object T, expiration time.Duration) error {
c.metrics.incCacheRequests("failed")
return KeyError{object, ErrClosed}
}
item, ok := c.Items[key]
item, ok := c.items[key]
if ok {
item.Expiration = time.Now().Add(expiration).UnixNano()
c.Items[key] = item
item.expiration = time.Now().Add(expiration).UnixNano()
c.items[key] = item
}
c.mu.Unlock()
c.metrics.incCacheRequests("success")
Expand All @@ -359,40 +360,40 @@ func (c *Cache[T]) GetExpiration(object T) (time.Duration, error) {
c.metrics.incCacheRequests("failed")
return 0, KeyError{object, ErrClosed}
}
item, ok := c.Items[key]
item, ok := c.items[key]
if !ok {
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return 0, KeyError{object, ErrNotFound}
}
if item.Expiration > 0 {
if item.Expiration < time.Now().UnixNano() {
if item.expiration > 0 {
if item.expiration < time.Now().UnixNano() {
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return 0, nil
}
}
c.mu.RUnlock()
c.metrics.incCacheRequests("success")
return time.Duration(item.Expiration - time.Now().UnixNano()), nil
return time.Duration(item.expiration - time.Now().UnixNano()), nil
}

// DeleteExpired deletes all expired items from the cache.
func (c *cache[T]) DeleteExpired() {
// deleteExpired deletes all expired items from the cache.
// It is called by the janitor.
func (c *cache[T]) deleteExpired() {
c.mu.Lock()
if c.closed {
c.mu.Unlock()
c.metrics.incCacheRequests("failed")
return
}
for k, v := range c.Items {
if v.Expiration > 0 && v.Expiration < time.Now().UnixNano() {
delete(c.Items, k)
for k, v := range c.items {
if v.expiration > 0 && v.expiration < time.Now().UnixNano() {
delete(c.items, k)
c.metrics.incCacheEvictions()
c.metrics.decCacheItems()
}
}
c.mu.Unlock()
c.metrics.incCacheRequests("success")
}

type janitor[T any] struct {
Expand All @@ -405,7 +406,7 @@ func (j *janitor[T]) run(c *cache[T]) {
for {
select {
case <-ticker.C:
c.DeleteExpired()
c.deleteExpired()
case <-j.stop:
ticker.Stop()
return
Expand Down
Loading

0 comments on commit f5a5a36

Please sign in to comment.