Skip to content
This repository has been archived by the owner on Mar 17, 2023. It is now read-only.

Applying changes list #2

Merged
merged 1 commit into from
May 31, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ cache can be saved to and loaded from a file (using `c.Items()` to retrieve the
items map to serialize, and `NewFrom()` to create a cache from a deserialized
one) to recover from downtime quickly. (See the docs for `NewFrom()` for caveats.)

### Updates

add the limitation of max capacity on the total memory consumption.

- first version: If the cache is full, stop to set the new key.

- second version: If the cache is full, run some replacement algorithm.

### Installation

`go get github.com/pmylund/go-cache`
Expand Down
98 changes: 59 additions & 39 deletions cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (
"time"
)

var (
ErrKeyExists = fmt.Errorf("item already exists")
ErrCacheMiss = fmt.Errorf("item not found")
)

type Item struct {
Object interface{}
Expiration *time.Time
Expand Down Expand Up @@ -42,6 +47,9 @@ type cache struct {
defaultExpiration time.Duration
items map[string]*Item
janitor *janitor

// capacity - max number of cached items
capacity int
}

// Add an item to the cache, replacing any existing item. If the duration is 0
Expand All @@ -64,6 +72,14 @@ func (c *cache) set(k string, x interface{}, d time.Duration) {
t := time.Now().Add(d)
e = &t
}

oldItem, _ := c.get(k)

if oldItem == nil && len(c.items) >= c.capacity {
// the new key, but the capacity is reached
return
}

c.items[k] = &Item{
Object: x,
Expiration: e,
Expand All @@ -77,7 +93,7 @@ func (c *cache) Add(k string, x interface{}, d time.Duration) error {
_, found := c.get(k)
if found {
c.Unlock()
return fmt.Errorf("Item %s already exists", k)
return ErrKeyExists
}
c.set(k, x, d)
c.Unlock()
Expand Down Expand Up @@ -109,9 +125,12 @@ func (c *cache) Get(k string) (interface{}, bool) {

func (c *cache) get(k string) (interface{}, bool) {
item, found := c.items[k]
if !found || item.Expired() {
if !found {
return nil, false
}
if item.Expired() {
return item.Object, false
}
return item.Object, true
}

Expand All @@ -125,7 +144,7 @@ func (c *cache) Increment(k string, n int64) error {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return fmt.Errorf("Item %s not found", k)
return ErrCacheMiss
}
switch v.Object.(type) {
case int:
Expand Down Expand Up @@ -172,7 +191,7 @@ func (c *cache) IncrementFloat(k string, n float64) error {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return fmt.Errorf("Item %s not found", k)
return ErrCacheMiss
}
switch v.Object.(type) {
case float32:
Expand All @@ -195,7 +214,7 @@ func (c *cache) IncrementInt(k string, n int) (int, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int)
if !ok {
Expand All @@ -216,7 +235,7 @@ func (c *cache) IncrementInt8(k string, n int8) (int8, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int8)
if !ok {
Expand All @@ -237,7 +256,7 @@ func (c *cache) IncrementInt16(k string, n int16) (int16, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int16)
if !ok {
Expand All @@ -258,7 +277,7 @@ func (c *cache) IncrementInt32(k string, n int32) (int32, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int32)
if !ok {
Expand All @@ -279,7 +298,7 @@ func (c *cache) IncrementInt64(k string, n int64) (int64, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int64)
if !ok {
Expand All @@ -300,7 +319,7 @@ func (c *cache) IncrementUint(k string, n uint) (uint, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint)
if !ok {
Expand All @@ -321,7 +340,7 @@ func (c *cache) IncrementUintptr(k string, n uintptr) (uintptr, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uintptr)
if !ok {
Expand All @@ -342,7 +361,7 @@ func (c *cache) IncrementUint8(k string, n uint8) (uint8, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint8)
if !ok {
Expand All @@ -363,7 +382,7 @@ func (c *cache) IncrementUint16(k string, n uint16) (uint16, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint16)
if !ok {
Expand All @@ -384,7 +403,7 @@ func (c *cache) IncrementUint32(k string, n uint32) (uint32, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint32)
if !ok {
Expand All @@ -405,7 +424,7 @@ func (c *cache) IncrementUint64(k string, n uint64) (uint64, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint64)
if !ok {
Expand All @@ -426,7 +445,7 @@ func (c *cache) IncrementFloat32(k string, n float32) (float32, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(float32)
if !ok {
Expand All @@ -447,7 +466,7 @@ func (c *cache) IncrementFloat64(k string, n float64) (float64, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(float64)
if !ok {
Expand All @@ -472,7 +491,7 @@ func (c *cache) Decrement(k string, n int64) error {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return fmt.Errorf("Item not found")
return ErrCacheMiss
}
switch v.Object.(type) {
case int:
Expand Down Expand Up @@ -519,7 +538,7 @@ func (c *cache) DecrementFloat(k string, n float64) error {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return fmt.Errorf("Item %s not found", k)
return ErrCacheMiss
}
switch v.Object.(type) {
case float32:
Expand All @@ -542,7 +561,7 @@ func (c *cache) DecrementInt(k string, n int) (int, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int)
if !ok {
Expand All @@ -563,7 +582,7 @@ func (c *cache) DecrementInt8(k string, n int8) (int8, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int8)
if !ok {
Expand All @@ -584,7 +603,7 @@ func (c *cache) DecrementInt16(k string, n int16) (int16, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int16)
if !ok {
Expand All @@ -605,7 +624,7 @@ func (c *cache) DecrementInt32(k string, n int32) (int32, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int32)
if !ok {
Expand All @@ -626,7 +645,7 @@ func (c *cache) DecrementInt64(k string, n int64) (int64, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(int64)
if !ok {
Expand All @@ -647,7 +666,7 @@ func (c *cache) DecrementUint(k string, n uint) (uint, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint)
if !ok {
Expand All @@ -668,7 +687,7 @@ func (c *cache) DecrementUintptr(k string, n uintptr) (uintptr, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uintptr)
if !ok {
Expand All @@ -689,7 +708,7 @@ func (c *cache) DecrementUint8(k string, n uint8) (uint8, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint8)
if !ok {
Expand All @@ -710,7 +729,7 @@ func (c *cache) DecrementUint16(k string, n uint16) (uint16, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint16)
if !ok {
Expand All @@ -731,7 +750,7 @@ func (c *cache) DecrementUint32(k string, n uint32) (uint32, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint32)
if !ok {
Expand All @@ -752,7 +771,7 @@ func (c *cache) DecrementUint64(k string, n uint64) (uint64, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(uint64)
if !ok {
Expand All @@ -773,7 +792,7 @@ func (c *cache) DecrementFloat32(k string, n float32) (float32, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(float32)
if !ok {
Expand All @@ -794,7 +813,7 @@ func (c *cache) DecrementFloat64(k string, n float64) (float64, error) {
v, found := c.items[k]
if !found || v.Expired() {
c.Unlock()
return 0, fmt.Errorf("Item %s not found", k)
return 0, ErrCacheMiss
}
rv, ok := v.Object.(float64)
if !ok {
Expand Down Expand Up @@ -964,19 +983,20 @@ func runJanitor(c *cache, ci time.Duration) {
go j.Run(c)
}

func newCache(de time.Duration, m map[string]*Item) *cache {
func newCache(de time.Duration, m map[string]*Item, capacity int) *cache {
if de == 0 {
de = -1
}
c := &cache{
defaultExpiration: de,
items: m,
capacity: capacity,
}
return c
}

func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item) *Cache {
c := newCache(de, m)
func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item, capacity int) *Cache {
c := newCache(de, m, capacity)
// This trick ensures that the janitor goroutine (which--granted it
// was enabled--is running DeleteExpired on c forever) does not keep
// the returned C object from being garbage collected. When it is
Expand All @@ -995,9 +1015,9 @@ func newCacheWithJanitor(de time.Duration, ci time.Duration, m map[string]*Item)
// the items in the cache never expire (by default), and must be deleted
// manually. If the cleanup interval is less than one, expired items are not
// deleted from the cache before calling c.DeleteExpired().
func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
func New(defaultExpiration, cleanupInterval time.Duration, capacity int) *Cache {
items := make(map[string]*Item)
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items, capacity)
}

// Return a new cache with a given default expiration duration and cleanup
Expand All @@ -1021,6 +1041,6 @@ func New(defaultExpiration, cleanupInterval time.Duration) *Cache {
// gob.Register() the individual types stored in the cache before encoding a
// map retrieved with c.Items(), and to register those same types before
// decoding a blob containing an items map.
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]*Item) *Cache {
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items)
func NewFrom(defaultExpiration, cleanupInterval time.Duration, items map[string]*Item, capacity int) *Cache {
return newCacheWithJanitor(defaultExpiration, cleanupInterval, items, capacity)
}
Loading