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 }