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 &registryRevisionCache{
    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  }