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 }