github.com/Tri-stone/burrow@v0.25.0/execution/names/cache.go (about) 1 // Copyright 2017 Monax Industries Limited 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package names 16 17 import ( 18 "fmt" 19 "sort" 20 "sync" 21 ) 22 23 // The Cache helps prevent unnecessary IAVLTree updates and garbage generation. 24 type Cache struct { 25 sync.RWMutex 26 backend Reader 27 names map[string]*nameInfo 28 } 29 30 type nameInfo struct { 31 sync.RWMutex 32 entry *Entry 33 removed bool 34 updated bool 35 } 36 37 var _ Writer = &Cache{} 38 39 // Returns a Cache that wraps an underlying NameRegCacheGetter to use on a cache miss, can write to an 40 // output Writer via Sync. 41 // Not goroutine safe, use syncStateCache if you need concurrent access 42 func NewCache(backend Reader) *Cache { 43 return &Cache{ 44 backend: backend, 45 names: make(map[string]*nameInfo), 46 } 47 } 48 49 func (cache *Cache) GetName(name string) (*Entry, error) { 50 nameInfo, err := cache.get(name) 51 if err != nil { 52 return nil, err 53 } 54 nameInfo.RLock() 55 defer nameInfo.RUnlock() 56 if nameInfo.removed { 57 return nil, nil 58 } 59 return nameInfo.entry, nil 60 } 61 62 func (cache *Cache) UpdateName(entry *Entry) error { 63 nameInfo, err := cache.get(entry.Name) 64 if err != nil { 65 return err 66 } 67 nameInfo.Lock() 68 defer nameInfo.Unlock() 69 if nameInfo.removed { 70 return fmt.Errorf("UpdateName on a removed name: %s", nameInfo.entry.Name) 71 } 72 73 nameInfo.entry = entry 74 nameInfo.updated = true 75 return nil 76 } 77 78 func (cache *Cache) RemoveName(name string) error { 79 nameInfo, err := cache.get(name) 80 if err != nil { 81 return err 82 } 83 nameInfo.Lock() 84 defer nameInfo.Unlock() 85 if nameInfo.removed { 86 return fmt.Errorf("RemoveName on removed name: %s", name) 87 } 88 nameInfo.removed = true 89 return nil 90 } 91 92 // Writes whatever is in the cache to the output Writer state. Does not flush the cache, to do that call Reset() 93 // after Sync or use Flusth if your wish to use the output state as your next backend 94 func (cache *Cache) Sync(state Writer) error { 95 cache.Lock() 96 defer cache.Unlock() 97 // Determine order for names 98 // note names may be of any length less than some limit 99 var names sort.StringSlice 100 for nameStr := range cache.names { 101 names = append(names, nameStr) 102 } 103 sort.Stable(names) 104 105 // Update or delete names. 106 for _, name := range names { 107 nameInfo := cache.names[name] 108 nameInfo.RLock() 109 if nameInfo.removed { 110 err := state.RemoveName(name) 111 if err != nil { 112 nameInfo.RUnlock() 113 return err 114 } 115 } else if nameInfo.updated { 116 err := state.UpdateName(nameInfo.entry) 117 if err != nil { 118 nameInfo.RUnlock() 119 return err 120 } 121 } 122 nameInfo.RUnlock() 123 } 124 return nil 125 } 126 127 // Resets the cache to empty initialising the backing map to the same size as the previous iteration. 128 func (cache *Cache) Reset(backend Reader) { 129 cache.Lock() 130 defer cache.Unlock() 131 cache.backend = backend 132 cache.names = make(map[string]*nameInfo) 133 } 134 135 // Syncs the Cache and Resets it to use Writer as the backend Reader 136 func (cache *Cache) Flush(output Writer, backend Reader) error { 137 err := cache.Sync(output) 138 if err != nil { 139 return err 140 } 141 cache.Reset(backend) 142 return nil 143 } 144 145 func (cache *Cache) Backend() Reader { 146 return cache.backend 147 } 148 149 // Get the cache accountInfo item creating it if necessary 150 func (cache *Cache) get(name string) (*nameInfo, error) { 151 cache.RLock() 152 nmeInfo := cache.names[name] 153 cache.RUnlock() 154 if nmeInfo == nil { 155 cache.Lock() 156 defer cache.Unlock() 157 nmeInfo = cache.names[name] 158 if nmeInfo == nil { 159 entry, err := cache.backend.GetName(name) 160 if err != nil { 161 return nil, err 162 } 163 nmeInfo = &nameInfo{ 164 entry: entry, 165 } 166 cache.names[name] = nmeInfo 167 } 168 } 169 return nmeInfo, nil 170 }