func (c *cache) Invalidate(uniqueSuffix string) {
	if !c.cache.Has(uniqueSuffix) {
		logger.Debugf("[%s:%s] Document not cached for unique suffix [%s]", c.channelID, c.Namespace, uniqueSuffix)
		return
	}
	v, err := c.cache.Get(uniqueSuffix)
	if err != nil {
		logger.Errorf("[%s:%s] Error retrieving document from cache for unique suffix [%s]: err", c.channelID, c.Namespace, uniqueSuffix, err)
		return
	}
	cv := v.(*cacheValue)
	cv.stale = true
	if c.DocumentExpiry > 0 {
		err = c.cache.SetWithExpire(uniqueSuffix, cv, c.DocumentExpiry)
	} else {
		err = c.cache.Set(uniqueSuffix, cv)
	}
	if err != nil {
		logger.Errorf("[%s:%s] Error caching document for unique suffix [%s]: %s", c.channelID, c.Namespace, uniqueSuffix, err)
		return
	}
	logger.Debugf("[%s:%s] Invalidated document for unique suffix [%s]", c.channelID, c.Namespace, uniqueSuffix)
}
Invalidate invalidates the entry for the given unique suffix. The entry is still cached but it is marked as stale so that subsequent
requests know that the cached entry is stale (based on the hash of the value) and then retrieval is performed via the DB. Once a different
entry is retrieved from the DB, the cache is updated with that entry.
package doccache
import (
	"bytes"
	"encoding/json"
	"fmt"
	"time"
	"github.com/bluele/gcache"
	"github.com/hyperledger/fabric/common/flogging"
	"github.com/trustbloc/sidetree-core-go/pkg/api/protocol"
	"github.com/trustbloc/sidetree-core-go/pkg/dochandler"
	"github.com/trustbloc/sidetree-fabric/pkg/rest/sidetreehandler"
)
var logger = flogging.MustGetLogger("sidetree_context")
const defaultCacheSize = 10000
var errNotFound = fmt.Errorf("cache not found")
type gCache interface {
	Get(key interface{}) (interface{}, error)
	Has(key interface{}) bool
	Set(key, value interface{}) error
	SetWithExpire(interface{}, interface{}, time.Duration) error
}
type Invalidator interface {
	Invalidate(uniqueSuffix string)
}
type Provider struct {
	cache gCache
}
func New() *Provider {
	return &Provider{
		cache: gcache.New(0).Build(),
	}
}
func (p *Provider) CreateCachingOperationProcessor(channelID string, cfg sidetreehandler.Config, target dochandler.OperationProcessor) dochandler.OperationProcessor {
	logger.Infof("[%s:%s] Updating document cache - DocumentExpiry: %s", channelID, cfg.Namespace, cfg.DocumentExpiry)
	cachingResolver := newCache(channelID, cfg, target)
	if err := p.cache.Set(
		cacheKey{channelID: channelID, namespace: cfg.Namespace},
		cachingResolver,
	); err != nil {
		panic(err)
	}
	return cachingResolver
}
func (p *Provider) GetDocumentInvalidator(channelID, namespace string) (Invalidator, error) {
	c, err := p.cache.Get(cacheKey{
		channelID: channelID,
		namespace: namespace,
	})
	if err != nil {
		if err == gcache.KeyNotFoundError {
			return nil, errNotFound
		}
		return nil, err
	}
	return c.(Invalidator), nil
}
type cacheKey struct {
	channelID, namespace string
}
type cacheValue struct {
	stale       bool
	resultBytes []byte
}
type cache struct {
	sidetreehandler.Config
	channelID string
	target    dochandler.OperationProcessor
	cache     gCache
	marshal   func(v interface{}) ([]byte, error)
	unmarshal func(data []byte, v interface{}) error
}
func newCache(channelID string, cfg sidetreehandler.Config, target dochandler.OperationProcessor) *cache {
	size := int(cfg.DocumentCacheSize)
	if size == 0 {
		size = defaultCacheSize
	}
	logger.Infof("[%s:%s] Creating document cache - Max Size: %d", channelID, cfg.Namespace, size)
	c := &cache{
		Config:    cfg,
		target:    target,
		channelID: channelID,
		marshal:   json.Marshal,
		unmarshal: json.Unmarshal,
	}
	c.cache = gcache.New(size).ARC().LoaderExpireFunc(c.load).Build()
	return c
}
func (c *cache) Resolve(uniqueSuffix string) (*protocol.ResolutionModel, error) {
	v, err := c.cache.Get(uniqueSuffix)
	if err != nil {
		return nil, err
	}
	cv := v.(*cacheValue)
	logger.Debugf("[%s:%s] Retrieved document from cache [%s]: Stale: %t, Value: %s", c.channelID, c.Namespace, uniqueSuffix, cv.stale, cv.resultBytes)
	var resultBytes []byte
	if cv.stale {
		resultBytes, err = c.loadAndUpdate(uniqueSuffix, cv.resultBytes)
		if err != nil {
			return nil, err
		}
	} else {
		resultBytes = cv.resultBytes
	}
	r := &protocol.ResolutionModel{}
	err = c.unmarshal(resultBytes, r)
	if err != nil {
		return nil, err
	}
	return r, nil
}