github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/execution/names/cache.go (about) 1 // Copyright Monax Industries Limited 2 // SPDX-License-Identifier: Apache-2.0 3 4 package names 5 6 import ( 7 "fmt" 8 "sort" 9 "sync" 10 ) 11 12 // The Cache helps prevent unnecessary IAVLTree updates and garbage generation. 13 type Cache struct { 14 sync.RWMutex 15 backend Reader 16 names map[string]*nameInfo 17 } 18 19 type nameInfo struct { 20 sync.RWMutex 21 entry *Entry 22 removed bool 23 updated bool 24 } 25 26 var _ Writer = &Cache{} 27 28 // Returns a Cache that wraps an underlying NameRegCacheGetter to use on a cache miss, can write to an 29 // output Writer via Sync. Not goroutine safe, use syncStateCache if you need concurrent access 30 func NewCache(backend Reader) *Cache { 31 return &Cache{ 32 backend: backend, 33 names: make(map[string]*nameInfo), 34 } 35 } 36 37 func (cache *Cache) GetName(name string) (*Entry, error) { 38 nameInfo, err := cache.get(name) 39 if err != nil { 40 return nil, err 41 } 42 nameInfo.RLock() 43 defer nameInfo.RUnlock() 44 if nameInfo.removed { 45 return nil, nil 46 } 47 return nameInfo.entry, nil 48 } 49 50 func (cache *Cache) UpdateName(entry *Entry) error { 51 nameInfo, err := cache.get(entry.Name) 52 if err != nil { 53 return err 54 } 55 nameInfo.Lock() 56 defer nameInfo.Unlock() 57 if nameInfo.removed { 58 return fmt.Errorf("UpdateName on a removed name: %s", nameInfo.entry.Name) 59 } 60 61 nameInfo.entry = entry 62 nameInfo.updated = true 63 return nil 64 } 65 66 func (cache *Cache) RemoveName(name string) error { 67 nameInfo, err := cache.get(name) 68 if err != nil { 69 return err 70 } 71 nameInfo.Lock() 72 defer nameInfo.Unlock() 73 if nameInfo.removed { 74 return fmt.Errorf("RemoveName on removed name: %s", name) 75 } 76 nameInfo.removed = true 77 return nil 78 } 79 80 // Writes whatever is in the cache to the output Writer state. Does not flush the cache, to do that call Reset() 81 // after Sync or use Flush if your wish to use the output state as your next backend 82 func (cache *Cache) Sync(state Writer) error { 83 cache.Lock() 84 defer cache.Unlock() 85 // Determine order for names 86 // note names may be of any length less than some limit 87 var names sort.StringSlice 88 for nameStr := range cache.names { 89 names = append(names, nameStr) 90 } 91 sort.Stable(names) 92 93 // Update or delete names 94 for _, name := range names { 95 nameInfo := cache.names[name] 96 nameInfo.RLock() 97 if nameInfo.removed { 98 err := state.RemoveName(name) 99 if err != nil { 100 nameInfo.RUnlock() 101 return err 102 } 103 } else if nameInfo.updated { 104 err := state.UpdateName(nameInfo.entry) 105 if err != nil { 106 nameInfo.RUnlock() 107 return err 108 } 109 } 110 nameInfo.RUnlock() 111 } 112 return nil 113 } 114 115 // Resets the cache to empty initialising the backing map to the same size as the previous iteration 116 func (cache *Cache) Reset(backend Reader) { 117 cache.Lock() 118 defer cache.Unlock() 119 cache.backend = backend 120 cache.names = make(map[string]*nameInfo) 121 } 122 123 func (cache *Cache) Backend() Reader { 124 return cache.backend 125 } 126 127 // Get the cache accountInfo item creating it if necessary 128 func (cache *Cache) get(name string) (*nameInfo, error) { 129 cache.RLock() 130 nmeInfo := cache.names[name] 131 cache.RUnlock() 132 if nmeInfo == nil { 133 cache.Lock() 134 defer cache.Unlock() 135 nmeInfo = cache.names[name] 136 if nmeInfo == nil { 137 entry, err := cache.backend.GetName(name) 138 if err != nil { 139 return nil, err 140 } 141 nmeInfo = &nameInfo{ 142 entry: entry, 143 } 144 cache.names[name] = nmeInfo 145 } 146 } 147 return nmeInfo, nil 148 }