github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libkbfs/mdcache.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libkbfs
     6  
     7  import (
     8  	"sync"
     9  
    10  	lru "github.com/hashicorp/golang-lru"
    11  	"github.com/keybase/client/go/kbfs/kbfsmd"
    12  	"github.com/keybase/client/go/kbfs/tlf"
    13  	"github.com/keybase/client/go/kbfs/tlfhandle"
    14  	"github.com/keybase/client/go/protocol/keybase1"
    15  	"github.com/pkg/errors"
    16  )
    17  
    18  // MDCacheStandard implements a simple LRU cache for per-folder
    19  // metadata objects.
    20  type MDCacheStandard struct {
    21  	// lock protects `lru` from atomic operations that need atomicity
    22  	// across multiple `lru` calls.
    23  	lock  sync.RWMutex
    24  	lru   *lru.Cache
    25  	idLRU *lru.Cache
    26  
    27  	nextMDLRU *lru.Cache
    28  }
    29  
    30  type mdCacheKey struct {
    31  	tlf tlf.ID
    32  	rev kbfsmd.Revision
    33  	bid kbfsmd.BranchID
    34  }
    35  
    36  const (
    37  	defaultMDCacheCapacity     = 5000
    38  	defaultNextMDCacheCapacity = 100
    39  )
    40  
    41  // NewMDCacheStandard constructs a new MDCacheStandard using the given
    42  // cache capacity.
    43  func NewMDCacheStandard(capacity int) *MDCacheStandard {
    44  	mdLRU, err := lru.New(capacity)
    45  	if err != nil {
    46  		return nil
    47  	}
    48  	idLRU, err := lru.New(capacity)
    49  	if err != nil {
    50  		return nil
    51  	}
    52  	// Hard-code the nextMD cache size to something small, since only
    53  	// one entry is used for each revoked device we need to verify.
    54  	nextMDLRU, err := lru.New(defaultNextMDCacheCapacity)
    55  	if err != nil {
    56  		return nil
    57  	}
    58  	return &MDCacheStandard{lru: mdLRU, idLRU: idLRU, nextMDLRU: nextMDLRU}
    59  }
    60  
    61  // Get implements the MDCache interface for MDCacheStandard.
    62  func (md *MDCacheStandard) Get(tlf tlf.ID, rev kbfsmd.Revision, bid kbfsmd.BranchID) (
    63  	ImmutableRootMetadata, error) {
    64  	md.lock.RLock()
    65  	defer md.lock.RUnlock()
    66  	key := mdCacheKey{tlf, rev, bid}
    67  	if tmp, ok := md.lru.Get(key); ok {
    68  		if rmd, ok := tmp.(ImmutableRootMetadata); ok {
    69  			return rmd, nil
    70  		}
    71  		return ImmutableRootMetadata{}, BadMDError{tlf}
    72  	}
    73  	return ImmutableRootMetadata{}, NoSuchMDError{tlf, rev, bid}
    74  }
    75  
    76  // Put implements the MDCache interface for MDCacheStandard.
    77  func (md *MDCacheStandard) Put(rmd ImmutableRootMetadata) error {
    78  	md.lock.Lock()
    79  	defer md.lock.Unlock()
    80  	key := mdCacheKey{rmd.TlfID(), rmd.Revision(), rmd.BID()}
    81  	if _, ok := md.lru.Get(key); ok {
    82  		// Don't overwrite the cache.  In the case that `rmd` is
    83  		// different from what's in the cache, we require that upper
    84  		// layers manage the cache explicitly by deleting or replacing
    85  		// it explicitly.
    86  		return nil
    87  	}
    88  	md.lru.Add(key, rmd)
    89  	return nil
    90  }
    91  
    92  // Delete implements the MDCache interface for MDCacheStandard.
    93  func (md *MDCacheStandard) Delete(tlf tlf.ID, rev kbfsmd.Revision,
    94  	bid kbfsmd.BranchID) {
    95  	md.lock.Lock()
    96  	defer md.lock.Unlock()
    97  	key := mdCacheKey{tlf, rev, bid}
    98  	md.lru.Remove(key)
    99  }
   100  
   101  // Replace implements the MDCache interface for MDCacheStandard.
   102  func (md *MDCacheStandard) Replace(newRmd ImmutableRootMetadata,
   103  	oldBID kbfsmd.BranchID) error {
   104  	md.lock.Lock()
   105  	defer md.lock.Unlock()
   106  	oldKey := mdCacheKey{newRmd.TlfID(), newRmd.Revision(), oldBID}
   107  	newKey := mdCacheKey{newRmd.TlfID(), newRmd.Revision(), newRmd.BID()}
   108  	// TODO: implement our own LRU where we can replace the old data
   109  	// without affecting the LRU status.
   110  	md.lru.Remove(oldKey)
   111  	md.lru.Add(newKey, newRmd)
   112  	return nil
   113  }
   114  
   115  // MarkPutToServer implements the MDCache interface for
   116  // MDCacheStandard.
   117  func (md *MDCacheStandard) MarkPutToServer(
   118  	tlf tlf.ID, rev kbfsmd.Revision, bid kbfsmd.BranchID) {
   119  	md.lock.Lock()
   120  	defer md.lock.Unlock()
   121  	key := mdCacheKey{tlf, rev, bid}
   122  	tmp, ok := md.lru.Get(key)
   123  	if !ok {
   124  		return
   125  	}
   126  	rmd, ok := tmp.(ImmutableRootMetadata)
   127  	if !ok {
   128  		return
   129  	}
   130  	rmd.putToServer = true
   131  	md.lru.Add(key, rmd)
   132  }
   133  
   134  // GetIDForHandle implements the MDCache interface for
   135  // MDCacheStandard.
   136  func (md *MDCacheStandard) GetIDForHandle(handle *tlfhandle.Handle) (tlf.ID, error) {
   137  	md.lock.RLock()
   138  	defer md.lock.RUnlock()
   139  	key := handle.GetCanonicalPath()
   140  	tmp, ok := md.idLRU.Get(key)
   141  	if !ok {
   142  		return tlf.NullID, NoSuchTlfIDError{handle}
   143  	}
   144  	id, ok := tmp.(tlf.ID)
   145  	if !ok {
   146  		return tlf.NullID, errors.Errorf("Bad ID for handle %s", key)
   147  	}
   148  	return id, nil
   149  }
   150  
   151  // PutIDForHandle implements the MDCache interface for
   152  // MDCacheStandard.
   153  func (md *MDCacheStandard) PutIDForHandle(handle *tlfhandle.Handle, id tlf.ID) error {
   154  	md.lock.RLock()
   155  	defer md.lock.RUnlock()
   156  	key := handle.GetCanonicalPath()
   157  	md.idLRU.Add(key, id)
   158  	return nil
   159  }
   160  
   161  // ChangeHandleForID implements the MDCache interface for
   162  // MDCacheStandard.
   163  func (md *MDCacheStandard) ChangeHandleForID(
   164  	oldHandle *tlfhandle.Handle, newHandle *tlfhandle.Handle) {
   165  	md.lock.RLock()
   166  	defer md.lock.RUnlock()
   167  	oldKey := oldHandle.GetCanonicalPath()
   168  	tmp, ok := md.idLRU.Get(oldKey)
   169  	if !ok {
   170  		return
   171  	}
   172  	md.idLRU.Remove(oldKey)
   173  	newKey := newHandle.GetCanonicalPath()
   174  	md.idLRU.Add(newKey, tmp)
   175  }
   176  
   177  type mdcacheNextMDKey struct {
   178  	tlfID     tlf.ID
   179  	rootSeqno keybase1.Seqno
   180  }
   181  
   182  type mdcacheNextMDVal struct {
   183  	nextKbfsRoot    *kbfsmd.MerkleRoot
   184  	nextMerkleNodes [][]byte
   185  	nextRootSeqno   keybase1.Seqno
   186  }
   187  
   188  // GetNextMD implements the MDCache interface for MDCacheStandard.
   189  func (md *MDCacheStandard) GetNextMD(tlfID tlf.ID, rootSeqno keybase1.Seqno) (
   190  	nextKbfsRoot *kbfsmd.MerkleRoot, nextMerkleNodes [][]byte,
   191  	nextRootSeqno keybase1.Seqno, err error) {
   192  	key := mdcacheNextMDKey{tlfID, rootSeqno}
   193  	tmp, ok := md.nextMDLRU.Get(key)
   194  	if !ok {
   195  		return nil, nil, 0, NextMDNotCachedError{tlfID, rootSeqno}
   196  	}
   197  	val, ok := tmp.(mdcacheNextMDVal)
   198  	if !ok {
   199  		return nil, nil, 0, errors.Errorf(
   200  			"Bad next MD for %s, seqno=%d", tlfID, rootSeqno)
   201  	}
   202  	return val.nextKbfsRoot, val.nextMerkleNodes, val.nextRootSeqno, nil
   203  }
   204  
   205  // PutNextMD implements the MDCache interface for MDCacheStandard.
   206  func (md *MDCacheStandard) PutNextMD(
   207  	tlfID tlf.ID, rootSeqno keybase1.Seqno, nextKbfsRoot *kbfsmd.MerkleRoot,
   208  	nextMerkleNodes [][]byte, nextRootSeqno keybase1.Seqno) error {
   209  	key := mdcacheNextMDKey{tlfID, rootSeqno}
   210  	val := mdcacheNextMDVal{nextKbfsRoot, nextMerkleNodes, nextRootSeqno}
   211  	md.nextMDLRU.Add(key, val)
   212  	return nil
   213  }