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 }