github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/team_member_count_cache.go (about) 1 package libkb 2 3 import ( 4 "sync" 5 "time" 6 7 "github.com/keybase/client/go/protocol/keybase1" 8 context "golang.org/x/net/context" 9 "golang.org/x/time/rate" 10 ) 11 12 type TeamMemberCountCache struct { 13 g *GlobalContext 14 notifyCh chan struct{} 15 16 lock sync.RWMutex 17 cache map[keybase1.TeamID]int 18 } 19 20 func newTeamMemberCountCache(g *GlobalContext) *TeamMemberCountCache { 21 cache := &TeamMemberCountCache{ 22 cache: make(map[keybase1.TeamID]int), 23 g: g, 24 notifyCh: make(chan struct{}, 1), 25 } 26 cache.startNotifyLoop() 27 g.AddLogoutHook(cache, "TeamMemberCountCache") 28 return cache 29 } 30 31 func (c *TeamMemberCountCache) OnLogout(mctx MetaContext) error { 32 mctx.Debug("TeamMemberCountCache OnLogout: clearing cache") 33 c.lock.Lock() 34 defer c.lock.Unlock() 35 c.cache = make(map[keybase1.TeamID]int) 36 return nil 37 } 38 39 func (c *TeamMemberCountCache) startNotifyLoop() { 40 ctx, cancel := context.WithCancel(context.Background()) 41 c.g.PushShutdownHook(func(mctx MetaContext) error { 42 mctx.Debug("TeamMemberCountCache shutdown") 43 cancel() 44 return nil 45 }) 46 go c.notifyLoop(ctx) 47 } 48 49 func (c *TeamMemberCountCache) notifyLoop(ctx context.Context) { 50 const notifyInterval = 5 * time.Second 51 const notifyTimeout = 10 * time.Second 52 limiter := rate.NewLimiter(rate.Every(notifyInterval), 1) 53 for { 54 if err := limiter.Wait(ctx); err != nil { 55 return 56 } 57 select { 58 case <-c.notifyCh: 59 ctx, cancel := context.WithTimeout(context.Background(), notifyTimeout) 60 c.g.NotifyRouter.HandleTeamMetadataUpdate(ctx) 61 cancel() 62 case <-ctx.Done(): 63 return 64 } 65 } 66 } 67 68 func (c *TeamMemberCountCache) Set(teamID keybase1.TeamID, count int) { 69 c.lock.Lock() 70 defer c.lock.Unlock() 71 if c, ok := c.cache[teamID]; ok && c == count { 72 return 73 } 74 c.cache[teamID] = count 75 select { 76 case c.notifyCh <- struct{}{}: 77 default: 78 } 79 } 80 81 func (c *TeamMemberCountCache) Get(teamID keybase1.TeamID) (count int, ok bool) { 82 c.lock.RLock() 83 defer c.lock.RUnlock() 84 count, ok = c.cache[teamID] 85 return count, ok 86 } 87 88 func (c *TeamMemberCountCache) GetWithFallback(teamID keybase1.TeamID, fallback int) (count int) { 89 count, ok := c.Get(teamID) 90 if ok { 91 return count 92 } 93 return fallback 94 }