func (c *LruCache) Invalidate(fn func(key string) bool) {
	for _, k := range c.backend.Keys() {  
		if key, ok := k.(string); ok && fn(key) {
			c.backend.Remove(key)
		}
	}
}
Invalidate removes keys with passed predicate fn, i.e. fn(key) should be true to get evicted
package lcw
import (
	"sync/atomic"
	"github.com/google/uuid"
	lru "github.com/hashicorp/golang-lru"
	"github.com/pkg/errors"
	"github.com/go-pkgz/lcw/eventbus"
)
type LruCache struct {
	options
	CacheStat
	backend     *lru.Cache
	currentSize int64
	id          string  
}
func NewLruCache(opts ...Option) (*LruCache, error) {
	res := LruCache{
		options: options{
			maxKeys:      1000,
			maxValueSize: 0,
			eventBus:     &eventbus.NopPubSub{},
		},
		id: uuid.New().String(),
	}
	for _, opt := range opts {
		if err := opt(&res.options); err != nil {
			return nil, errors.Wrap(err, "failed to set cache option")
		}
	}
	err := res.init()
	return &res, err
}
func (c *LruCache) init() error {
	if err := c.eventBus.Subscribe(c.onBusEvent); err != nil {
		return errors.Wrapf(err, "can't subscribe to event bus")
	}
	onEvicted := func(key interface{}, value interface{}) {
		if c.onEvicted != nil {
			c.onEvicted(key.(string), value)
		}
		if s, ok := value.(Sizer); ok {
			size := s.Size()
			atomic.AddInt64(&c.currentSize, -1*int64(size))
		}
		_ = c.eventBus.Publish(c.id, key.(string))  
	}
	var err error
	if c.backend, err = lru.NewWithEvict(c.maxKeys, onEvicted); err != nil {
		return errors.Wrap(err, "failed to make lru cache backend")
	}
	return nil
}
func (c *LruCache) Get(key string, fn func() (interface{}, error)) (data interface{}, err error) {
	if v, ok := c.backend.Get(key); ok {
		atomic.AddInt64(&c.Hits, 1)
		return v, nil
	}
	if data, err = fn(); err != nil {
		atomic.AddInt64(&c.Errors, 1)
		return data, err
	}
	atomic.AddInt64(&c.Misses, 1)
	if !c.allowed(key, data) {
		return data, nil
	}
	c.backend.Add(key, data)
	if s, ok := data.(Sizer); ok {
		atomic.AddInt64(&c.currentSize, int64(s.Size()))
		if c.maxCacheSize > 0 && atomic.LoadInt64(&c.currentSize) > c.maxCacheSize {
			for atomic.LoadInt64(&c.currentSize) > c.maxCacheSize {
				c.backend.RemoveOldest()
			}
		}
	}
	return data, nil
}
func (c *LruCache) Peek(key string) (interface{}, bool) {
	return c.backend.Peek(key)
}
func (c *LruCache) Purge() {
	c.backend.Purge()
	atomic.StoreInt64(&c.currentSize, 0)
}