github.com/Axway/agent-sdk@v1.1.101/pkg/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"sync"
    12  	"time"
    13  
    14  	util "github.com/Axway/agent-sdk/pkg/util"
    15  )
    16  
    17  var globalCache Cache
    18  
    19  // Cache - Interface for managing the proxy cache
    20  type Cache interface {
    21  	Get(key string) (interface{}, error)
    22  	GetItem(key string) (*Item, error)
    23  	GetBySecondaryKey(secondaryKey string) (interface{}, error)
    24  	GetItemBySecondaryKey(secondaryKey string) (*Item, error)
    25  	GetForeignKeys() []string
    26  	GetItemsByForeignKey(foreignKey string) ([]*Item, error)
    27  	GetKeys() []string
    28  	HasItemChanged(key string, data interface{}) (bool, error)
    29  	HasItemBySecondaryKeyChanged(secondaryKey string, data interface{}) (bool, error)
    30  	Set(key string, data interface{}) error
    31  	SetWithSecondaryKey(key string, secondaryKey string, data interface{}) error
    32  	SetWithForeignKey(key string, foreignKey string, data interface{}) error
    33  	SetSecondaryKey(key string, secondaryKey string) error
    34  	SetForeignKey(key string, foreignKey string) error
    35  	Delete(key string) error
    36  	DeleteBySecondaryKey(secondaryKey string) error
    37  	DeleteSecondaryKey(secondaryKey string) error
    38  	DeleteForeignKey(foreignKey string) error
    39  	DeleteItemsByForeignKey(foreignKey string) error
    40  	Flush()
    41  	Save(path string) error
    42  	Load(path string) error
    43  }
    44  
    45  // GetItem interface for getting a single item from a cache.
    46  type GetItem interface {
    47  	Get(key string) (interface{}, error)
    48  }
    49  
    50  type action int
    51  
    52  const (
    53  	getAction action = iota
    54  	setAction
    55  	deleteAction
    56  	findAction
    57  	hasChangedAction
    58  	setSecKeyAction
    59  	setForeignKeyAction
    60  	deleteSecKeyAction
    61  	deleteForeignKeyAction
    62  	flushAction
    63  	loadAction
    64  	getKeysAction
    65  	getForeignKeysAction
    66  	getItemsByForeignKeyAction
    67  	marshalAction
    68  )
    69  
    70  type cacheAction struct {
    71  	action action
    72  	key    string
    73  	secKey string
    74  	forKey string
    75  	data   interface{}
    76  	path   string
    77  }
    78  
    79  type cacheReply struct {
    80  	item    *Item
    81  	key     string
    82  	err     error
    83  	changed bool
    84  	keys    []string
    85  	items   []*Item
    86  	data    []byte
    87  }
    88  
    89  // itemCache
    90  type itemCache struct {
    91  	Items         map[string]*Item  `json:"cache"`
    92  	SecKeys       map[string]string `json:"secondaryKeys"`
    93  	startedMutex  *sync.Mutex
    94  	saveMutex     *sync.Mutex
    95  	started       bool
    96  	actionChannel chan cacheAction
    97  	replyChannel  chan cacheReply
    98  }
    99  
   100  func (c *itemCache) MarshalJSON() ([]byte, error) {
   101  	marshalReply := c.runAction(cacheAction{
   102  		action: marshalAction,
   103  	})
   104  	if marshalReply.err != nil {
   105  		return nil, marshalReply.err
   106  	}
   107  
   108  	return marshalReply.data, nil
   109  }
   110  
   111  func init() {
   112  	// Creates the global cache on first import of cache module
   113  	SetCache(New())
   114  }
   115  
   116  // SetCache - sets the global cache
   117  func SetCache(c Cache) {
   118  	if c != nil {
   119  		globalCache = c
   120  	}
   121  }
   122  
   123  // GetCache - get the global cache object
   124  func GetCache() Cache {
   125  	return globalCache
   126  }
   127  
   128  // New - create a new cache object
   129  func New() Cache {
   130  	newCache := &itemCache{
   131  		Items:         make(map[string]*Item),
   132  		SecKeys:       make(map[string]string),
   133  		startedMutex:  &sync.Mutex{},
   134  		saveMutex:     &sync.Mutex{},
   135  		actionChannel: make(chan cacheAction),
   136  		replyChannel:  make(chan cacheReply),
   137  	}
   138  	go newCache.handleAction()
   139  	return newCache
   140  }
   141  
   142  // Load - create a new cache object and load saved data
   143  func Load(path string) Cache {
   144  	newCache := &itemCache{
   145  		Items:         make(map[string]*Item),
   146  		SecKeys:       make(map[string]string),
   147  		startedMutex:  &sync.Mutex{},
   148  		saveMutex:     &sync.Mutex{},
   149  		actionChannel: make(chan cacheAction),
   150  		replyChannel:  make(chan cacheReply),
   151  	}
   152  	go newCache.handleAction()
   153  	newCache.Load(path)
   154  	return newCache
   155  }
   156  
   157  // LoadFromBuffer - create a new cache object and loads the data from buffer
   158  func LoadFromBuffer(buffer []byte) Cache {
   159  	newCache := &itemCache{
   160  		Items:        make(map[string]*Item),
   161  		SecKeys:      make(map[string]string),
   162  		startedMutex: &sync.Mutex{},
   163  		saveMutex:    &sync.Mutex{},
   164  	}
   165  	json.Unmarshal(buffer, &newCache)
   166  
   167  	newCache.actionChannel = make(chan cacheAction)
   168  	newCache.replyChannel = make(chan cacheReply)
   169  	go newCache.handleAction()
   170  	return newCache
   171  }
   172  
   173  func (c *itemCache) isStarted() bool {
   174  	c.startedMutex.Lock()
   175  	defer c.startedMutex.Unlock()
   176  	return c.started
   177  }
   178  
   179  func (c *itemCache) updateIsStarted(val bool) {
   180  	c.startedMutex.Lock()
   181  	defer c.startedMutex.Unlock()
   182  	c.started = val
   183  }
   184  
   185  // handleAction - handles all calls to the cache to prevent locking issues
   186  func (c *itemCache) handleAction() {
   187  	// make sure only one handleAction loop is running
   188  	if c.isStarted() {
   189  		return
   190  	}
   191  	c.updateIsStarted(true)
   192  	defer c.updateIsStarted(false)
   193  
   194  	actionMap := map[action]func(cacheAction) cacheReply{
   195  		getAction:                  c.get,
   196  		getKeysAction:              c.getKeys,
   197  		getForeignKeysAction:       c.getForeignKeys,
   198  		setAction:                  c.set,
   199  		deleteAction:               c.delete,
   200  		findAction:                 c.findPrimaryKey,
   201  		hasChangedAction:           c.hasItemChanged,
   202  		setSecKeyAction:            c.setSecondaryKey,
   203  		setForeignKeyAction:        c.setForeignKey,
   204  		getItemsByForeignKeyAction: c.getItemsByForeignKeys,
   205  		deleteSecKeyAction:         c.deleteSecondaryKey,
   206  		deleteForeignKeyAction:     c.deleteForeignKey,
   207  		flushAction:                c.flush,
   208  		marshalAction:              c.marshal,
   209  		loadAction:                 c.load,
   210  	}
   211  
   212  	for {
   213  		thisAction := <-c.actionChannel
   214  		reply := actionMap[thisAction.action](thisAction)
   215  		c.replyChannel <- reply
   216  	}
   217  }
   218  
   219  func (c *itemCache) marshal(thisAction cacheAction) (thisReply cacheReply) {
   220  	thisReply = cacheReply{
   221  		err: nil,
   222  	}
   223  
   224  	itemBytes, err := json.Marshal(c.Items)
   225  	if err != nil {
   226  		thisReply.err = err
   227  		return
   228  	}
   229  
   230  	secKeysBytes, err := json.Marshal(c.SecKeys)
   231  	if err != nil {
   232  		thisReply.err = err
   233  		return
   234  	}
   235  
   236  	type alias struct {
   237  		Items   json.RawMessage `json:"cache"`
   238  		SecKeys json.RawMessage `json:"secondaryKeys"`
   239  	}
   240  
   241  	a := &alias{
   242  		Items:   json.RawMessage(itemBytes),
   243  		SecKeys: json.RawMessage(secKeysBytes),
   244  	}
   245  
   246  	cachedBytes, err := json.Marshal(a)
   247  	if err != nil {
   248  		thisReply.err = err
   249  		return
   250  	}
   251  	thisReply.data = cachedBytes
   252  	return
   253  }
   254  
   255  // check the current hash vs the newHash, return true if it has changed
   256  func (c *itemCache) hasItemChanged(thisAction cacheAction) (thisReply cacheReply) {
   257  	key := thisAction.key
   258  	data := thisAction.data
   259  
   260  	thisReply = cacheReply{
   261  		changed: true,
   262  		err:     nil,
   263  	}
   264  
   265  	// Get the current item by key=
   266  	item, ok := c.Items[key]
   267  	if !ok {
   268  		thisReply.err = fmt.Errorf("could not find item with key: %s", key)
   269  		return
   270  	}
   271  
   272  	// Get the hash of the new data
   273  	newHash, err := util.ComputeHash(data)
   274  	if err != nil {
   275  		thisReply.changed = false
   276  		thisReply.err = err
   277  		return
   278  	}
   279  
   280  	// Check the hash
   281  	if item.Hash == newHash {
   282  		thisReply.changed = false
   283  		thisReply.err = nil
   284  	}
   285  	return
   286  }
   287  
   288  // returns the entire item, if found
   289  func (c *itemCache) get(thisAction cacheAction) (thisReply cacheReply) {
   290  	key := thisAction.key
   291  
   292  	thisReply = cacheReply{
   293  		item: nil,
   294  		err:  fmt.Errorf("could not find item with key: %s", key),
   295  	}
   296  	if item, ok := c.Items[key]; ok {
   297  		replyItem := &Item{
   298  			UpdateTime:    item.UpdateTime,
   299  			Hash:          item.Hash,
   300  			SecondaryKeys: item.SecondaryKeys,
   301  			ForeignKey:    item.ForeignKey,
   302  			Object:        item.Object,
   303  		}
   304  		if item.Object != nil && item.ContainsPointer && reflect.ValueOf(item.Object).Type().Kind() == reflect.Ptr {
   305  			pOriginal := reflect.ValueOf(item.Object).Elem().Interface()
   306  			rf := reflect.ValueOf(pOriginal)
   307  			p := reflect.New(rf.Type())
   308  			p.Elem().Set(rf)
   309  			replyItem.Object = p.Interface()
   310  		}
   311  
   312  		thisReply = cacheReply{
   313  			item: replyItem,
   314  			err:  nil,
   315  		}
   316  	}
   317  	return
   318  }
   319  
   320  // getKeys - Returns the keys in cache
   321  func (c *itemCache) getKeys(thisAction cacheAction) (thisReply cacheReply) {
   322  	keys := []string{}
   323  	for key := range c.Items {
   324  		keys = append(keys, key)
   325  	}
   326  	thisReply = cacheReply{
   327  		keys: keys,
   328  		err:  nil,
   329  	}
   330  	return
   331  }
   332  
   333  // getForeignKeys - Returns the Foreign keys in cache
   334  func (c *itemCache) getForeignKeys(thisAction cacheAction) (thisReply cacheReply) {
   335  	keys := []string{}
   336  	for key := range c.Items {
   337  		if c.Items[key].ForeignKey != "" {
   338  			keys = append(keys, c.Items[key].ForeignKey)
   339  		}
   340  	}
   341  
   342  	thisReply = cacheReply{
   343  		keys: keys,
   344  		err:  nil,
   345  	}
   346  	return
   347  }
   348  
   349  // getItemsByForeignKeys - Returns the Items with a particular Foreign key in cache
   350  func (c *itemCache) getItemsByForeignKeys(thisAction cacheAction) (thisReply cacheReply) {
   351  	var keys []string
   352  	var items []*Item
   353  
   354  	for key, item := range c.Items {
   355  		if item.ForeignKey == thisAction.forKey {
   356  			keys = append(keys, key)
   357  			items = append(items, item)
   358  		}
   359  	}
   360  
   361  	thisReply = cacheReply{
   362  		items: items,
   363  		keys:  keys,
   364  		err:   nil,
   365  	}
   366  	return
   367  }
   368  
   369  // returns the primary key based on the secondary key
   370  func (c *itemCache) findPrimaryKey(thisAction cacheAction) (thisReply cacheReply) {
   371  	secondaryKey := thisAction.secKey
   372  
   373  	thisReply = cacheReply{
   374  		key: "",
   375  		err: fmt.Errorf("could not find secondary key: %s", secondaryKey),
   376  	}
   377  
   378  	if key, ok := c.SecKeys[secondaryKey]; ok {
   379  		thisReply = cacheReply{
   380  			key: key,
   381  			err: nil,
   382  		}
   383  	}
   384  	return
   385  }
   386  
   387  // set the Item object to the key specified, updates the hash
   388  func (c *itemCache) set(thisAction cacheAction) (thisReply cacheReply) {
   389  	key := thisAction.key
   390  	data := thisAction.data
   391  
   392  	thisReply = cacheReply{
   393  		err: nil,
   394  	}
   395  
   396  	hash, err := util.ComputeHash(data)
   397  	if err != nil {
   398  		thisReply.err = err
   399  		return
   400  	}
   401  
   402  	secKeys := make(map[string]bool)
   403  	if _, ok := c.Items[key]; ok {
   404  		secKeys = c.Items[key].SecondaryKeys
   405  	}
   406  
   407  	c.Items[key] = &Item{
   408  		Object:        data,
   409  		UpdateTime:    time.Now().Unix(),
   410  		Hash:          hash,
   411  		SecondaryKeys: secKeys,
   412  	}
   413  
   414  	if data != nil && reflect.ValueOf(data).Type().Kind() == reflect.Ptr {
   415  		c.Items[key].ContainsPointer = true
   416  	}
   417  	return
   418  }
   419  
   420  // set the secondaryKey for the key given
   421  func (c *itemCache) setSecondaryKey(thisAction cacheAction) (thisReply cacheReply) {
   422  	key := thisAction.key
   423  	secondaryKey := thisAction.secKey
   424  
   425  	thisReply = cacheReply{
   426  		err: nil,
   427  	}
   428  
   429  	// check that the secondary key given is not used as primary
   430  	if _, ok := c.Items[secondaryKey]; ok {
   431  		thisReply.err = fmt.Errorf("can't use %s as a secondary key, it is already a primary key", secondaryKey)
   432  		return
   433  	}
   434  
   435  	item, ok := c.Items[key]
   436  	// Check that the key given is in the cache
   437  	if !ok {
   438  		thisReply.err = fmt.Errorf("can't set secondary key, %s, for a key, %s, as %s is not a known key", secondaryKey, key, key)
   439  		return
   440  	}
   441  
   442  	c.SecKeys[secondaryKey] = key
   443  	item.SecondaryKeys[secondaryKey] = true
   444  	return
   445  }
   446  
   447  // set the ForeignKey for the key given
   448  func (c *itemCache) setForeignKey(thisAction cacheAction) (thisReply cacheReply) {
   449  	key := thisAction.key
   450  	foreignKey := thisAction.forKey
   451  
   452  	thisReply = cacheReply{
   453  		err: nil,
   454  	}
   455  
   456  	item, ok := c.Items[key]
   457  	// Check that the key given is in the cache
   458  	if !ok {
   459  		thisReply.err = fmt.Errorf("can't set foreign key, %s, for a key, %s, as %s is not a known key", foreignKey, key, key)
   460  		return
   461  	}
   462  
   463  	// check that the foreign key given is not already a foreign key
   464  	if foreignKey == item.ForeignKey {
   465  		thisReply.err = fmt.Errorf("can't use %s as a foreign key, it is already a foreign key for the item", foreignKey)
   466  		return
   467  	}
   468  
   469  	item.ForeignKey = foreignKey
   470  	return
   471  }
   472  
   473  // delete an item from the cache
   474  func (c *itemCache) delete(thisAction cacheAction) (thisReply cacheReply) {
   475  	key := thisAction.key
   476  
   477  	thisReply = cacheReply{
   478  		err: nil,
   479  	}
   480  
   481  	// Check that the key given is in the cache
   482  	if _, ok := c.Items[key]; !ok {
   483  		thisReply.err = fmt.Errorf("cache item with key %s does not exist", key)
   484  		return
   485  	}
   486  
   487  	// Remove all secondary keys
   488  	for secKey := range c.Items[key].SecondaryKeys {
   489  		c.removeSecondaryKey(secKey)
   490  	}
   491  
   492  	delete(c.Items, key)
   493  	return
   494  }
   495  
   496  // deleteSecondaryKey - removes a secondary key reference in the cache, but locks the items before doing so
   497  func (c *itemCache) deleteSecondaryKey(thisAction cacheAction) (thisReply cacheReply) {
   498  	secondaryKey := thisAction.secKey
   499  
   500  	thisReply = cacheReply{
   501  		err: c.removeSecondaryKey(secondaryKey),
   502  	}
   503  	return
   504  }
   505  
   506  // removeSecondaryKey - removes a secondary key reference in the cache
   507  func (c *itemCache) removeSecondaryKey(secondaryKey string) error {
   508  	// Check that the secondaryKey given is in the cache
   509  	key, ok := c.SecKeys[secondaryKey]
   510  	if !ok {
   511  		return fmt.Errorf("cache item with secondary key %s does not exist", key)
   512  	}
   513  
   514  	delete(c.Items[key].SecondaryKeys, secondaryKey)
   515  	delete(c.SecKeys, secondaryKey)
   516  	return nil
   517  }
   518  
   519  // deleteForeignKey - removes a foreign key reference in the cache, but locks the items before doing so
   520  func (c *itemCache) deleteForeignKey(thisAction cacheAction) (thisReply cacheReply) {
   521  	key := thisAction.key
   522  
   523  	item, ok := c.Items[key]
   524  	// Check that the key given is in the cache
   525  	if !ok {
   526  		thisReply.err = fmt.Errorf("cache item with key %s does not exist", key)
   527  		return
   528  	}
   529  
   530  	item.ForeignKey = ""
   531  	return
   532  }
   533  
   534  func (c *itemCache) flush(thisAction cacheAction) (thisReply cacheReply) {
   535  	thisReply = cacheReply{}
   536  
   537  	c.SecKeys = make(map[string]string)
   538  	c.Items = make(map[string]*Item)
   539  	return
   540  }
   541  
   542  func (c *itemCache) load(thisAction cacheAction) (thisReply cacheReply) {
   543  	path := thisAction.path
   544  
   545  	thisReply = cacheReply{
   546  		err: nil,
   547  	}
   548  
   549  	file, err := os.Open(filepath.Clean(path))
   550  	if err != nil {
   551  		thisReply.err = err
   552  		return
   553  	}
   554  
   555  	thisReply.err = json.NewDecoder(file).Decode(c)
   556  	file.Close()
   557  	return
   558  }
   559  
   560  func (c *itemCache) runAction(thisAction cacheAction) cacheReply {
   561  	c.actionChannel <- thisAction
   562  	thisReply := <-c.replyChannel
   563  	return thisReply
   564  }
   565  
   566  func (c *itemCache) runFindPrimaryKey(secondaryKey string) (string, error) {
   567  	findReply := c.runAction(cacheAction{
   568  		action: findAction,
   569  		secKey: secondaryKey,
   570  	})
   571  	if findReply.err != nil {
   572  		return "", findReply.err
   573  	}
   574  	return findReply.key, nil
   575  }
   576  
   577  // Get - return the object in the cache
   578  func (c *itemCache) Get(key string) (interface{}, error) {
   579  	item, err := c.GetItem(key)
   580  	if item != nil {
   581  		return item.Object, nil
   582  	}
   583  	return nil, err
   584  }
   585  
   586  // GetItem - Return a pointer to the Item structure
   587  func (c *itemCache) GetItem(key string) (*Item, error) {
   588  	getReply := c.runAction(cacheAction{
   589  		action: getAction,
   590  		key:    key,
   591  	})
   592  	if getReply.err != nil {
   593  		return nil, getReply.err
   594  	}
   595  
   596  	return getReply.item, nil
   597  }
   598  
   599  // GetBySecondaryKey - Using the secondary key return the object in the cache
   600  func (c *itemCache) GetBySecondaryKey(secondaryKey string) (interface{}, error) {
   601  	item, err := c.GetItemBySecondaryKey(secondaryKey)
   602  	if item != nil {
   603  		return item.Object, nil
   604  	}
   605  	return nil, err
   606  }
   607  
   608  // GetItemBySecondaryKey - Using the secondary key return a pointer to the Item structure
   609  func (c *itemCache) GetItemBySecondaryKey(secondaryKey string) (*Item, error) {
   610  	// Find the primary key
   611  	key, err := c.runFindPrimaryKey(secondaryKey)
   612  	if err != nil {
   613  		return nil, err
   614  	}
   615  	getReply := c.runAction(cacheAction{
   616  		action: getAction,
   617  		key:    key,
   618  	})
   619  
   620  	return getReply.item, getReply.err
   621  }
   622  
   623  // GetItemsByForeignKey - Using the foreign key return an array of pointers to the Items which have that particular foreign key
   624  func (c *itemCache) GetItemsByForeignKey(foreignKey string) ([]*Item, error) {
   625  
   626  	getItemsForeignKeyReply := c.runAction(cacheAction{
   627  		action: getItemsByForeignKeyAction,
   628  		forKey: foreignKey,
   629  	})
   630  
   631  	return getItemsForeignKeyReply.items, getItemsForeignKeyReply.err
   632  }
   633  
   634  // GetKeys - Returns the keys in cache
   635  func (c *itemCache) GetKeys() []string {
   636  	getKeysReply := c.runAction(cacheAction{
   637  		action: getKeysAction,
   638  	})
   639  	if getKeysReply.err != nil {
   640  		return []string{}
   641  	}
   642  
   643  	return getKeysReply.keys
   644  }
   645  
   646  // GetForeignKeys - Returns the Foreign keys in cache
   647  func (c *itemCache) GetForeignKeys() []string {
   648  	getKeysReply := c.runAction(cacheAction{
   649  		action: getForeignKeysAction,
   650  	})
   651  	if getKeysReply.err != nil {
   652  		return []string{}
   653  	}
   654  
   655  	return getKeysReply.keys
   656  }
   657  
   658  // HasItemChanged - Check if the item has changed
   659  func (c *itemCache) HasItemChanged(key string, data interface{}) (bool, error) {
   660  	changedReply := c.runAction(cacheAction{
   661  		action: hasChangedAction,
   662  		key:    key,
   663  		data:   data,
   664  	})
   665  	return changedReply.changed, changedReply.err
   666  }
   667  
   668  // HasItemBySecondaryKeyChanged - Using the secondary key check if the item has changed
   669  func (c *itemCache) HasItemBySecondaryKeyChanged(secondaryKey string, data interface{}) (bool, error) {
   670  	// Find the primary key
   671  	key, err := c.runFindPrimaryKey(secondaryKey)
   672  	if err != nil {
   673  		return false, err
   674  	}
   675  
   676  	return c.HasItemChanged(key, data)
   677  }
   678  
   679  // Set - Create a new item, or update an existing item, in the cache with key
   680  func (c *itemCache) Set(key string, data interface{}) error {
   681  	// Find the primary key
   682  	setReply := c.runAction(cacheAction{
   683  		action: setAction,
   684  		key:    key,
   685  		data:   data,
   686  	})
   687  	return setReply.err
   688  }
   689  
   690  // SetSecondaryKey - Create a new item in the cache with key and a secondaryKey reference
   691  func (c *itemCache) SetWithSecondaryKey(key string, secondaryKey string, data interface{}) error {
   692  	err := c.Set(key, data)
   693  	if err != nil {
   694  		return err
   695  	}
   696  
   697  	return c.SetSecondaryKey(key, secondaryKey)
   698  }
   699  
   700  // SetSecondaryKey - Add the secondaryKey as a way to reference the item with key
   701  func (c *itemCache) SetSecondaryKey(key string, secondaryKey string) error {
   702  	setSecKeyReply := c.runAction(cacheAction{
   703  		action: setSecKeyAction,
   704  		key:    key,
   705  		secKey: secondaryKey,
   706  	})
   707  	return setSecKeyReply.err
   708  }
   709  
   710  // SetWithForeignKey - Create a new item in the cache with key and a ForeignKey reference
   711  func (c *itemCache) SetWithForeignKey(key string, foreignKey string, data interface{}) error {
   712  	err := c.Set(key, data)
   713  	if err != nil {
   714  		return err
   715  	}
   716  
   717  	return c.SetForeignKey(key, foreignKey)
   718  }
   719  
   720  // SetForeignKey - Add the ForeignKey as a way to reference the item with key
   721  func (c *itemCache) SetForeignKey(key string, foreignKey string) error {
   722  	setForeignKeyReply := c.runAction(cacheAction{
   723  		action: setForeignKeyAction,
   724  		key:    key,
   725  		forKey: foreignKey,
   726  	})
   727  	return setForeignKeyReply.err
   728  }
   729  
   730  // Delete - Remove the item which is found with this key
   731  func (c *itemCache) Delete(key string) error {
   732  	deleteReply := c.runAction(cacheAction{
   733  		action: deleteAction,
   734  		key:    key,
   735  	})
   736  	return deleteReply.err
   737  }
   738  
   739  // DeleteBySecondaryKey - Remove the item which is found with this secondary key
   740  func (c *itemCache) DeleteBySecondaryKey(secondaryKey string) error {
   741  	// Find the primary key
   742  	key, err := c.runFindPrimaryKey(secondaryKey)
   743  	if err != nil {
   744  		return err
   745  	}
   746  
   747  	return c.Delete(key)
   748  }
   749  
   750  // DeleteItemsByForeignKey - Remove all the items which is found with this foreign key
   751  func (c *itemCache) DeleteItemsByForeignKey(foreignKey string) error {
   752  	getItemsForeignKeyReply := c.runAction(cacheAction{
   753  		action: getItemsByForeignKeyAction,
   754  		forKey: foreignKey,
   755  	})
   756  	if len(getItemsForeignKeyReply.keys) == 0 {
   757  		return fmt.Errorf("no items found with foreign key: %s", foreignKey)
   758  	}
   759  
   760  	var lastErr error
   761  	for _, key := range getItemsForeignKeyReply.keys {
   762  		deleteReply := c.runAction(cacheAction{
   763  			action: deleteAction,
   764  			key:    key,
   765  		})
   766  
   767  		if deleteReply.err != nil {
   768  			lastErr = deleteReply.err
   769  		}
   770  	}
   771  
   772  	return lastErr
   773  
   774  }
   775  
   776  // DeleteSecondaryKey - Remove the secondary key, preserve the item
   777  func (c *itemCache) DeleteSecondaryKey(secondaryKey string) error {
   778  	deleteSecKeyReply := c.runAction(cacheAction{
   779  		action: deleteSecKeyAction,
   780  		secKey: secondaryKey,
   781  	})
   782  	return deleteSecKeyReply.err
   783  }
   784  
   785  // DeleteForeignKey - Remove the foreign key, preserve the item
   786  func (c *itemCache) DeleteForeignKey(key string) error {
   787  	deleteForeignKeyReply := c.runAction(cacheAction{
   788  		action: deleteForeignKeyAction,
   789  		key:    key,
   790  	})
   791  	return deleteForeignKeyReply.err
   792  }
   793  
   794  // Flush - Clears the entire cache
   795  func (c *itemCache) Flush() {
   796  	c.runAction(cacheAction{
   797  		action: flushAction,
   798  	})
   799  }
   800  
   801  // Save - Save the data in this cache to file described by path
   802  func (c *itemCache) Save(path string) error {
   803  	c.saveMutex.Lock()
   804  	defer c.saveMutex.Unlock()
   805  
   806  	file, err := os.Create(filepath.Clean(path))
   807  	defer func() {
   808  		file.Close()
   809  	}()
   810  
   811  	if err != nil {
   812  		return err
   813  	}
   814  
   815  	cacheBytes, err := json.Marshal(c)
   816  	if err != nil {
   817  		return err
   818  	}
   819  
   820  	_, err = io.Copy(file, bytes.NewReader(cacheBytes))
   821  	return err
   822  }
   823  
   824  // Load - Load the data from the file described by path to this cache
   825  func (c *itemCache) Load(path string) error {
   826  	loadReply := c.runAction(cacheAction{
   827  		action: loadAction,
   828  		path:   path,
   829  	})
   830  	return loadReply.err
   831  }