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

     1  // Copyright 2017 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  	"context"
     9  	"net"
    10  
    11  	lru "github.com/hashicorp/golang-lru"
    12  	"github.com/keybase/client/go/kbfs/kbfsblock"
    13  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    14  	"github.com/keybase/client/go/kbfs/kbfsmd"
    15  	"github.com/keybase/client/go/kbfs/tlf"
    16  	"github.com/keybase/client/go/libkb"
    17  	kbgitkbfs "github.com/keybase/client/go/protocol/kbgitkbfs1"
    18  	"github.com/keybase/go-framed-msgpack-rpc/rpc"
    19  )
    20  
    21  type diskBlockCacheRemoteConfig interface {
    22  	logMaker
    23  }
    24  
    25  const (
    26  	diskBlockCacheRemoteStatusCacheCapacity = 5000
    27  )
    28  
    29  // DiskBlockCacheRemote implements a client to access a remote
    30  // DiskBlockCacheService. It implements the DiskBlockCache interface.
    31  type DiskBlockCacheRemote struct {
    32  	conn   net.Conn
    33  	client kbgitkbfs.DiskBlockCacheClient
    34  	log    traceLogger
    35  
    36  	// Keep an LRU cache of the prefetch statuses for each block, so
    37  	// we can avoid making an RPC to get them unless necessary.  For
    38  	// most efficient performance, this assumes that the process using
    39  	// this remote will basically be the only one prefetching the
    40  	// blocks in the cache (as is the case most of the time with the
    41  	// git helper, for example); if not, the cache might get out of
    42  	// date, resulting in extra prefetching work to be done by this
    43  	// process.
    44  	statuses *lru.Cache
    45  }
    46  
    47  var _ DiskBlockCache = (*DiskBlockCacheRemote)(nil)
    48  
    49  // NewDiskBlockCacheRemote creates a new remote disk cache client.
    50  func NewDiskBlockCacheRemote(kbCtx Context, config diskBlockCacheRemoteConfig) (
    51  	*DiskBlockCacheRemote, error) {
    52  	conn, xp, _, err := kbCtx.GetKBFSSocket(true)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	cli := rpc.NewClient(xp, KBFSErrorUnwrapper{},
    57  		libkb.LogTagsFromContext)
    58  	client := kbgitkbfs.DiskBlockCacheClient{Cli: cli}
    59  
    60  	statuses, err := lru.New(diskBlockCacheRemoteStatusCacheCapacity)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return &DiskBlockCacheRemote{
    66  		conn:     conn,
    67  		client:   client,
    68  		log:      traceLogger{config.MakeLogger("DBR")},
    69  		statuses: statuses,
    70  	}, nil
    71  }
    72  
    73  // Get implements the DiskBlockCache interface for DiskBlockCacheRemote.
    74  func (dbcr *DiskBlockCacheRemote) Get(ctx context.Context, tlfID tlf.ID,
    75  	blockID kbfsblock.ID, _ DiskBlockCacheType) (buf []byte,
    76  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
    77  	prefetchStatus PrefetchStatus, err error) {
    78  	dbcr.log.LazyTrace(ctx, "DiskBlockCacheRemote: Get %s", blockID)
    79  	defer func() {
    80  		dbcr.log.LazyTrace(ctx, "DiskBlockCacheRemote: Get %s done (err=%+v)", blockID, err)
    81  	}()
    82  
    83  	res, err := dbcr.client.GetBlock(ctx, kbgitkbfs.GetBlockArg{
    84  		TlfID:   tlfID.Bytes(),
    85  		BlockID: blockID.Bytes(),
    86  	})
    87  	if err != nil {
    88  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, NoPrefetch, err
    89  	}
    90  
    91  	err = serverHalf.UnmarshalBinary(res.ServerHalf)
    92  	if err != nil {
    93  		return nil, kbfscrypto.BlockCryptKeyServerHalf{}, NoPrefetch, err
    94  	}
    95  	prefetchStatus = PrefetchStatusFromProtocol(res.PrefetchStatus)
    96  	dbcr.statuses.Add(blockID, prefetchStatus)
    97  	return res.Buf, serverHalf, prefetchStatus, nil
    98  }
    99  
   100  // GetPrefetchStatus implements the DiskBlockCache interface for
   101  // DiskBlockCacheRemote.
   102  func (dbcr *DiskBlockCacheRemote) GetPrefetchStatus(
   103  	ctx context.Context, tlfID tlf.ID, blockID kbfsblock.ID,
   104  	cacheType DiskBlockCacheType) (
   105  	prefetchStatus PrefetchStatus, err error) {
   106  	if tmp, ok := dbcr.statuses.Get(blockID); ok {
   107  		prefetchStatus := tmp.(PrefetchStatus)
   108  		return prefetchStatus, nil
   109  	}
   110  
   111  	dbcr.log.LazyTrace(
   112  		ctx, "DiskBlockCacheRemote: GetPrefetchStatus %s", blockID)
   113  	defer func() {
   114  		dbcr.log.LazyTrace(
   115  			ctx, "DiskBlockCacheRemote: GetPrefetchStatus %s done (err=%+v)",
   116  			blockID, err)
   117  	}()
   118  
   119  	res, err := dbcr.client.GetPrefetchStatus(
   120  		ctx, kbgitkbfs.GetPrefetchStatusArg{
   121  			TlfID:   tlfID.Bytes(),
   122  			BlockID: blockID.Bytes(),
   123  		})
   124  	if err != nil {
   125  		return NoPrefetch, err
   126  	}
   127  
   128  	return PrefetchStatusFromProtocol(res), nil
   129  }
   130  
   131  // Put implements the DiskBlockCache interface for DiskBlockCacheRemote.
   132  func (dbcr *DiskBlockCacheRemote) Put(ctx context.Context, tlfID tlf.ID,
   133  	blockID kbfsblock.ID, buf []byte,
   134  	serverHalf kbfscrypto.BlockCryptKeyServerHalf,
   135  	_ DiskBlockCacheType) (err error) {
   136  	dbcr.log.LazyTrace(ctx, "DiskBlockCacheRemote: Put %s", blockID)
   137  	defer func() {
   138  		dbcr.log.LazyTrace(ctx, "DiskBlockCacheRemote: Put %s done (err=%+v)", blockID, err)
   139  	}()
   140  
   141  	return dbcr.client.PutBlock(ctx, kbgitkbfs.PutBlockArg{
   142  		TlfID:      tlfID.Bytes(),
   143  		BlockID:    blockID.Bytes(),
   144  		Buf:        buf,
   145  		ServerHalf: serverHalf.Bytes(),
   146  	})
   147  }
   148  
   149  // Delete implements the DiskBlockCache interface for DiskBlockCacheRemote.
   150  func (dbcr *DiskBlockCacheRemote) Delete(
   151  	ctx context.Context, blockIDs []kbfsblock.ID,
   152  	cacheType DiskBlockCacheType) (
   153  	numRemoved int, sizeRemoved int64, err error) {
   154  	numBlocks := len(blockIDs)
   155  	dbcr.log.LazyTrace(ctx, "DiskBlockCacheRemote: Delete %s block(s)",
   156  		numBlocks)
   157  	defer func() {
   158  		dbcr.log.LazyTrace(ctx, "DiskBlockCacheRemote: Delete %s block(s) "+
   159  			"done (err=%+v)", numBlocks, err)
   160  	}()
   161  	blocks := make([][]byte, 0, len(blockIDs))
   162  	for _, b := range blockIDs {
   163  		blocks = append(blocks, b.Bytes())
   164  	}
   165  	res, err := dbcr.client.DeleteBlocks(ctx, blocks)
   166  	if err != nil {
   167  		return 0, 0, err
   168  	}
   169  	return res.NumRemoved, res.SizeRemoved, nil
   170  }
   171  
   172  // UpdateMetadata implements the DiskBlockCache interface for
   173  // DiskBlockCacheRemote.
   174  func (dbcr *DiskBlockCacheRemote) UpdateMetadata(ctx context.Context,
   175  	tlfID tlf.ID, blockID kbfsblock.ID, prefetchStatus PrefetchStatus,
   176  	_ DiskBlockCacheType) error {
   177  	dbcr.statuses.Add(blockID, prefetchStatus)
   178  	return dbcr.client.UpdateBlockMetadata(ctx,
   179  		kbgitkbfs.UpdateBlockMetadataArg{
   180  			TlfID:          tlfID.Bytes(),
   181  			BlockID:        blockID.Bytes(),
   182  			PrefetchStatus: prefetchStatus.ToProtocol(),
   183  		})
   184  }
   185  
   186  // ClearAllTlfBlocks implements the DiskBlockCache interface for
   187  // DiskBlockCacheRemote.
   188  func (dbcr *DiskBlockCacheRemote) ClearAllTlfBlocks(
   189  	_ context.Context, _ tlf.ID, _ DiskBlockCacheType) error {
   190  	panic("ClearAllTlfBlocks() not implemented in DiskBlockCacheRemote")
   191  }
   192  
   193  // GetLastUnrefRev implements the DiskBlockCache interface for
   194  // DiskBlockCacheRemote.
   195  func (dbcr *DiskBlockCacheRemote) GetLastUnrefRev(
   196  	_ context.Context, _ tlf.ID, _ DiskBlockCacheType) (
   197  	kbfsmd.Revision, error) {
   198  	panic("GetLastUnrefRev() not implemented in DiskBlockCacheRemote")
   199  }
   200  
   201  // PutLastUnrefRev implements the DiskBlockCache interface for
   202  // DiskBlockCacheRemote.
   203  func (dbcr *DiskBlockCacheRemote) PutLastUnrefRev(
   204  	_ context.Context, _ tlf.ID, _ kbfsmd.Revision,
   205  	_ DiskBlockCacheType) error {
   206  	panic("PutLastUnrefRev() not implemented in DiskBlockCacheRemote")
   207  }
   208  
   209  // Status implements the DiskBlockCache interface for DiskBlockCacheRemote.
   210  func (dbcr *DiskBlockCacheRemote) Status(ctx context.Context) map[string]DiskBlockCacheStatus {
   211  	// We don't return a status because it isn't needed in the contexts
   212  	// this block cache is used.
   213  	panic("Status() not implemented in DiskBlockCacheRemote")
   214  }
   215  
   216  // DoesCacheHaveSpace implements the DiskBlockCache interface for
   217  // DiskBlockCacheRemote.
   218  func (dbcr *DiskBlockCacheRemote) DoesCacheHaveSpace(
   219  	_ context.Context, _ DiskBlockCacheType) (bool, int64, error) {
   220  	// We won't be kicking off long syncing prefetching via the remote
   221  	// cache, so just pretend the cache has space.
   222  	return true, 0, nil
   223  }
   224  
   225  // Mark implements the DiskBlockCache interface for DiskBlockCacheRemote.
   226  func (dbcr *DiskBlockCacheRemote) Mark(
   227  	_ context.Context, _ kbfsblock.ID, _ string, _ DiskBlockCacheType) error {
   228  	panic("Mark() not implemented in DiskBlockCacheRemote")
   229  }
   230  
   231  // DeleteUnmarked implements the DiskBlockCache interface for
   232  // DiskBlockCacheRemote.
   233  func (dbcr *DiskBlockCacheRemote) DeleteUnmarked(
   234  	_ context.Context, _ tlf.ID, _ string, _ DiskBlockCacheType) error {
   235  	panic("DeleteUnmarked() not implemented in DiskBlockCacheRemote")
   236  }
   237  
   238  // AddHomeTLF implements the DiskBlockCache interface for DiskBlockCacheRemote.
   239  func (dbcr *DiskBlockCacheRemote) AddHomeTLF(ctx context.Context,
   240  	tlfID tlf.ID) error {
   241  	// Let the local cache care about home TLFs.
   242  	return nil
   243  }
   244  
   245  // ClearHomeTLFs implements the DiskBlockCache interface for
   246  // DiskBlockCacheRemote.
   247  func (dbcr *DiskBlockCacheRemote) ClearHomeTLFs(ctx context.Context) error {
   248  	// Let the local cache care about home TLFs.
   249  	return nil
   250  }
   251  
   252  // GetTlfSize implements the DiskBlockCache interface for
   253  // DiskBlockCacheRemote.
   254  func (dbcr *DiskBlockCacheRemote) GetTlfSize(
   255  	_ context.Context, _ tlf.ID, _ DiskBlockCacheType) (uint64, error) {
   256  	panic("GetTlfSize() not implemented in DiskBlockCacheRemote")
   257  }
   258  
   259  // GetTlfIDs implements the DiskBlockCache interface for
   260  // DiskBlockCacheRemote.
   261  func (dbcr *DiskBlockCacheRemote) GetTlfIDs(
   262  	_ context.Context, _ DiskBlockCacheType) ([]tlf.ID, error) {
   263  	panic("GetTlfIDs() not implemented in DiskBlockCacheRemote")
   264  }
   265  
   266  // WaitUntilStarted implements the DiskBlockCache interface for
   267  // DiskBlockCacheRemote.
   268  func (dbcr *DiskBlockCacheRemote) WaitUntilStarted(
   269  	_ DiskBlockCacheType) error {
   270  	panic("WaitUntilStarted() not implemented in DiskBlockCacheRemote")
   271  }
   272  
   273  // Shutdown implements the DiskBlockCache interface for DiskBlockCacheRemote.
   274  func (dbcr *DiskBlockCacheRemote) Shutdown(ctx context.Context) <-chan struct{} {
   275  	dbcr.conn.Close()
   276  	ch := make(chan struct{})
   277  	close(ch)
   278  	return ch
   279  }