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  }