gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/workerregistrycache.go (about) 1 package renter 2 3 import ( 4 "sync" 5 6 "gitlab.com/NebulousLabs/fastrand" 7 "go.sia.tech/siad/modules" 8 "go.sia.tech/siad/types" 9 ) 10 11 type ( 12 // registryRevisionCache is a helper type to cache information about registry values 13 // in memory. It decides randomly which entries to evict to make it more 14 // unpredictable for the host. 15 registryRevisionCache struct { 16 entryMap map[modules.RegistryEntryID]*cachedEntry 17 entryList []*cachedEntry 18 maxEntries uint64 19 staticHPK types.SiaPublicKey 20 mu sync.Mutex 21 } 22 23 // cachedEntry describes a single cached entry. To make sure we can cache as 24 // many entries as possible, this only contains the necessary information. 25 cachedEntry struct { 26 key modules.RegistryEntryID 27 rv modules.RegistryValue 28 } 29 ) 30 31 // cachedEntryEstimatedSize is the estimated size of a cachedEntry in memory. 32 // hash + revision + overhead of 2 pointers 33 const cachedEntryEstimatedSize = 32 + 8 + 16 34 35 // newRegistryCache creates a new registry cache. 36 func newRegistryCache(size uint64, hpk types.SiaPublicKey) *registryRevisionCache { 37 return ®istryRevisionCache{ 38 entryMap: make(map[modules.RegistryEntryID]*cachedEntry), 39 entryList: nil, 40 maxEntries: size / cachedEntryEstimatedSize, 41 staticHPK: hpk, 42 } 43 } 44 45 // Delete deletes an entry from the cache without replacing it. 46 func (rc *registryRevisionCache) Delete(sid modules.RegistryEntryID) { 47 rc.mu.Lock() 48 defer rc.mu.Unlock() 49 50 entry, exists := rc.entryMap[sid] 51 if !exists { 52 return 53 } 54 delete(rc.entryMap, sid) 55 for idx := range rc.entryList { 56 if rc.entryList[idx] != entry { 57 continue 58 } 59 rc.entryList[idx] = rc.entryList[len(rc.entryList)-1] 60 rc.entryList = rc.entryList[:len(rc.entryList)-1] 61 break 62 } 63 } 64 65 // Get fetches an entry from the cache. 66 func (rc *registryRevisionCache) Get(sid modules.RegistryEntryID) (modules.RegistryValue, bool) { 67 rc.mu.Lock() 68 defer rc.mu.Unlock() 69 70 cachedEntry, exists := rc.entryMap[sid] 71 if !exists { 72 return modules.RegistryValue{}, false 73 } 74 return cachedEntry.rv, true 75 } 76 77 // Set sets an entry in the registry. When 'force' is false, settings a lower 78 // revision number will be a no-op. 79 func (rc *registryRevisionCache) Set(sid modules.RegistryEntryID, rv modules.SignedRegistryValue, force bool) { 80 rc.mu.Lock() 81 defer rc.mu.Unlock() 82 83 // Check if entry already exists. 84 ce, exists := rc.entryMap[sid] 85 86 // Check if the entry is preferable over the known one. 87 var update bool 88 if exists { 89 update, _ = ce.rv.ShouldUpdateWith(&rv.RegistryValue, rc.staticHPK) 90 } 91 92 // If it does, update the revision. 93 if exists && (update || force) { 94 ce.rv = rv.RegistryValue 95 return 96 } else if exists { 97 return 98 } 99 100 // If it doesn't, create a new one. 101 ce = &cachedEntry{ 102 key: sid, 103 rv: rv.RegistryValue, 104 } 105 rc.entryMap[sid] = ce 106 rc.entryList = append(rc.entryList, ce) 107 108 // Make sure we stay within maxEntries. 109 for uint64(len(rc.entryList)) > rc.maxEntries { 110 // Figure out which entry to delete. 111 idx := fastrand.Intn(len(rc.entryList)) 112 toDelete := rc.entryList[idx] 113 114 // Delete it from the map. 115 delete(rc.entryMap, toDelete.key) 116 117 // Delete it from the list. 118 rc.entryList[idx] = rc.entryList[len(rc.entryList)-1] 119 rc.entryList = rc.entryList[:len(rc.entryList)-1] 120 } 121 }