github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/full_self_cacher.go (about)

     1  package libkb
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"time"
     7  
     8  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
     9  	context "golang.org/x/net/context"
    10  )
    11  
    12  type FullSelfer interface {
    13  	WithSelf(ctx context.Context, f func(u *User) error) error
    14  	WithSelfForcePoll(ctx context.Context, f func(u *User) error) error
    15  	WithUser(arg LoadUserArg, f func(u *User) error) (err error)
    16  	HandleUserChanged(u keybase1.UID) error
    17  	Update(ctx context.Context, u *User) error
    18  	New() FullSelfer
    19  	OnLogin(mctx MetaContext) error
    20  }
    21  
    22  type UncachedFullSelf struct {
    23  	Contextified
    24  }
    25  
    26  var _ FullSelfer = (*UncachedFullSelf)(nil)
    27  
    28  func (n *UncachedFullSelf) WithSelf(ctx context.Context, f func(u *User) error) error {
    29  	arg := NewLoadUserArg(n.G()).WithPublicKeyOptional().WithSelf(true).WithNetContext(ctx)
    30  	return n.WithUser(arg, f)
    31  }
    32  
    33  func (n *UncachedFullSelf) WithSelfForcePoll(ctx context.Context, f func(u *User) error) error {
    34  	arg := NewLoadUserArg(n.G()).WithPublicKeyOptional().WithSelf(true).WithNetContext(ctx).WithForcePoll(true)
    35  	return n.WithUser(arg, f)
    36  }
    37  
    38  func (n *UncachedFullSelf) WithUser(arg LoadUserArg, f func(u *User) error) error {
    39  	u, err := LoadUser(arg)
    40  	if err != nil {
    41  		return err
    42  	}
    43  	return f(u)
    44  }
    45  
    46  func (n *UncachedFullSelf) HandleUserChanged(u keybase1.UID) error    { return nil }
    47  func (n *UncachedFullSelf) OnLogin(mctx MetaContext) error            { return nil }
    48  func (n *UncachedFullSelf) Update(ctx context.Context, u *User) error { return nil }
    49  
    50  func (n *UncachedFullSelf) New() FullSelfer { return NewUncachedFullSelf(n.G()) }
    51  
    52  func NewUncachedFullSelf(g *GlobalContext) *UncachedFullSelf {
    53  	return &UncachedFullSelf{NewContextified(g)}
    54  }
    55  
    56  // CachedFullSelf caches a full-on *User for the "me" or "self" user.
    57  // Because it's a full-on *User, it contains many pointers and can't
    58  // reasonably be deep-copied. So we're going to insist that access to the
    59  // cached user is protected inside a lock.
    60  type CachedFullSelf struct {
    61  	Contextified
    62  	sync.Mutex
    63  	me             *User
    64  	cachedAt       time.Time
    65  	TestDeadlocker func()
    66  }
    67  
    68  var _ FullSelfer = (*CachedFullSelf)(nil)
    69  
    70  // NewCachedFullSelf makes a new full self cacher in the given GlobalContext
    71  func NewCachedFullSelf(g *GlobalContext) *CachedFullSelf {
    72  	return &CachedFullSelf{
    73  		Contextified: NewContextified(g),
    74  	}
    75  }
    76  
    77  func (m *CachedFullSelf) New() FullSelfer { return NewCachedFullSelf(m.G()) }
    78  
    79  func (m *CachedFullSelf) isSelfLoad(arg LoadUserArg) bool {
    80  	if arg.self {
    81  		return true
    82  	}
    83  	if arg.name != "" && NewNormalizedUsername(arg.name).Eq(m.me.GetNormalizedName()) {
    84  		return true
    85  	}
    86  	if arg.uid.Exists() && arg.uid.Equal(m.me.GetUID()) {
    87  		return true
    88  	}
    89  	return false
    90  }
    91  
    92  // WithSelf loads only the self user, and maybe hits the cache.
    93  // It takes a closure, in which the user object is locked and accessible,
    94  // but we should be sure the user never escapes this closure. If the user
    95  // is fresh-loaded, then it is stored in memory.
    96  func (m *CachedFullSelf) WithSelf(ctx context.Context, f func(u *User) error) error {
    97  	arg := NewLoadUserArg(m.G()).WithPublicKeyOptional().WithSelf(true).WithNetContext(ctx)
    98  	return m.WithUser(arg, f)
    99  }
   100  
   101  // WithSelfForcePoll is like WithSelf but forces a poll. I.e., it will always go to the server for
   102  // a merkle check, regardless of when the existing self was cached.
   103  func (m *CachedFullSelf) WithSelfForcePoll(ctx context.Context, f func(u *User) error) error {
   104  	arg := NewLoadUserArg(m.G()).WithPublicKeyOptional().WithSelf(true).WithNetContext(ctx).WithForcePoll(true)
   105  	return m.WithUser(arg, f)
   106  }
   107  
   108  func (m *CachedFullSelf) maybeClearCache(ctx context.Context, arg *LoadUserArg) {
   109  	var err error
   110  	m.G().Log.CDebugf(ctx, "CachedFullSelf#maybeClearCache(%+v)", arg)
   111  
   112  	now := m.G().Clock().Now()
   113  	diff := now.Sub(m.cachedAt)
   114  
   115  	if diff < CachedUserTimeout && !arg.forcePoll {
   116  		m.G().Log.CDebugf(ctx, "| was fresh, last loaded %s ago", diff)
   117  		return
   118  	}
   119  
   120  	var sigHints *SigHints
   121  	var leaf *MerkleUserLeaf
   122  
   123  	sigHints, leaf, err = lookupSigHintsAndMerkleLeaf(NewMetaContext(ctx, m.G()), arg.uid, true, MerkleOpts{})
   124  	if err != nil {
   125  		m.me = nil
   126  		m.G().Log.CDebugf(ctx, "| CachedFullSelf error querying merkle tree, will nil-out cache: %s", err)
   127  		return
   128  	}
   129  
   130  	arg.sigHints = sigHints
   131  	arg.merkleLeaf = leaf
   132  
   133  	var idVersion int64
   134  
   135  	if idVersion, err = m.me.GetIDVersion(); err != nil {
   136  		m.me = nil
   137  		m.G().Log.CDebugf(ctx, "| CachedFullSelf: error get id version, will nil-out cache: %s", err)
   138  		return
   139  	}
   140  
   141  	if leaf.public != nil && leaf.public.Seqno == m.me.GetSigChainLastKnownSeqno() && leaf.idVersion == idVersion {
   142  		m.G().Log.CDebugf(ctx, "| CachedFullSelf still fresh at seqno=%d, idVersion=%d", leaf.public.Seqno, leaf.idVersion)
   143  		return
   144  	}
   145  
   146  	m.G().Log.CDebugf(ctx, "| CachedFullSelf was out of date")
   147  	m.me = nil
   148  }
   149  
   150  // WithUser loads any old user. If it happens to be the self user, then it behaves
   151  // as in WithSelf. Otherwise, it will just load the user, and throw it out when done.
   152  // WithUser supports other so that code doesn't need to change if we're doing the
   153  // operation for the user or someone else.
   154  func (m *CachedFullSelf) WithUser(arg LoadUserArg, f func(u *User) error) (err error) {
   155  	ctx := arg.GetNetContext()
   156  	ctx = WithLogTag(ctx, "SELF")
   157  	arg = arg.WithNetContext(ctx)
   158  
   159  	m.G().Log.CDebugf(ctx, "+ CachedFullSelf#WithUser(%+v)", arg)
   160  	m.Lock()
   161  
   162  	defer func() {
   163  		m.G().Log.CDebugf(ctx, "- CachedFullSelf#WithUser")
   164  		m.Unlock()
   165  	}()
   166  
   167  	var u *User
   168  
   169  	if m.me != nil && m.isSelfLoad(arg) {
   170  		// This UID might be nil. Or it could be wrong, so just overwrite it with the current
   171  		// self that we have loaded into the full self cacher.
   172  		arg.uid = m.me.GetUID()
   173  		m.maybeClearCache(ctx, &arg)
   174  	}
   175  
   176  	if m.me == nil || !m.isSelfLoad(arg) {
   177  
   178  		if m.TestDeadlocker != nil {
   179  			m.TestDeadlocker()
   180  		}
   181  
   182  		u, err = LoadUser(arg)
   183  
   184  		if err != nil {
   185  			return err
   186  		}
   187  		// WARNING! You can't call m.G().GetMyUID() if this function is called from
   188  		// within the Account/LoginState inner loop. Because m.G().GetMyUID() calls
   189  		// back into Account, it will deadlock.
   190  		if arg.self || u.GetUID().Equal(m.G().GetMyUID()) {
   191  			m.G().Log.CDebugf(ctx, "| CachedFullSelf#WithUser: cache populate")
   192  			m.cacheMe(u)
   193  			if ldr := m.G().GetUPAKLoader(); ldr != nil {
   194  				if err := ldr.PutUserToCache(ctx, u); err != nil {
   195  					m.G().Log.CDebugf(ctx, "| CachedFullSelf#WithUser: continuing past error putting user to cache: %s", err)
   196  				}
   197  			}
   198  		} else {
   199  			m.G().Log.CDebugf(ctx, "| CachedFullSelf#WithUser: other user")
   200  		}
   201  	} else {
   202  		m.G().Log.CDebugf(ctx, "| CachedFullSelf#WithUser: cache hit")
   203  		u = m.me
   204  	}
   205  	return f(u)
   206  }
   207  
   208  func (m *CachedFullSelf) cacheMe(u *User) {
   209  	m.me = u
   210  	m.cachedAt = m.G().Clock().Now()
   211  }
   212  
   213  // Update updates the CachedFullSelf with a User loaded from someplace else -- let's
   214  // say the UPAK loader. We throw away objects for other users or that aren't newer than
   215  // the one we have.
   216  //
   217  // CALLER BEWARE! You must only provide this function with a user you know to be "self".
   218  // This function will not do any checking along those lines (see comment below).
   219  func (m *CachedFullSelf) Update(ctx context.Context, u *User) (err error) {
   220  
   221  	// NOTE(max) 20171101: We used to do this:
   222  	//
   223  	//   if !u.GetUID().Equal(m.G().GetMyUID()) {
   224  	//   	return
   225  	//   }
   226  	//
   227  	// BUT IT IS DEADLY! The problem is that m.G().GetMyUID() calls into LoginState, but often
   228  	// we're being called from a LoginState context, so we get a circular locking situation.
   229  	// So the onus is on the caller to check that we're actually loading self.
   230  
   231  	defer m.G().CTrace(ctx, fmt.Sprintf("CachedFullSelf#Update(%s)", u.GetUID()), &err)()
   232  	m.Lock()
   233  	defer m.Unlock()
   234  
   235  	if m.me == nil {
   236  		m.G().Log.CDebugf(ctx, "Updating user, since our copy was null")
   237  		m.cacheMe(u)
   238  	} else {
   239  		var newer bool
   240  		newer, err = u.IsNewerThan(m.me)
   241  		if err != nil {
   242  			return err
   243  		}
   244  		if newer {
   245  			m.G().Log.CDebugf(ctx, "Updating user, since we got a newer copy")
   246  			m.cacheMe(u)
   247  		} else {
   248  			m.G().Log.CDebugf(ctx, "CachedFullSelf#Update called with older user")
   249  		}
   250  	}
   251  	return nil
   252  }
   253  
   254  // HandleUserChanged clears the cached self user if it's the UID of the self user.
   255  func (m *CachedFullSelf) HandleUserChanged(u keybase1.UID) error {
   256  	m.Lock()
   257  	defer m.Unlock()
   258  	if m.me != nil && m.me.GetUID().Equal(u) {
   259  		m.G().Log.Debug("| CachedFullSelf#HandleUserChanged: Invalidating me for UID=%s", u)
   260  		m.me = nil
   261  	} else {
   262  		m.G().Log.Debug("| CachedFullSelf#HandleUserChanged: Ignoring cache bust for UID=%s", u)
   263  	}
   264  	return nil
   265  }
   266  
   267  // OnLogin clears the cached self user if it differs from what's already cached.
   268  func (m *CachedFullSelf) OnLogin(mctx MetaContext) error {
   269  	m.Lock()
   270  	defer m.Unlock()
   271  	if m.me != nil && !m.me.GetUID().Equal(m.G().GetMyUID()) {
   272  		m.me = nil
   273  	}
   274  	return nil
   275  }
   276  
   277  func LoadSelfForTeamSignatures(ctx context.Context, g *GlobalContext) (ret UserForSignatures, err error) {
   278  	err = g.GetFullSelfer().WithSelf(ctx, func(u *User) (err error) {
   279  		if u == nil {
   280  			return LoginRequiredError{"no self in FullSelfCacher"}
   281  		}
   282  		ret, err = u.ToUserForSignatures()
   283  		return err
   284  	})
   285  	return ret, err
   286  }