github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/identify2_cache.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    11  	"stathat.com/c/ramcache"
    12  )
    13  
    14  // Identify2Cache stores User objects in memory for a fixed amount of
    15  // time.
    16  type Identify2Cache struct {
    17  	cache *ramcache.Ramcache
    18  }
    19  
    20  type Identify2Cacher interface {
    21  	Get(keybase1.UID, GetCheckTimeFunc, GetCacheDurationFunc, bool) (*keybase1.Identify2ResUPK2, error)
    22  	Insert(up *keybase1.Identify2ResUPK2) error
    23  	DidFullUserLoad(keybase1.UID)
    24  	Shutdown()
    25  	Delete(uid keybase1.UID) error
    26  	UseDiskCache() bool
    27  }
    28  
    29  type GetCheckTimeFunc func(keybase1.Identify2ResUPK2) keybase1.Time
    30  type GetCacheDurationFunc func(keybase1.Identify2ResUPK2) time.Duration
    31  
    32  // NewIdentify2Cache creates a Identify2Cache and sets the object max age to
    33  // maxAge.  Once a user is inserted, after maxAge duration passes,
    34  // the user will be removed from the cache.
    35  func NewIdentify2Cache(maxAge time.Duration) *Identify2Cache {
    36  	res := &Identify2Cache{
    37  		cache: ramcache.New(),
    38  	}
    39  	res.cache.MaxAge = maxAge
    40  	res.cache.TTL = maxAge
    41  	return res
    42  }
    43  
    44  // Get returns a user object.  If none exists for uid, it will return nil.
    45  func (c *Identify2Cache) Get(uid keybase1.UID, gctf GetCheckTimeFunc, gcdf GetCacheDurationFunc, breaksOK bool) (*keybase1.Identify2ResUPK2, error) {
    46  	v, err := c.cache.Get(string(uid))
    47  	if err != nil {
    48  		if err == ramcache.ErrNotFound {
    49  			return nil, nil
    50  		}
    51  		return nil, err
    52  	}
    53  	up, ok := v.(*keybase1.Identify2ResUPK2)
    54  	if !ok {
    55  		return nil, fmt.Errorf("invalid type in cache: %T", v)
    56  	}
    57  
    58  	if gctf != nil {
    59  		then := gctf(*up)
    60  		if then == 0 {
    61  			return nil, IdentifyTimeoutError{}
    62  		}
    63  		if up.TrackBreaks != nil && !breaksOK {
    64  			return nil, TrackBrokenError{}
    65  		}
    66  
    67  		thenTime := keybase1.FromTime(then)
    68  		timeout := gcdf(*up)
    69  		if time.Since(thenTime) > timeout {
    70  			return nil, IdentifyTimeoutError{}
    71  		}
    72  	}
    73  
    74  	return up, nil
    75  }
    76  
    77  // Insert adds a user to the cache, keyed on UID.
    78  func (c *Identify2Cache) Insert(up *keybase1.Identify2ResUPK2) error {
    79  	tmp := *up
    80  	copy := &tmp
    81  	copy.Upk.Uvv.CachedAt = keybase1.ToTime(time.Now())
    82  	return c.cache.Set(string(up.Upk.GetUID()), copy)
    83  }
    84  
    85  func (c *Identify2Cache) Delete(uid keybase1.UID) error {
    86  	return c.cache.Delete(string(uid))
    87  }
    88  
    89  // Shutdown stops any goroutines in the cache.
    90  func (c *Identify2Cache) Shutdown() {
    91  	c.cache.Shutdown()
    92  }
    93  
    94  // DidFullUserLoad is a noop unless we're testing...
    95  func (c *Identify2Cache) DidFullUserLoad(_ keybase1.UID) {}
    96  
    97  func (c *Identify2Cache) UseDiskCache() bool { return true }